Handle exceptions with a crash dialog.

This commit is contained in:
Florian Bruhin 2014-01-30 20:42:47 +01:00
parent a71684ea0f
commit 2c276b98a4
2 changed files with 93 additions and 7 deletions

View File

@ -1,7 +1,9 @@
""" Initialization of qutebrowser and application-wide things """
import os
import sys
import logging
import traceback
import faulthandler
from signal import signal, SIGINT
from argparse import ArgumentParser
@ -12,12 +14,13 @@ from argparse import ArgumentParser
import qutebrowser.utils.harfbuzz as harfbuzz
harfbuzz.fix()
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QApplication, QDialog
from PyQt5.QtCore import QUrl, QTimer
import qutebrowser.commands.utils as cmdutils
import qutebrowser.utils.config as config
from qutebrowser.widgets.mainwindow import MainWindow
from qutebrowser.widgets import CrashDialog
from qutebrowser.commands.keys import KeyParser
from qutebrowser.utils.appdirs import AppDirs
@ -107,32 +110,57 @@ class QuteBrowser(QApplication):
fallback='http://ddg.gg/').split(','):
self.mainwindow.tabs.tabopen(url)
def _tmp_exception_hook(self, exctype, value, traceback):
def _tmp_exception_hook(self, exctype, excvalue, tb):
"""Handle exceptions while initializing by simply exiting.
This is only temporary and will get replaced by exception_hook later.
It's necessary because PyQt seems to ignore exceptions by default.
"""
sys.__excepthook__(exctype, value, traceback)
sys.__excepthook__(exctype, excvalue, tb)
self.exit(1)
def _exception_hook(self, exctype, value, traceback):
def _exception_hook(self, exctype, excvalue, tb):
"""Handle uncaught python exceptions.
It'll try very hard to write all open tabs to a file, and then exit
gracefully.
"""
# pylint: disable=broad-except
sys.__excepthook__(exctype, value, traceback)
exc = (exctype, excvalue, tb)
traceback.print_exception(*exc)
pages = []
try:
for tabidx in range(self.mainwindow.tabs.count()):
try:
# FIXME write to some file
print(self.mainwindow.tabs.widget(tabidx).url().url())
url = self.mainwindow.tabs.widget(tabidx).url().toString()
url = url.strip()
if url:
pages.append(url)
except Exception:
pass
except Exception:
pass
try:
history = self.mainwindow.status.cmd.history[-5:]
except Exception:
history = []
dlg = CrashDialog(pages, history, exc)
ret = dlg.exec_()
if ret == QDialog.Accepted: # restore
os.environ['PYTHONPATH'] = os.pathsep.join(sys.path)
# FIXME we might want to use argparse's features to not open pages
# again if they were opened via cmdline
argv = [sys.executable] + sys.argv + pages
logging.debug('Running {} with args {}'.format(sys.executable,
argv))
sys.stdout.flush()
# FIXME this seems broken on Windows, execv() splits on whitespace
# in arguments?!?
os.execv(sys.executable, argv)
self.exit(1)
def _python_hacks(self):

View File

@ -1 +1,59 @@
"""The Qt widgets needed by qutebrowser."""
import sys
import traceback
from PyQt5.QtWidgets import (QDialog, QLabel, QTextEdit, QVBoxLayout,
QHBoxLayout, QPushButton)
import qutebrowser.utils as utils
class CrashDialog(QDialog):
"""Dialog which gets shown after there was a crash."""
def __init__(self, pages, cmdhist, exc):
super().__init__()
self.setFixedSize(500, 350)
self.setWindowTitle('Whoops!')
vbox = QVBoxLayout()
lbl = QLabel(self)
#lbl.setGeometry(5, 5, 395, 295)
lbl.setText(
'Argh! qutebrowser crashed unexpectedly.<br/>'
'Please review the info below to remove sensitive data and then '
'submit it to '
'<a href="mailto:me@the-compiler.org">me@the-compiler.org</a>.'
'<br/><br/>You can click "Restore tabs" to attempt to reopen your '
'open pages.'
)
vbox.addWidget(lbl)
txt = QTextEdit(self)
txt.setReadOnly(True)
#txt.setGeometry(5, 400, 395, 295)
txt.setText(
'==== Version info ====\n{}\n\n'.format(utils.version()) +
'==== Exception ====\n{}\n'.format(
''.join(traceback.format_exception(*exc))) +
'==== Open pages ====\n{}\n\n'.format('\n'.join(pages)) +
'==== Command history ====\n{}\n\n'.format('\n'.join(cmdhist)) +
'==== Commandline args ====\n{}'.format(' '.join(sys.argv[1:]))
)
vbox.addWidget(txt)
self.setLayout(vbox)
hbox = QHBoxLayout()
btn_quit = QPushButton(self)
btn_quit.setText('Quit')
btn_quit.clicked.connect(self.reject)
hbox.addWidget(btn_quit)
btn_restore = QPushButton(self)
btn_restore.setText('Restore tabs')
btn_restore.clicked.connect(self.accept)
btn_restore.setDefault(True)
hbox.addWidget(btn_restore)
vbox.addLayout(hbox)
self.show()