我在 PyQt 中创建了一个 GUI 应用程序,我想与很多人分享。有时我会遇到意想不到的异常,我认为这是理所当然的 - 每次异常之后我都会改进我的代码,它会变得越来越好。
我使用记录器来记录这些异常,并使用 PyQt 静默异常的特殊钩子。我的代码如下所示:
记录器
def setLogger(level=logging.DEBUG,
name="my_logger",
file=join("src", "log.out")):
logger = logging.getLogger(name)
logger.setLevel(level)
# console logger
ch = logging.StreamHandler()
console_lvl = logging.DEBUG
ch.setLevel(console_lvl)
formatter = logging.Formatter('%(message)s')
ch.setFormatter(formatter)
#file logger
fh = logging.FileHandler(file)
fh.setLevel(level)
formatter = logging.Formatter(
'%(asctime)s :: %(name)s - %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p')
fh.setFormatter(formatter)
logger.addHandler(ch)
logger.addHandler(fh)
return logger
GUI 中的 2 个典型类
class MainWindow(QtGui.QMainWindow):
def __init__(self):
self.logger = setLogger(name='main_window')
[...]
class UploadDialog(QtGui.QDialog):
def __init__(self):
self.logger = setLogger(name='upload_dialog')
[...]
等等
PyQt 有其静默异常 - 发生异常但程序继续运行。我在这种情况下使用特殊的钩子(在某处看到过,这不是我的代码)
import sys
sys._excepthook = sys.excepthook
def exception_hook(exctype, value, traceback):
sys._excepthook(exctype, value, traceback)
sys.exit(1)
现在我想捕获任何异常,因此我的程序的用户在发生任何崩溃时只需向我发送他们的log.out,我就会看到完整的日志。
我就是从这样的事情开始的。
try:
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
except Exception:
logger.exception("MainWindow exception")
我工作得很好,直到在 UploadDialog 内引发异常。没有任何内容被“记录”。所以我就这么做了:
#inside MainWindow class
def runDialog(self):
try:
dialog = UploadDialog()
dialog.exec_()
except Exception:
self.logger.exception("Dialog exception")
sys.exit(1)
再次,一切正常,直到WorkThread内部出现异常(继承自QThread类,用于后台上传)。又来了一次。
#inside UploadDialog
def upload(self):
# initialized as self.task = WorkThread()
try:
self.task.start()
except Exception:
self.logger.exception("Exception in WorkThread")
然后,不是在 WorkThread 启动后而是在初始化时引发了异常,所以
class UploadDialog(QtGui.QDialog):
def __init__(self):
try:
self.task = WorkThread()
except Exception:
self.logger.exception("Exception in WorkThread")
在这里,我只是抬起头对自己尖叫 - “停下来。你做错了。这不是蟒蛇式的,它很脏,它让我的眼睛流血”。
所以,我问 - 有没有一种优雅且Python式的方法来捕获在 PyQt 应用程序中用一个
try...except
块引发的绝对异常?
使用 excepthook 的目的是让您可以监视程序引发的所有异常,并集中处理它们。
因此,您应该删除大部分
try/except
块,并将异常记录在 excepthook 函数中。我说“大多数”,因为你可能会受到 bug 1230540 的影响,其中 sys.excepthook
在主线程之外没有被正确调用。请参阅跟踪器线程以获取一些解决方法,或者这个SO答案。
(PS:线程问题已在 Python-3.8 中修复。请参阅这个答案)。