When errors occur in lots of background jobs, add an option to the error message to temporarily suppress subsequent error messages. Fixes #886904 ("Error:" boxes during conversion)

This commit is contained in:
Kovid Goyal 2012-07-27 10:18:05 +05:30
parent ffdbd911e0
commit bd11311242
2 changed files with 47 additions and 10 deletions

View File

@ -9,7 +9,7 @@ import sys
from PyQt4.Qt import (QDialog, QIcon, QApplication, QSize, QKeySequence, from PyQt4.Qt import (QDialog, QIcon, QApplication, QSize, QKeySequence,
QAction, Qt, QTextBrowser, QDialogButtonBox, QVBoxLayout, QGridLayout, QAction, Qt, QTextBrowser, QDialogButtonBox, QVBoxLayout, QGridLayout,
QLabel, QPlainTextEdit, QTextDocument) QLabel, QPlainTextEdit, QTextDocument, QCheckBox, pyqtSignal)
from calibre.constants import __version__, isfrozen from calibre.constants import __version__, isfrozen
from calibre.gui2.dialogs.message_box_ui import Ui_Dialog from calibre.gui2.dialogs.message_box_ui import Ui_Dialog
@ -270,12 +270,14 @@ class ErrorNotification(MessageBox): # {{{
class JobError(QDialog): # {{{ class JobError(QDialog): # {{{
WIDTH = 600 WIDTH = 600
do_pop = pyqtSignal()
def __init__(self, gui): def __init__(self, gui):
QDialog.__init__(self, gui) QDialog.__init__(self, gui)
self.setAttribute(Qt.WA_DeleteOnClose, False) self.setAttribute(Qt.WA_DeleteOnClose, False)
self.gui = gui self.gui = gui
self.queue = [] self.queue = []
self.do_pop.connect(self.pop, type=Qt.QueuedConnection)
self._layout = l = QGridLayout() self._layout = l = QGridLayout()
self.setLayout(l) self.setLayout(l)
@ -285,6 +287,7 @@ class JobError(QDialog): # {{{
self.icon_label.setPixmap(self.icon.pixmap(128, 128)) self.icon_label.setPixmap(self.icon.pixmap(128, 128))
self.icon_label.setMaximumSize(QSize(128, 128)) self.icon_label.setMaximumSize(QSize(128, 128))
self.msg_label = QLabel('<p>&nbsp;') self.msg_label = QLabel('<p>&nbsp;')
self.msg_label.setStyleSheet('QLabel { margin-top: 1ex; }')
self.msg_label.setWordWrap(True) self.msg_label.setWordWrap(True)
self.msg_label.setTextFormat(Qt.RichText) self.msg_label.setTextFormat(Qt.RichText)
self.det_msg = QPlainTextEdit(self) self.det_msg = QPlainTextEdit(self)
@ -302,17 +305,25 @@ class JobError(QDialog): # {{{
self.det_msg_toggle.clicked.connect(self.toggle_det_msg) self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
self.det_msg_toggle.setToolTip( self.det_msg_toggle.setToolTip(
_('Show detailed information about this error')) _('Show detailed information about this error'))
self.suppress = QCheckBox(self)
self.suppress.setVisible(False)
l.addWidget(self.icon_label, 0, 0, 1, 1) l.addWidget(self.icon_label, 0, 0, 1, 1)
l.addWidget(self.msg_label, 0, 1, 1, 1, Qt.AlignLeft|Qt.AlignTop) l.addWidget(self.msg_label, 0, 1, 1, 1, Qt.AlignTop)
l.addWidget(self.det_msg, 1, 0, 1, 2) l.addWidget(self.det_msg, 1, 0, 1, 2)
l.addWidget(self.suppress, 2, 0, 1, 2, Qt.AlignLeft|Qt.AlignBottom)
l.addWidget(self.bb, 2, 0, 1, 2, Qt.AlignRight|Qt.AlignBottom) l.addWidget(self.bb, 3, 0, 1, 2, Qt.AlignRight|Qt.AlignBottom)
l.setColumnStretch(1, 100)
self.setModal(False) self.setModal(False)
self.base_height = max(200, self.sizeHint().height() + 20) self.base_height = max(200, self.sizeHint().height() + 20)
self.do_resize() self.do_resize()
def update_suppress_state(self):
self.suppress.setText(_(
'Hide the remaining %d error messages'%len(self.queue)))
self.suppress.setVisible(len(self.queue) > 3)
def copy_to_clipboard(self, *args): def copy_to_clipboard(self, *args):
d = QTextDocument() d = QTextDocument()
d.setHtml(self.msg_label.text()) d.setHtml(self.msg_label.text())
@ -342,6 +353,33 @@ class JobError(QDialog): # {{{
self.bb.button(self.bb.Close).setFocus(Qt.OtherFocusReason) self.bb.button(self.bb.Close).setFocus(Qt.OtherFocusReason)
return ret return ret
def show_error(self, title, msg, det_msg=u''):
self.queue.append((title, msg, det_msg))
self.update_suppress_state()
self.pop()
def pop(self):
if not self.queue or self.isVisible(): return
title, msg, det_msg = self.queue.pop(0)
self.setWindowTitle(title)
self.msg_label.setText(msg)
self.det_msg.setPlainText(det_msg)
self.det_msg.setVisible(False)
self.det_msg_toggle.setText(self.show_det_msg)
self.det_msg_toggle.setVisible(True)
self.suppress.setChecked(False)
self.update_suppress_state()
if not det_msg:
self.det_msg_toggle.setVisible(False)
self.do_resize()
self.show()
def done(self, r):
if self.suppress.isChecked():
self.queue = []
self.do_pop.emit()
return QDialog.done(self, r)
# }}} # }}}
if __name__ == '__main__': if __name__ == '__main__':
@ -349,7 +387,7 @@ if __name__ == '__main__':
from calibre.gui2.preferences import init_gui from calibre.gui2.preferences import init_gui
gui = init_gui() gui = init_gui()
d = JobError(gui) d = JobError(gui)
d.show() d.show_error('test title', 'some long meaningless test message')
app.exec_() app.exec_()
gui.shutdown() gui.shutdown()

View File

@ -44,6 +44,7 @@ from calibre.gui2.keyboard import Manager
from calibre.gui2.auto_add import AutoAdder from calibre.gui2.auto_add import AutoAdder
from calibre.library.sqlite import sqlite, DatabaseException from calibre.library.sqlite import sqlite, DatabaseException
from calibre.gui2.proceed import ProceedQuestion from calibre.gui2.proceed import ProceedQuestion
from calibre.gui2.dialogs.message_box import JobError
class Listener(Thread): # {{{ class Listener(Thread): # {{{
@ -111,6 +112,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.proceed_requested.connect(self.do_proceed, self.proceed_requested.connect(self.do_proceed,
type=Qt.QueuedConnection) type=Qt.QueuedConnection)
self.proceed_question = ProceedQuestion(self) self.proceed_question = ProceedQuestion(self)
self.job_error_dialog = JobError(self)
self.keyboard = Manager(self) self.keyboard = Manager(self)
_gui = self _gui = self
self.opts = opts self.opts = opts
@ -690,12 +692,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
except: except:
pass pass
if not minz: if not minz:
d = error_dialog(self, dialog_title, self.job_error_dialog.show_error(dialog_title,
_('<b>Failed</b>')+': '+unicode(job.description), _('<b>Failed</b>')+': '+unicode(job.description),
det_msg=job.details) det_msg=job.details)
d.setModal(False)
d.show()
self._modeless_dialogs.append(d)
def read_settings(self): def read_settings(self):
geometry = config['main_window_geometry'] geometry = config['main_window_geometry']