From e6b153df139380b2d05c61e2546a87987c357110 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 27 Mar 2011 12:25:53 +0900 Subject: [PATCH] split error handling into separate file; remove dependency on recurring timer --- aqt/errors.py | 67 ++++++++++++++++++++++++++++++++++ aqt/main.py | 96 ++++++------------------------------------------- aqt/progress.py | 3 +- aqt/utils.py | 2 +- 4 files changed, 79 insertions(+), 89 deletions(-) create mode 100644 aqt/errors.py diff --git a/aqt/errors.py b/aqt/errors.py new file mode 100644 index 000000000..765999c81 --- /dev/null +++ b/aqt/errors.py @@ -0,0 +1,67 @@ +# Copyright: Damien Elmes +# -*- coding: utf-8 -*- +# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html + +from PyQt4.QtCore import * +from PyQt4.QtGui import * +import sys +from aqt.utils import showText + +class ErrorHandler(QObject): + "Catch stderr and write into buffer." + + def __init__(self, mw): + QObject.__init__(self, mw) + self.mw = mw + self.timer = None + self.connect(self, SIGNAL("errorTimer"), self._setTimer) + self.pool = "" + sys.stderr = self + + def write(self, data): + # make sure we have unicode + if not isinstance(data, unicode): + data = unicode(data, "utf8", "replace") + # dump to stdout + print data.encode("utf-8") + # save in buffer + self.pool += data + # and update timer + self.setTimer() + + def setTimer(self): + # we can't create a timer from a different thread, so we post a + # message to the object on the main thread + self.emit(SIGNAL("errorTimer")) + + def _setTimer(self): + ivl = 200 + if not self.timer: + self.timer = QTimer(self.mw) + self.mw.connect(self.timer, SIGNAL("timeout()"), self.onTimeout) + self.timer.setInterval(200) + self.timer.setSingleShot(True) + self.timer.start() + + def onTimeout(self): + error = self.pool + self.pool = "" + stdText = _("""\ +An error occurred. It may have been caused by a harmless bug,
+or your deck may have a problem. +

To confirm it's not a problem with your deck, please run +Tools > Advanced > Check Database. +

If that doesn't fix the problem, please copy the following
+into a bug report:
+""") + pluginText = _("""\ +An error occurred in a plugin. Please contact the plugin author.
+Please do not file a bug report with Anki.
""") + if "plugin" in error: + txt = pluginText + else: + txt = stdText + # show dialog + txt = txt + "

" + error + "
" + showText(txt, type="html") + self.mw.progress.clear() diff --git a/aqt/main.py b/aqt/main.py index 59891e84e..4745eb789 100755 --- a/aqt/main.py +++ b/aqt/main.py @@ -61,6 +61,7 @@ class AnkiQt(QMainWindow): def setup(self): self.deck = None self.state = None + self.setupThreads() self.setupLang() self.setupMainWindow() self.setupStyle() @@ -181,91 +182,8 @@ title="%s">%s''' % ( ########################################################################## def setupErrorHandler(self): - class ErrorPipe(object): - def __init__(self, parent): - self.parent = parent - self.timer = None - self.pool = "" - self.poolUpdated = 0 - - def write(self, data): - try: - print data.encode("utf-8"), - except: - print data - self.pool += data - self.poolUpdated = time.time() - - def haveError(self): - if self.pool: - if (time.time() - self.poolUpdated) > 1: - return True - - def getError(self): - p = self.pool - self.pool = "" - try: - return unicode(p, 'utf8', 'replace') - except TypeError: - # already unicode - return p - - self.errorPipe = ErrorPipe(self) - sys.stderr = self.errorPipe - self.errorTimer = QTimer(self) - self.errorTimer.start(1000) - self.connect(self.errorTimer, - SIGNAL("timeout()"), - self.onErrorTimer) - - def onErrorTimer(self): - if self.errorPipe.haveError(): - error = self.errorPipe.getError() - if "font_manager.py" in error: - # hack for matplotlib errors on osx - return - if "Audio player not found" in error: - showInfo(_("Couldn't play sound. Please install mplayer.")) - return - if "ERR-0100" in error: - showInfo(error) - return - if "ERR-0101" in error: - showInfo(error) - return - stdText = _("""\ - -An error occurred. It may have been caused by a harmless bug,
-or your deck may have a problem. -

To confirm it's not a problem with your deck, please restart
-Anki
and run Tools > Advanced > Check Database. - -

If that doesn't fix the problem, please copy the following
-into a bug report:
-""") - pluginText = _("""\ -An error occurred in a plugin. Please contact the plugin author.
-Please do not file a bug report with Anki.
""") - if "plugin" in error: - txt = pluginText - else: - txt = stdText - # show dialog - diag = QDialog(self.app.activeWindow()) - diag.setWindowTitle("Anki") - layout = QVBoxLayout(diag) - diag.setLayout(layout) - text = QTextEdit() - text.setReadOnly(True) - text.setHtml(txt + "

" + error + "
") - layout.addWidget(text) - box = QDialogButtonBox(QDialogButtonBox.Close) - layout.addWidget(box) - self.connect(box, SIGNAL("rejected()"), diag, SLOT("reject()")) - diag.setMinimumHeight(400) - diag.setMinimumWidth(500) - diag.exec_() - self.progress.clear() + import aqt.errors + self.errorHandler = aqt.errors.ErrorHandler(self) # Main window setup ########################################################################## @@ -416,11 +334,17 @@ counts are %d %d %d if self.appUpdated: self.config['version'] = aqt.appVersion + def setupThreads(self): + self._mainThread = QThread.currentThread() + + def inMainThread(self): + return self._mainThread == QThread.currentThread() + def onReload(self): self.moveToState("auto") def onNotify(self, msg): - if self.mainThread != QThread.currentThread(): + if not self.inMainThread(): # decks may be opened in a sync thread sys.stderr.write(msg + "\n") else: diff --git a/aqt/progress.py b/aqt/progress.py index 48be4384c..aeb3bc2e2 100644 --- a/aqt/progress.py +++ b/aqt/progress.py @@ -16,7 +16,6 @@ class ProgressManager(object): self.app = QApplication.instance() self._win = None self._levels = 0 - self._mainThread = QThread.currentThread() # SQLite progress handler ########################################################################## @@ -37,7 +36,7 @@ class ProgressManager(object): return self.lastDbProgress = time.time() # and we're in the main thread - if self._mainThread != QThread.currentThread(): + if not self.mw.inMainThread(): return # ensure timers don't fire self.inDB = True diff --git a/aqt/utils.py b/aqt/utils.py index ccf778184..7fbe69fae 100644 --- a/aqt/utils.py +++ b/aqt/utils.py @@ -41,7 +41,7 @@ def showInfo(text, parent=None, help="", func=None): def showText(txt, parent=None, type="text"): if not parent: - parent = aqt.mw + parent = aqt.mw.app.activeWindow() or aqt.mw diag = QDialog(parent) diag.setWindowTitle("Anki") layout = QVBoxLayout(diag)