Make closing/crashing much more reliable.
This commit is contained in:
parent
966ceba1e6
commit
8c37e1c33a
@ -70,6 +70,7 @@ class QuteBrowser(QApplication):
|
||||
keyparser = None
|
||||
args = None # ArgumentParser
|
||||
timer = None # QTimer for python hacks
|
||||
shutting_down = False
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(sys.argv)
|
||||
@ -94,7 +95,8 @@ class QuteBrowser(QApplication):
|
||||
self._init_cmds()
|
||||
self.mainwindow = MainWindow()
|
||||
|
||||
self.aboutToQuit.connect(self._shutdown)
|
||||
self.setQuitOnLastWindowClosed(False)
|
||||
self.lastWindowClosed.connect(self.shutdown)
|
||||
self.mainwindow.tabs.keypress.connect(self.keyparser.handle)
|
||||
self.keyparser.set_cmd_text.connect(
|
||||
self.mainwindow.status.cmd.on_set_cmd_text)
|
||||
@ -171,6 +173,15 @@ class QuteBrowser(QApplication):
|
||||
except Exception:
|
||||
history = []
|
||||
|
||||
# Try to shutdown gracefully
|
||||
try:
|
||||
self.shutdown(do_quit=False)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
self.lastWindowClosed.disconnect(self.shutdown)
|
||||
except TypeError:
|
||||
logging.exception("Preventing shutdown failed.")
|
||||
QApplication.closeAllWindows()
|
||||
dlg = CrashDialog(pages, history, exc)
|
||||
ret = dlg.exec_()
|
||||
@ -182,7 +193,10 @@ class QuteBrowser(QApplication):
|
||||
logging.debug('Running {} with args {}'.format(sys.executable,
|
||||
argv))
|
||||
subprocess.Popen(argv)
|
||||
sys.exit(1)
|
||||
try:
|
||||
self.quit()
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
def _python_hacks(self):
|
||||
"""Get around some PyQt-oddities by evil hacks.
|
||||
@ -246,10 +260,24 @@ class QuteBrowser(QApplication):
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def _shutdown(self):
|
||||
"""Try to shutdown everything cleanly."""
|
||||
def shutdown(self, do_quit=True):
|
||||
"""Try to shutdown everything cleanly.
|
||||
|
||||
For some reason lastWindowClosing sometimes seem to get emitted twice,
|
||||
so we make sure we only run once here.
|
||||
|
||||
quit -- Whether to quit after shutting down.
|
||||
|
||||
"""
|
||||
if self.shutting_down:
|
||||
return
|
||||
self.shutting_down = True
|
||||
logging.debug("Shutting down...")
|
||||
config.config.save()
|
||||
self.mainwindow.tabs.shutdown_complete.connect(self.quit)
|
||||
self.mainwindow.tabs.shutdown()
|
||||
if do_quit:
|
||||
self.quit()
|
||||
|
||||
@pyqtSlot(tuple)
|
||||
def cmd_handler(self, tpl):
|
||||
@ -272,7 +300,7 @@ class QuteBrowser(QApplication):
|
||||
'opencur': self.mainwindow.tabs.opencur,
|
||||
'tabopen': self.mainwindow.tabs.tabopen,
|
||||
'tabopencur': self.mainwindow.tabs.tabopencur,
|
||||
'quit': self.quit,
|
||||
'quit': self.shutdown,
|
||||
'tabclose': self.mainwindow.tabs.cur_close,
|
||||
'tabprev': self.mainwindow.tabs.switch_prev,
|
||||
'tabnext': self.mainwindow.tabs.switch_next,
|
||||
|
@ -67,6 +67,7 @@ class TabbedBrowser(TabWidget):
|
||||
cur_scroll_perc_changed = pyqtSignal(int, int)
|
||||
set_cmd_text = pyqtSignal(str) # Set commandline to a given text
|
||||
keypress = pyqtSignal('QKeyEvent')
|
||||
shutdown_complete = pyqtSignal() # All tabs have been shut down.
|
||||
_url_stack = [] # Stack of URLs of closed tabs
|
||||
_space = None # Space QShortcut
|
||||
_tabs = None
|
||||
@ -154,15 +155,22 @@ class TabbedBrowser(TabWidget):
|
||||
# FIXME maybe we actually should store the webview objects here
|
||||
self._url_stack.append(tab.url())
|
||||
self.removeTab(idx)
|
||||
try:
|
||||
self._tabs.remove(tab)
|
||||
except ValueError:
|
||||
pass
|
||||
tab.shutdown()
|
||||
tab.shutdown(callback=functools.partial(self._cb_tab_shutdown,
|
||||
tab))
|
||||
else:
|
||||
# FIXME
|
||||
pass
|
||||
|
||||
def _cb_tab_shutdown(self, tab):
|
||||
"""Called after a tab has been shut down completely."""
|
||||
try:
|
||||
self._tabs.remove(tab)
|
||||
except ValueError:
|
||||
logging.error("tab {} could not be removed from tabs {}.".format(
|
||||
tab, self._tabs))
|
||||
if not self._tabs: # all tabs shut down
|
||||
self.shutdown_complete.emit()
|
||||
|
||||
def cur_reload(self, count=None):
|
||||
"""Reload the current/[count]th tab.
|
||||
|
||||
@ -433,9 +441,14 @@ class TabbedBrowser(TabWidget):
|
||||
|
||||
def shutdown(self):
|
||||
"""Try to shut down all tabs cleanly."""
|
||||
self.currentChanged.disconnect()
|
||||
try:
|
||||
self.currentChanged.disconnect()
|
||||
except TypeError:
|
||||
pass
|
||||
for tabidx in range(self.count()):
|
||||
self.widget(tabidx).shutdown()
|
||||
tab = self.widget(tabidx)
|
||||
tab.shutdown(callback=functools.partial(self._cb_tab_shutdown,
|
||||
tab))
|
||||
|
||||
|
||||
class BrowserTab(QWebView):
|
||||
@ -451,12 +464,15 @@ class BrowserTab(QWebView):
|
||||
open_tab = pyqtSignal('QUrl')
|
||||
linkHovered = pyqtSignal(str, str, str)
|
||||
_scroll_pos = (-1, -1)
|
||||
_shutdown_callback = None # callback to be called after shutdown
|
||||
_open_new_tab = False # open new tab for the next action
|
||||
_destroyed = None # Dict of all items to be destroyed.
|
||||
# dict of tab specific signals, and the values we last got from them.
|
||||
signal_cache = None
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._destroyed = {}
|
||||
self.setPage(BrowserPage())
|
||||
self.signal_cache = SignalCache(uncached=['linkHovered'])
|
||||
self.loadProgress.connect(self.on_load_progress)
|
||||
@ -513,7 +529,7 @@ class BrowserTab(QWebView):
|
||||
"""
|
||||
self.progress = prog
|
||||
|
||||
def shutdown(self):
|
||||
def shutdown(self, callback=None):
|
||||
"""Shut down the tab cleanly and remove it.
|
||||
|
||||
Inspired by [1].
|
||||
@ -521,13 +537,37 @@ class BrowserTab(QWebView):
|
||||
[1] https://github.com/integricho/path-of-a-pyqter/tree/master/qttut08
|
||||
|
||||
"""
|
||||
page = self.page()
|
||||
netman = page.networkAccessManager()
|
||||
self._shutdown_callback = callback
|
||||
try:
|
||||
# Avoid loading finished signal when stopping
|
||||
self.loadFinished.disconnect()
|
||||
except TypeError:
|
||||
logging.exception("This should never happen.")
|
||||
self.stop()
|
||||
self.close()
|
||||
self.settings().setAttribute(QWebSettings.JavascriptEnabled, False)
|
||||
self.page().deleteLater()
|
||||
|
||||
self._destroyed[page] = False
|
||||
page.destroyed.connect(functools.partial(self.on_destroyed, page))
|
||||
page.deleteLater()
|
||||
|
||||
self._destroyed[self] = False
|
||||
self.destroyed.connect(functools.partial(self.on_destroyed, self))
|
||||
self.deleteLater()
|
||||
self.page().networkAccessManager().abort_requests()
|
||||
self.page().networkAccessManager().deleteLater()
|
||||
|
||||
self._destroyed[netman] = False
|
||||
netman.abort_requests()
|
||||
netman.destroyed.connect(functools.partial(self.on_destroyed, netman))
|
||||
netman.deleteLater()
|
||||
|
||||
def on_destroyed(self, sender):
|
||||
"""Called when a subsystem has been destroyed during shutdown."""
|
||||
self._destroyed[sender] = True
|
||||
if all(self._destroyed.values()):
|
||||
if self._shutdown_callback is not None:
|
||||
self._shutdown_callback()
|
||||
|
||||
def eventFilter(self, watched, e):
|
||||
"""Dirty hack to emit a signal if the scroll position changed.
|
||||
|
Loading…
Reference in New Issue
Block a user