Search code examples
pythonexceptionuser-interfacetkinterwarnings

How can I make silent exceptions louder in tkinter?


If I run the following code from a terminal, I get a helpful error message in the terminal:

import Tkinter as tk

master = tk.Tk()

def callback():
    raise UserWarning("Exception!")

b = tk.Button(master, text="This will raise an exception", command=callback)
b.pack()

tk.mainloop()

However, if I run it without a terminal (say, by double-clicking an icon), the error message is suppressed.

In my real, more complicated Tkinter application, I like that the GUI is a little crash-resistant. I don't like that my users have a hard time giving me useful feedback to fix the resulting unexpected behavior.

How should I handle this? Is there a standard way to expose tracebacks or stderror or whatnot in a Tkinter application? I'm looking for something more elegant than putting try/except everywhere.

EDIT: Jochen Ritzel gave an excellent answer below that pops up a warning box, and mentioned attaching it to a class. Just to make this explicit:

import Tkinter as tk
import traceback, tkMessageBox

class App:
    def __init__(self, master):
        master.report_callback_exception = self.report_callback_exception
        self.frame = tk.Frame(master)
        self.frame.pack()
        b = tk.Button(
            self.frame, text="This will cause an exception",
            command=self.cause_exception)
        b.pack()

    def cause_exception(self):
        a = []
        a.a = 0 #A traceback makes this easy to catch and fix

    def report_callback_exception(self, *args):
        err = traceback.format_exception(*args)
        tkMessageBox.showerror('Exception', err)

root = tk.Tk()
app = App(root)
root.mainloop()

My remaining confusion: Jochen mentions the possibility of having different exception reporting functions in different frames. I don't yet see how to do that. Is this obvious?


Solution

  • There is report_callback_exception to do this:

    import traceback
    import tkMessageBox  # py3: import tkinter.messagebox as 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
    

    If you didn't import Tkinter as tk, then do

    Tkinter.Tk.report_callback_exception = show_error