为什么通用“尝试/除外"块不适用于 Python TkInter 回调?

问题描述:

我已经确定,在 Python TkInter GUI 程序中,最佳实践是将整个内容包含在 try/except 块中,以便捕获所有异常并将它们呈现给最终用户(而不是默默地出错)或者程序无故退出).

I have determined that in a Python TkInter GUI program, it is best practice to enclose the entire thing in a try / except block in order to catch all exceptions and present them to the end user (as opposed to something going wrong silently or the program exiting for seemingly no reason).

然而,这种方法有一些问题.考虑以下小程序,当单击按钮时尝试除以 0:

However, this approach has some problems. Consider the following tiny program that attempts to divide by 0 when a button is clicked:

import tkinter

class Foo():
    def __init__(self):
        # Initialize a new GUI window
        root = tkinter.Tk()

        # The "Generic error" message is shown when the following is uncommented
        #number = 1 / 0

        # Define a button and draw it
        button = tkinter.Button(root, text='Generate an error', command=self.generate_error)
        button.pack()

        # Loop forever
        root.mainloop()

    def generate_error(self):
        # The "Generic error" message is not shown
        number = 1 / 0



if __name__ == '__main__':
    try:
        # Run the Foo class
        Foo()

    except Exception as e:
        print('Generic error:', str(e))

为什么通用错误"语句不适用于按钮回调函数?

Why does the "Generic error" statement not apply to the button callback function?

以下 StackOverflow 帖子很有帮助:我应该在 tkinter 中使无声例外更响亮吗?

The following StackOverflow post was helpful: Should I make silent exceptions louder in tkinter?

基本上,我需要使用report_callback_exception.我相应地修改了代码片段:

Basically, I need to use report_callback_exception. I've modified the code snippet accordingly:

import tkinter
import tkinter.messagebox
import traceback

class Foo():
    def __init__(self):
        # Initialize a new GUI window
        tkinter.Tk.report_callback_exception = callback_error  # TkInter callbacks run in different threads, so if we want to handle generic exceptions caused in a TkInter callback, we must define a specific custom exception handler for that
        root = tkinter.Tk()

        # The error() function is triggered when the following is uncommented
        #number = 1 / 0

        # Define a button and draw it
        button = tkinter.Button(root, text='Generate an error', command=self.generate_error)
        button.pack()

        # Loop forever
        root.mainloop()

    def generate_error(self):
        # The "callback_error()" function is triggered when the button is clicked
        number = 1 / 0


def error(message, exception):
    # Build the error message
    if exception is not None:
        message += '\n\n'
        message += traceback.format_exc()

    # Also log the error to a file
    # TODO

    # Show the error to the user
    tkinter.messagebox.showerror('Error', message)

    # Exit the program immediately
    exit()


def callback_error(self, *args):
    # Build the error message
    message = 'Generic error:\n\n'
    message += traceback.format_exc()

    # Also log the error to a file
    # TODO

    # Show the error to the user
    tkinter.messagebox.showerror('Error', message)

    # Exit the program immediately
    exit()



if __name__ == '__main__':
    try:
        # Run the Foo class
        Foo()

    except Exception as e:
        error('Generic error:', e)