From fa902c5d82fd6366289fe8e9d13d72ba4dfc8a85 Mon Sep 17 00:00:00 2001
From: Florian Bruhin
Date: Wed, 27 Sep 2017 23:04:18 +0200
Subject: [PATCH] Improve error dialogs when QtWebKit/QtWebEngine was not found
---
qutebrowser/misc/backendproblem.py | 113 +++++++++++++++++++++++++++--
qutebrowser/misc/earlyinit.py | 31 --------
qutebrowser/misc/savemanager.py | 5 ++
3 files changed, 111 insertions(+), 38 deletions(-)
diff --git a/qutebrowser/misc/backendproblem.py b/qutebrowser/misc/backendproblem.py
index 15d27212f..caa21f97d 100644
--- a/qutebrowser/misc/backendproblem.py
+++ b/qutebrowser/misc/backendproblem.py
@@ -22,15 +22,16 @@
import os
import sys
import functools
+import html
import attr
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QDialog, QPushButton, QHBoxLayout,
- QVBoxLayout, QLabel)
+ QVBoxLayout, QLabel, QMessageBox)
from qutebrowser.config import config
-from qutebrowser.utils import usertypes, objreg, version
-from qutebrowser.misc import objects
+from qutebrowser.utils import usertypes, objreg, version, qtutils
+from qutebrowser.misc import objects, msgbox
_Result = usertypes.enum('_Result', ['quit', 'restart'], is_int=True,
@@ -67,8 +68,8 @@ class _Dialog(QDialog):
"qutebrowser tried to start with the {backend} backend but "
"failed because {because}.
{text}"
"Forcing the {other_backend.name} backend
"
- "This forces usage of the {other_backend.name} backend. "
- "This sets the backend = '{other_setting}' setting "
+ "
This forces usage of the {other_backend.name} backend by "
+ "setting the backend = '{other_setting}' option "
"(if you have a config.py file, you'll need to set "
"this manually).
".format(
backend=backend.name, because=because, text=text,
@@ -101,6 +102,8 @@ class _Dialog(QDialog):
def _change_setting(self, setting, value):
"""Change the given setting and restart."""
config.instance.set_obj(setting, value, save_yaml=True)
+ save_manager = objreg.get('save-manager')
+ save_manager.save_all(is_exit=True)
self.done(_Result.restart)
@@ -110,13 +113,15 @@ def _show_dialog(*args, **kwargs):
status = dialog.exec_()
- if status == _Result.quit:
+ if status in [_Result.quit, QDialog.Rejected]:
sys.exit(usertypes.Exit.err_init)
elif status == _Result.restart:
# FIXME pass --backend webengine
quitter = objreg.get('quitter')
quitter.restart()
sys.exit(usertypes.Exit.err_init)
+ else:
+ assert False, status
def _handle_nouveau_graphics():
@@ -143,7 +148,7 @@ def _handle_nouveau_graphics():
"This allows you to use the newer QtWebEngine backend (based "
"on Chromium) but could have noticable performance impact "
"(depending on your hardware). "
- "This sets the force_software_rendering = True setting "
+ "This sets the force_software_rendering = True option "
"(if you have a config.py file, you'll need to set this "
"manually).
",
buttons=[button],
@@ -174,7 +179,101 @@ def _handle_wayland():
assert False
+@attr.s
+class BackendImports:
+
+ """Whether backend modules could be imported."""
+
+ webkit_available = attr.ib(default=None)
+ webengine_available = attr.ib(default=None)
+ webkit_error = attr.ib(default=None)
+ webengine_error = attr.ib(default=None)
+
+
+def _try_import_backends():
+ """Check whether backends can be imported and return BackendImports."""
+ results = BackendImports()
+
+ try:
+ from PyQt5 import QtWebKit
+ from PyQt5 import QtWebKitWidgets
+ except ImportError as e:
+ results.webkit_available = False
+ results.webkit_error = str(e)
+ else:
+ if qtutils.is_new_qtwebkit():
+ results.webkit_available = True
+ else:
+ results.webkit_available = False
+ results.webkit_error = "Unsupported legacy QtWebKit found"
+
+ try:
+ from PyQt5 import QtWebEngineWidgets
+ except ImportError as e:
+ results.webengine_available = False
+ results.webengine_error = str(e)
+ else:
+ results.webengine_available = True
+
+ assert results.webkit_available is not None
+ assert results.webengine_available is not None
+ if not results.webkit_available:
+ assert results.webkit_error is not None
+ if not results.webengine_available:
+ assert results.webengine_error is not None
+
+ return results
+
+
+def _check_backend_modules():
+ """Check for the modules needed for QtWebKit/QtWebEngine."""
+ imports = _try_import_backends()
+
+ if imports.webkit_available and imports.webengine_available:
+ return
+ elif not imports.webkit_available and not imports.webengine_available:
+ text = ("qutebrowser needs QtWebKit or QtWebEngine, but neither "
+ "could be imported!
"
+ "The errors encountered were:
"
+ "- QtWebKit: {webkit_error}"
+ "
- QtWebEngine: {webengine_error}"
+ "
".format(
+ webkit_error=html.escape(imports.webkit_error),
+ webengine_error=html.escape(imports.webengine_error)))
+ errbox = msgbox.msgbox(parent=None,
+ title="No backend library found!",
+ text=text,
+ icon=QMessageBox.Critical,
+ plain_text=False)
+ errbox.exec_()
+ sys.exit(usertypes.Exit.err_init)
+ elif objects.backend == usertypes.Backend.QtWebKit:
+ if imports.webkit_available:
+ return
+ assert imports.webengine_available
+ _show_dialog(
+ backend=usertypes.Backend.QtWebKit,
+ because="QtWebKit could not be imported",
+ text="The error encountered was:
{}
".format(
+ html.escape(imports.webkit_error))
+ )
+ elif objects.backend == usertypes.Backend.QtWebEngine:
+ if imports.webengine_available:
+ return
+ assert imports.webkit_available
+ _show_dialog(
+ backend=usertypes.Backend.QtWebEngine,
+ because="QtWebEngine could not be imported",
+ text="The error encountered was:
{}
".format(
+ html.escape(imports.webengine_error))
+ )
+
+ # Should never be reached
+ assert False
+
+
def init():
+ _check_backend_modules()
if objects.backend == usertypes.Backend.QtWebEngine:
_handle_wayland()
_handle_nouveau_graphics()
diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py
index c2ab454ac..f481f4dba 100644
--- a/qutebrowser/misc/earlyinit.py
+++ b/qutebrowser/misc/earlyinit.py
@@ -247,35 +247,6 @@ def check_libraries():
_check_modules(modules)
-def check_backend_libraries(backend):
- """Make sure the libraries needed by the given backend are available.
-
- Args:
- backend: The backend as usertypes.Backend member.
- """
- from qutebrowser.utils import usertypes
- if backend == usertypes.Backend.QtWebEngine:
- modules = {
- 'PyQt5.QtWebEngineWidgets':
- _missing_str("QtWebEngine", webengine=True),
- }
- else:
- assert backend == usertypes.Backend.QtWebKit, backend
- modules = {
- 'PyQt5.QtWebKit': _missing_str("PyQt5.QtWebKit"),
- 'PyQt5.QtWebKitWidgets': _missing_str("PyQt5.QtWebKitWidgets"),
- }
- _check_modules(modules)
-
-
-def check_new_webkit(backend):
- """Make sure we use QtWebEngine or a new QtWebKit."""
- from qutebrowser.utils import usertypes, qtutils
- if backend == usertypes.Backend.QtWebKit and not qtutils.is_new_qtwebkit():
- _die("qutebrowser does not support legacy QtWebKit versions anymore, "
- "see the installation docs for details.")
-
-
def remove_inputhook():
"""Remove the PyQt input hook.
@@ -338,6 +309,4 @@ def init_with_backend(backend):
"""
assert not isinstance(backend, str), backend
assert backend is not None
- check_backend_libraries(backend)
check_backend_ssl_support(backend)
- check_new_webkit(backend)
diff --git a/qutebrowser/misc/savemanager.py b/qutebrowser/misc/savemanager.py
index ddda5325b..02001902c 100644
--- a/qutebrowser/misc/savemanager.py
+++ b/qutebrowser/misc/savemanager.py
@@ -164,6 +164,11 @@ class SaveManager(QObject):
self.saveables[name].save(is_exit=is_exit, explicit=explicit,
silent=silent, force=force)
+ def save_all(self, *args, **kwargs):
+ """Save all saveables."""
+ for saveable in self.saveables:
+ self.save(saveable, *args, **kwargs)
+
@pyqtSlot()
def autosave(self):
"""Slot used when the configs are auto-saved."""