diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 0f744e3708..a163f506d3 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1149,16 +1149,16 @@ def open_local_file(path): _ea_lock = Lock() -def ensure_app(): +def ensure_app(headless=True): global _store_app with _ea_lock: if _store_app is None and QApplication.instance() is None: args = sys.argv[:1] - headless = islinux or isbsd - if headless: + if headless and (islinux or isbsd): args += ['-platformpluginpath', sys.extensions_location, '-platform', 'headless'] _store_app = QApplication(args) - _store_app.headless = headless + if headless and (islinux or isbsd): + _store_app.headless = True import traceback # This is needed because as of PyQt 5.4 if sys.execpthook == # sys.__excepthook__ PyQt will abort the application on an @@ -1175,14 +1175,17 @@ def ensure_app(): pass sys.excepthook = eh -def must_use_qt(): +def app_is_headless(): + return getattr(_store_app, 'headless', False) + +def must_use_qt(headless=True): ''' This function should be called if you want to use Qt for some non-GUI task like rendering HTML/SVG or using a headless browser. It will raise a RuntimeError if using Qt is not possible, which will happen if the current thread is not the main GUI thread. On linux, it uses a special QPA headless plugin, so that the X server does not need to be running. ''' global gui_thread - ensure_app() + ensure_app(headless=headless) if gui_thread is None: gui_thread = QThread.currentThread() if gui_thread is not QThread.currentThread(): diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 01366d466a..123b66ea34 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -487,7 +487,7 @@ class BasicNewsRecipe(Recipe): if getattr(self, 'browser', None) is not None: return self.clone_browser(self.browser) from calibre.web.jsbrowser.browser import Browser - br = Browser() + br = Browser(headless=not self.test) with br: self.javascript_login(br, self.username, self.password) kwargs['user_agent'] = br.user_agent diff --git a/src/calibre/web/jsbrowser/browser.py b/src/calibre/web/jsbrowser/browser.py index 58f55c1610..f2ce3643a7 100644 --- a/src/calibre/web/jsbrowser/browser.py +++ b/src/calibre/web/jsbrowser/browser.py @@ -14,15 +14,15 @@ from threading import current_thread from PyQt5.QtWebKit import QWebSettings, QWebElement from PyQt5.QtWebKitWidgets import QWebPage, QWebView from PyQt5.Qt import ( - QObject, QNetworkAccessManager, QNetworkDiskCache, QCoreApplication, - QNetworkProxy, QNetworkProxyFactory, QEventLoop, QUrl, pyqtSignal, - QDialog, QVBoxLayout, QSize, QNetworkCookieJar, Qt, pyqtSlot, QPixmap) + QObject, QNetworkAccessManager, QNetworkDiskCache, QNetworkProxy, + QNetworkProxyFactory, QEventLoop, QUrl, pyqtSignal, QDialog, QVBoxLayout, + QSize, QNetworkCookieJar, Qt, pyqtSlot, QPixmap) from calibre import USER_AGENT, prints, get_proxies, get_proxy_info, prepare_string_for_xml from calibre.constants import ispy3, cache_dir from calibre.ptempfile import PersistentTemporaryDirectory from calibre.utils.logging import ThreadSafeLog -from calibre.gui2 import must_use_qt +from calibre.gui2 import must_use_qt, app_is_headless from calibre.web.jsbrowser.forms import FormsMixin, default_timeout class Timeout(Exception): @@ -364,9 +364,12 @@ class Browser(QObject, FormsMixin): verbosity=0, # The default timeout (in seconds) - default_timeout=30 + default_timeout=30, + + # If True, do not connect to the X server on linux + headless=True ): - must_use_qt() + must_use_qt(headless=headless) QObject.__init__(self) FormsMixin.__init__(self) @@ -682,7 +685,7 @@ class Browser(QObject, FormsMixin): ''' Show the currently loaded web page in a window. Useful for debugging. ''' - if getattr(QCoreApplication.instance(), 'headless', False): + if app_is_headless(): raise RuntimeError('Cannot show browser when running in a headless Qt application') view = BrowserView(self.page) view.exec_()