Starting to program in python I felt at home with its error reporting. Now that I'm programming with Tkinter instead, I'm finding that it often happens that there are errors in my program that I do not notice even if they generate an exception: I catch them (sometimes) just because I go debugging Step by Step (I use wingIDE) and e.g. at a given line I see the exception reported. But what annoys me is that the program doesn't stop, yet this happens even in blocks not inside try/error.
If what I said makes any sense, do you know about some overall approach to at least display the errors? Being in Tkinter I could create an error window, and fill it with any exception is being generated, when it happens.
See the answers to How can I make silent exceptions louder in tkinter which show how to hook a callback into tkinter.Tk.report_callback_exception
.
As @jochen-ritzel said (Should I make silent exceptions louder in tkinter?), there is tk.TK.report_callback_exception()
that you can override:
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
I prefer explicit extending Toplevel widget of Tk which represents mostly the main window of an application instead of injecting a 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()
IMHO it looks like a right way.