Try restarting the render process on crash

Also only load the link reports web view on demand
This commit is contained in:
Kovid Goyal 2018-07-29 16:43:36 +05:30
parent c9b2578b33
commit c6d6716965
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 56 additions and 20 deletions

View File

@ -146,6 +146,7 @@ class Boss(QObject):
self.gui.preview.split_start_requested.connect(self.split_start_requested)
self.gui.preview.split_requested.connect(self.split_requested)
self.gui.preview.link_clicked.connect(self.link_clicked)
self.gui.preview.render_process_restarted.connect(self.report_render_process_restart)
self.gui.check_book.item_activated.connect(self.check_item_activated)
self.gui.check_book.check_requested.connect(self.check_requested)
self.gui.check_book.fix_requested.connect(self.fix_requested)
@ -170,6 +171,9 @@ class Boss(QObject):
self.gui.reports.refresh_starting.connect(self.commit_all_editors_to_container)
self.gui.reports.delete_requested.connect(self.delete_requested)
def report_render_process_restart(self):
self.gui.show_status_message(_('The Qt WebEngine Render process crashed and has been restarted'))
@property
def currently_editing(self):
' Return the name of the file being edited currently or None if no file is being edited '

View File

@ -14,8 +14,8 @@ from functools import partial
from threading import Thread
from PyQt5.Qt import (
QApplication, QBuffer, QByteArray, QIcon, QMenu, QSize, QTimer,
QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal
QApplication, QBuffer, QByteArray, QIcon, QMenu, QSize, QTimer, QToolBar, QUrl,
QVBoxLayout, QWidget, pyqtSignal
)
from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler
from PyQt5.QtWebEngineWidgets import (
@ -30,7 +30,10 @@ from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
from calibre.ebooks.oeb.polish.parsing import parse
from calibre.gui2 import NO_URL_FORMATTING, error_dialog, open_url
from calibre.gui2.tweak_book import TOP, actions, current_container, editors, tprefs
from calibre.gui2.webengine import create_script, insert_scripts, secure_webengine, Bridge, from_js, to_js
from calibre.gui2.webengine import (
Bridge, RestartingWebEngineView, create_script, from_js, insert_scripts,
secure_webengine, to_js
)
from calibre.gui2.widgets2 import HistoryLineEdit2
from calibre.utils.ipc.simple_worker import offload_worker
from polyglot.builtins import unicode_type
@ -325,10 +328,10 @@ class WebPage(QWebEnginePage):
self.bridge.set_split_mode.emit(1 if enabled else 0)
class WebView(QWebEngineView):
class WebView(RestartingWebEngineView):
def __init__(self, parent=None):
QWebEngineView.__init__(self, parent)
RestartingWebEngineView.__init__(self, parent)
self.inspector = QWebEngineView(self)
w = QApplication.instance().desktop().availableGeometry(self).width()
self._size_hint = QSize(int(w/3), int(w/2))
@ -337,12 +340,13 @@ class WebView(QWebEngineView):
self.setPage(self._page)
self.clear()
self.setAcceptDrops(False)
self.renderProcessTerminated.connect(self.render_process_terminated)
self.render_process_failed.connect(self.render_process_died)
def render_process_terminated(self):
def render_process_died(self):
error_dialog(self, _('Render process crashed'), _(
'The Qt WebEngine Render process has crashed so Preview/Live css'
' will not work. You should try restarting the editor.'), show=True)
'The Qt WebEngine Render process has crashed so Preview/Live css will not work.'
' You should try restarting the editor.')
, show=True)
def sizeHint(self):
return self._size_hint
@ -393,6 +397,7 @@ class Preview(QWidget):
link_clicked = pyqtSignal(object, object)
refresh_starting = pyqtSignal()
refreshed = pyqtSignal()
render_process_restarted = pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent)
@ -403,6 +408,7 @@ class Preview(QWidget):
self.view._page.bridge.request_sync.connect(self.request_sync)
self.view._page.bridge.request_split.connect(self.request_split)
self.view._page.loadFinished.connect(self.load_finished)
self.view.render_process_restarted.connect(self.render_process_restarted)
self.pending_go_to_anchor = None
self.inspector = self.view.inspector
l.addWidget(self.view)

View File

@ -21,7 +21,6 @@ from PyQt5.Qt import (
QStyledItemDelegate, QModelIndex, QRect, QStyle, QPalette, QTimer, QMenu,
QAbstractItemModel, QTreeView, QFont, QRadioButton, QHBoxLayout,
QFontDatabase, QComboBox, QUrl)
from PyQt5.QtWebEngineWidgets import QWebEngineView
from calibre import human_readable, fit_image
from calibre.constants import DEBUG
@ -29,7 +28,7 @@ from calibre.ebooks.oeb.polish.report import (
gather_data, CSSEntry, CSSFileMatch, MatchLocation, ClassEntry,
ClassFileMatch, ClassElement, CSSRule, LinkLocation)
from calibre.gui2 import error_dialog, question_dialog, choose_save_file, open_url
from calibre.gui2.webengine import secure_webengine
from calibre.gui2.webengine import secure_webengine, RestartingWebEngineView
from calibre.gui2.tweak_book import current_container, tprefs, dictionaries
from calibre.gui2.tweak_book.widgets import Dialog
from calibre.gui2.progress_indicator import ProgressIndicator
@ -578,7 +577,7 @@ class LinksModel(FileCollection):
pass
class WebView(QWebEngineView):
class WebView(RestartingWebEngineView):
def sizeHint(self):
return QSize(600, 200)
@ -604,11 +603,8 @@ class LinksWidget(QWidget):
e.textChanged.connect(f.proxy.filter_text)
s.addWidget(f)
self.links.restore_table('links-table', sort_column=1)
self.view = WebView(self)
secure_webengine(self.view)
self.view = None
self.setContextMenuPolicy(Qt.NoContextMenu)
self.view.setContextMenuPolicy(Qt.NoContextMenu)
s.addWidget(self.view)
self.ignore_current_change = False
self.current_url = None
f.current_changed.connect(self.current_changed)
@ -616,10 +612,16 @@ class LinksWidget(QWidget):
s.restoreState(read_state('links-view-splitter'))
except TypeError:
pass
s.setCollapsible(0, False), s.setCollapsible(1, True)
s.setCollapsible(0, False)
s.setStretchFactor(0, 10)
def __call__(self, data):
if self.view is None:
self.view = WebView(self)
secure_webengine(self.view)
self.view.setContextMenuPolicy(Qt.NoContextMenu)
self.splitter.addWidget(self.view)
self.splitter.setCollapsible(1, True)
self.ignore_current_change = True
self.model(data)
self.filter_edit.clear()
@ -644,11 +646,13 @@ class LinksWidget(QWidget):
if link.anchor.id:
url.setFragment(link.anchor.id)
if url is None:
self.view.setHtml('<p>' + _('No destination found for this link'))
if self.view:
self.view.setHtml('<p>' + _('No destination found for this link'))
self.current_url = url
elif url != self.current_url:
self.current_url = url
self.view.setUrl(url)
if self.view:
self.view.setUrl(url)
def double_clicked(self, index):
link = index.data(Qt.UserRole)

View File

@ -6,10 +6,11 @@ from __future__ import absolute_import, division, print_function, unicode_litera
import json
from PyQt5.Qt import QObject, pyqtSignal
from PyQt5.Qt import QObject, Qt, pyqtSignal
from PyQt5.QtWebEngineWidgets import QWebEngineScript, QWebEngineView
from calibre import prints
from calibre.utils.monotonic import monotonic
from calibre.utils.rapydscript import special_title
@ -125,6 +126,27 @@ class Bridge(QObject):
traceback.print_exc()
class RestartingWebEngineView(QWebEngineView):
render_process_restarted = pyqtSignal()
render_process_failed = pyqtSignal()
def __init__(self, parent=None):
QWebEngineView.__init__(self, parent)
self._last_reload_at = None
self.renderProcessTerminated.connect(self.render_process_terminated)
self.render_process_restarted.connect(self.reload, type=Qt.QueuedConnection)
def render_process_terminated(self):
if self._last_reload_at is not None and monotonic() - self._last_reload_at < 2:
self.render_process_failed.emit()
print('The Qt WebEngine Render process crashed too often')
else:
self._last_reload_at = monotonic()
self.render_process_restarted.emit()
prints('The Qt WebEngine Render process crashed, restarting it')
if __name__ == '__main__':
from calibre.gui2 import Application
from calibre.gui2.tweak_book.preview import WebPage