split error handling into separate file; remove dependency on recurring timer

This commit is contained in:
Damien Elmes 2011-03-27 12:25:53 +09:00
parent fb0a0d5bdb
commit e6b153df13
4 changed files with 79 additions and 89 deletions

67
aqt/errors.py Normal file
View file

@ -0,0 +1,67 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# -*- 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, <br>
or your deck may have a problem.
<p>To confirm it's not a problem with your deck, please run
<b>Tools > Advanced > Check Database</b>.
<p>If that doesn't fix the problem, please copy the following<br>
into a bug report:<br>
""")
pluginText = _("""\
An error occurred in a plugin. Please contact the plugin author.<br>
Please do not file a bug report with Anki.<br>""")
if "plugin" in error:
txt = pluginText
else:
txt = stdText
# show dialog
txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
showText(txt, type="html")
self.mw.progress.clear()

View file

@ -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</button>''' % (
##########################################################################
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, <br>
or your deck may have a problem.
<p>To confirm it's not a problem with your deck, please <b>restart<br>
Anki</b> and run <b>Tools > Advanced > Check Database</b>.
<p>If that doesn't fix the problem, please copy the following<br>
into a bug report:<br>
""")
pluginText = _("""\
An error occurred in a plugin. Please contact the plugin author.<br>
Please do not file a bug report with Anki.<br>""")
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 + "<div style='white-space: pre-wrap'>" + error + "</div>")
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:

View file

@ -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

View file

@ -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)