mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -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):
|
def setup(self):
|
||||||
self.deck = None
|
self.deck = None
|
||||||
self.state = None
|
self.state = None
|
||||||
|
self.setupThreads()
|
||||||
self.setupLang()
|
self.setupLang()
|
||||||
self.setupMainWindow()
|
self.setupMainWindow()
|
||||||
self.setupStyle()
|
self.setupStyle()
|
||||||
|
@ -181,91 +182,8 @@ title="%s">%s</button>''' % (
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def setupErrorHandler(self):
|
def setupErrorHandler(self):
|
||||||
class ErrorPipe(object):
|
import aqt.errors
|
||||||
def __init__(self, parent):
|
self.errorHandler = aqt.errors.ErrorHandler(self)
|
||||||
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()
|
|
||||||
|
|
||||||
# Main window setup
|
# Main window setup
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -416,11 +334,17 @@ counts are %d %d %d
|
||||||
if self.appUpdated:
|
if self.appUpdated:
|
||||||
self.config['version'] = aqt.appVersion
|
self.config['version'] = aqt.appVersion
|
||||||
|
|
||||||
|
def setupThreads(self):
|
||||||
|
self._mainThread = QThread.currentThread()
|
||||||
|
|
||||||
|
def inMainThread(self):
|
||||||
|
return self._mainThread == QThread.currentThread()
|
||||||
|
|
||||||
def onReload(self):
|
def onReload(self):
|
||||||
self.moveToState("auto")
|
self.moveToState("auto")
|
||||||
|
|
||||||
def onNotify(self, msg):
|
def onNotify(self, msg):
|
||||||
if self.mainThread != QThread.currentThread():
|
if not self.inMainThread():
|
||||||
# decks may be opened in a sync thread
|
# decks may be opened in a sync thread
|
||||||
sys.stderr.write(msg + "\n")
|
sys.stderr.write(msg + "\n")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -16,7 +16,6 @@ class ProgressManager(object):
|
||||||
self.app = QApplication.instance()
|
self.app = QApplication.instance()
|
||||||
self._win = None
|
self._win = None
|
||||||
self._levels = 0
|
self._levels = 0
|
||||||
self._mainThread = QThread.currentThread()
|
|
||||||
|
|
||||||
# SQLite progress handler
|
# SQLite progress handler
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -37,7 +36,7 @@ class ProgressManager(object):
|
||||||
return
|
return
|
||||||
self.lastDbProgress = time.time()
|
self.lastDbProgress = time.time()
|
||||||
# and we're in the main thread
|
# and we're in the main thread
|
||||||
if self._mainThread != QThread.currentThread():
|
if not self.mw.inMainThread():
|
||||||
return
|
return
|
||||||
# ensure timers don't fire
|
# ensure timers don't fire
|
||||||
self.inDB = True
|
self.inDB = True
|
||||||
|
|
|
@ -41,7 +41,7 @@ def showInfo(text, parent=None, help="", func=None):
|
||||||
|
|
||||||
def showText(txt, parent=None, type="text"):
|
def showText(txt, parent=None, type="text"):
|
||||||
if not parent:
|
if not parent:
|
||||||
parent = aqt.mw
|
parent = aqt.mw.app.activeWindow() or aqt.mw
|
||||||
diag = QDialog(parent)
|
diag = QDialog(parent)
|
||||||
diag.setWindowTitle("Anki")
|
diag.setWindowTitle("Anki")
|
||||||
layout = QVBoxLayout(diag)
|
layout = QVBoxLayout(diag)
|
||||||
|
|
Loading…
Reference in a new issue