Tkinter Python:捕获异常

10

开始使用Python编程时,我发现其错误报告非常容易理解。然而,现在我用Tkinter编程时,有时候即使出现异常错误,我也注意不到我的程序中存在错误:有时我只是因为要逐步调试(我使用wingIDE)而捕获它们,并在给定行处看到异常报告。但是让我困扰的是,程序并没有停止,这种情况甚至发生在包含try/error块内部。

如果我说的有意义,你是否知道一些整体方法来至少显示错误?在Tkinter中,我可以创建一个错误窗口,并在发生异常时将其填充到其中。


2
有一个优雅的解决方案:https://dev59.com/lG445IYBdhLWcg3wnrrM - Gonzo
可能是Should I make silent exceptions louder in tkinter?的重复问题。 - Stevoisiak
3个回答

9

谢谢你的帮助,我现在可以“或多或少”让它工作了。但是我作为一个新手Python程序员并不熟悉@safe语法,所以我不知道确切的代码放置位置... 我觉得我必须在每个需要被监视的函数定义前面放置@safe... 是这样吗? - alessandro
@alessandro:你说得对。这些被称为装饰器。装饰器是一种语法糖,用于调用可以包装其他函数的类和函数。 - Steven Rumbalski
@StevenRumbalski 我故意不改变 import。我只进行了向后兼容 Python 2 的编辑。 - Stevoisiak
@StevenRumbalski 我认为print语句是向前兼容的,不需要导入future模块? - Stevoisiak
@StevenVascellaro:没有使用future import时,print 3print(3)是相同的,但print 1, 2, 3print(1, 2, 3)的输出分别为1 2 3(1, 2, 3)。而执行print(1, end='')会出现语法错误。单个参数没有问题,因为括号可以被忽略。多个参数变成元组,因为逗号。我认为命名参数是一个问题,因为Python已经决定print是一个语句,所以它甚至不会尝试将其视为函数。 - Steven Rumbalski

8

正如@jochen-ritzel所说(Python tkinter中的无声异常是否应该加强提示?), 你可以覆盖tk.TK.report_callback_exception():

import traceback
import tkMessageBox

# You would normally put that on the App class
def show_error(self, *args):
    err = traceback.format_exception(*args)
    tkMessageBox.showerror('Exception',err)
# but this works too
tk.Tk.report_callback_exception = show_error

1

我更喜欢显式地扩展Tk的Toplevel小部件,它主要代表应用程序的主窗口,而不是注入一个hack:

import tkinter as tk
from tkinter import messagebox

class FaultTolerantTk(tk.Tk):
    def report_callback_exception(self, exc, val, tb):
        self.destroy_unmapped_children(self)
        messagebox.showerror('Error!', val)

    # NOTE: It's an optional method. Add one if you have multiple windows to open
    def destroy_unmapped_children(self, parent):
        """
        Destroys unmapped windows (empty gray ones which got an error during initialization)
        recursively from bottom (root window) to top (last opened window).
        """
        children = parent.children.copy()
        for index, child in children.items():
            if not child.winfo_ismapped():
                parent.children.pop(index).destroy()
            else:
                self.destroy_unmapped_children(child)

def main():
    root = FaultTolerantTk()
    ...
    root.mainloop()


if __name__ == '__main__':
    main()

在我看来,这似乎是正确的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接