7.4 KiB
wxPython:如何捕捉所有异常
原文:https://www.blog.pythonlibrary.org/2014/01/31/wxpython-how-to-catch-all-exceptions/
我在 wxPython Google 组的一个朋友问如何捕捉 wxPython 中发生的任何异常。这个问题有点复杂,因为 wxPython 是 C++库(wxWidgets)上的一个包装器。你可以在 wxPython wiki 上了解这个问题。几个 wxPython 用户提到使用 Python 的 sys.excepthook 来捕捉错误。因此,我决定根据 Andrea Gavana 在前面提到的帖子中发布的内容,写一个例子来说明这是如何工作的。我们还将看看 wiki 链接中的解决方案。
用 sys.excepthook 捕获所有错误
这比我预期的要多一点,因为我最终需要导入 Python 的 traceback 模块,我决定显示错误,所以我也创建了一个对话框。让我们看一下代码:
import sys
import traceback
import wx
import wx.lib.agw.genericmessagedialog as GMD
########################################################################
class Panel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label="Raise Exception")
btn.Bind(wx.EVT_BUTTON, self.onExcept)
#----------------------------------------------------------------------
def onExcept(self, event):
"""
Raise an error
"""
1/0
########################################################################
class Frame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Exceptions")
sys.excepthook = MyExceptionHook
panel = Panel(self)
self.Show()
########################################################################
class ExceptionDialog(GMD.GenericMessageDialog):
""""""
#----------------------------------------------------------------------
def __init__(self, msg):
"""Constructor"""
GMD.GenericMessageDialog.__init__(self, None, msg, "Exception!",
wx.OK|wx.ICON_ERROR)
#----------------------------------------------------------------------
def MyExceptionHook(etype, value, trace):
"""
Handler for all unhandled exceptions.
:param `etype`: the exception type (`SyntaxError`, `ZeroDivisionError`, etc...);
:type `etype`: `Exception`
:param string `value`: the exception error message;
:param string `trace`: the traceback header, if any (otherwise, it prints the
standard Python header: ``Traceback (most recent call last)``.
"""
frame = wx.GetApp().GetTopWindow()
tmp = traceback.format_exception(etype, value, trace)
exception = "".join(tmp)
dlg = ExceptionDialog(exception)
dlg.ShowModal()
dlg.Destroy()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = Frame()
app.MainLoop()
这段代码有点复杂,所以我们将在每一部分花点时间。面板代码上有一个按钮,该按钮将调用一个方法,该方法将导致一个zerodisvisionerror。在 Frame 类中,我们将 sys.excepthook 设置为自定义函数 MyExceptionHook 。让我们来看看这个:
#----------------------------------------------------------------------
def MyExceptionHook(etype, value, trace):
"""
Handler for all unhandled exceptions.
:param `etype`: the exception type (`SyntaxError`, `ZeroDivisionError`, etc...);
:type `etype`: `Exception`
:param string `value`: the exception error message;
:param string `trace`: the traceback header, if any (otherwise, it prints the
standard Python header: ``Traceback (most recent call last)``.
"""
frame = wx.GetApp().GetTopWindow()
tmp = traceback.format_exception(etype, value, trace)
exception = "".join(tmp)
dlg = ExceptionDialog(exception)
dlg.ShowModal()
dlg.Destroy()
这个函数接受 3 个参数:etype、value 和 traceback。我们使用 traceback 模块将这些片段放在一起,得到一个完整的回溯,我们可以将它传递给我们的消息对话框。
使用原始的错误捕获方法
robin Dunn(wxPython 的创建者)提到在上面的同一个线程中有一个关于 wiki 的解决方案,他希望看到它被用作装饰器。以下是我的实现:
import logging
import wx
import wx.lib.agw.genericmessagedialog as GMD
########################################################################
class ExceptionLogging(object):
#----------------------------------------------------------------------
def __init__(self, fn):
self.fn = fn
# create logging instance
self.log = logging.getLogger("wxErrors")
self.log.setLevel(logging.INFO)
# create a logging file handler / formatter
log_fh = logging.FileHandler("error.log")
formatter = logging.Formatter("%(asctime)s - %(name)s - %(message)s")
log_fh.setFormatter(formatter)
self.log.addHandler(log_fh)
#----------------------------------------------------------------------
def __call__(self,evt):
try:
self.fn(self, evt)
except Exception, e:
self.log.exception("Exception")
########################################################################
class Panel(wx.Panel):
""""""
#----------------------------------------------------------------------
def __init__(self, parent):
"""Constructor"""
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label="Raise Exception")
btn.Bind(wx.EVT_BUTTON, self.onExcept)
#----------------------------------------------------------------------
@ExceptionLogging
def onExcept(self, event):
"""
Raise an error
"""
1/0
########################################################################
class Frame(wx.Frame):
""""""
#----------------------------------------------------------------------
def __init__(self):
"""Constructor"""
wx.Frame.__init__(self, None, title="Exceptions")
panel = Panel(self)
self.Show()
#----------------------------------------------------------------------
if __name__ == "__main__":
app = wx.App(False)
frame = Frame()
app.MainLoop()
我们使用自定义异常类来记录错误。为了将该类应用于我们的事件处理程序,我们使用@classname 用该类来修饰它们,在本例中该类被翻译成 @ExceptionLogging 。因此,无论何时调用这个事件处理程序,它都会通过装饰器运行,装饰器将事件处理程序包装在 try/except 中,并将所有异常记录到磁盘中。我不完全确定本文中提到的两种方法是否能捕捉到相同的错误。欢迎在评论中告诉我。
相关信息
- PyQt 邮件列表上的捕获错误线程