mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
split error handling into separate file; remove dependency on recurring timer
This commit is contained in:
parent
fb0a0d5bdb
commit
e6b153df13
4 changed files with 79 additions and 89 deletions
67
aqt/errors.py
Normal file
67
aqt/errors.py
Normal 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()
|
96
aqt/main.py
96
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</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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue