10

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.

MatthewMartin
  • 32,326
  • 33
  • 105
  • 164
alessandro
  • 3,838
  • 8
  • 40
  • 59
  • 2
    there is an elegant solution: http://stackoverflow.com/questions/4770993/silent-exceptions-in-python-tkinter-should-i-make-them-louder-how – Gonzo Jul 24 '12 at 10:26
  • Possible duplicate of [Should I make silent exceptions louder in tkinter?](https://stackoverflow.com/questions/4770993/should-i-make-silent-exceptions-louder-in-tkinter) – Stevoisiak Mar 01 '18 at 18:30

3 Answers3

9

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.

Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
  • thank you for your help, I can _more or less_ make it work. But I'm not familiar (newbie python programmer) with the @safe syntax, so I don't know exactly where to put it in my code... seems to me that I have to put @safe before each function definition that should be watched... is it so? – alessandro Jul 13 '11 at 08:22
  • @alessandro: You are correct. These are called decorators. Decorators are syntactic sugar for calling classes and functions that can wrap other functions. – Steven Rumbalski Jul 13 '11 at 13:37
  • @StevenRumbalski I intentionally left the import unchanged. I only made edits which were backwards compatible with Python 2. – Stevoisiak Mar 01 '18 at 19:28
  • @StevenRumbalski I thought print was forwards compatible without importing future? – Stevoisiak Mar 01 '18 at 19:36
  • @StevenVascellaro: Without the future import `print 3` and `print(3)` are the same, but `print 1, 2, 3` and `print(1, 2, 3)` have outputs of `1 2 3` and `(1, 2, 3)`, respectively. And doing `print(1, end='')` is a syntax error. A single argument is no problem because the parenthesis can just be ignored. Multiple arguments become a tuple because the commas. I think the named arguments are an issue because Python has already decided that `print` is a statement and so it isn't even trying to look at it like a function. – Steven Rumbalski Mar 01 '18 at 20:10
8

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
Community
  • 1
  • 1
ankostis
  • 8,579
  • 3
  • 47
  • 61
1

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.

Yurii Rabeshko
  • 591
  • 8
  • 17