Handling exception in python tkinter

后端 未结 4 630
刺人心
刺人心 2020-12-18 09:56

I have written an application in Python Tkinter. I recently noticed that for one of the operation, it sometimes closes (without giving any error) if that operation failed.

相关标签:
4条回答
  • 2020-12-18 10:21

    I am not very sure if I have understood you well, but this simple code gives you control over the case in which the directory could not be found:

    import os
    from Tkinter import *
    
    def copydir():
        src = "D:\\troll"
        dest = "D:\\trollo"
    
        try:
            os.rename(src, dest)
        except:
            print 'Sorry, I couldnt rename'
            # optionally: raise YourCustomException
            # or use a Tkinter popup to let the user know
    
    master = Tk()
    
    b = Button(master, text="OK", command=copydir)
    b.pack()
    
    mainloop()
    

    EDIT: Since you want a general method and Tkinter does not propagate exceptions, you have to program it. There are two ways:

    1) Hardcode it into the the functions as I did in the example above (horrible)

    2) Use a decorator to add a try-except block.

    import os
    from Tkinter import *
    
    
    class ProvideException(object):
        def __init__(self, func):
            self._func = func
    
        def __call__(self, *args):
    
            try:
                return self._func(*args)
    
            except Exception, e:
                print 'Exception was thrown', str(e)
                # Optionally raise your own exceptions, popups etc
    
    
    @ProvideException
    def copydir():
        src = "D:\\troll"
        dest = "D:\\trollo"
    
        os.rename(src, dest)
    
    master = Tk()
    
    b = Button(master, text="OK", command=copydir)
    b.pack()
    
    mainloop()
    

    EDIT: If you want to include the stack

    include traceback
    

    and in the except block:

    except Exception, e:
        print 'Exception was thrown', str(e)
        print traceback.print_stack()
    

    The solution that A.Rodas has proposed is cleaner and more complete, however, more complicated to understand.

    0 讨论(0)
  • 2020-12-18 10:26

    You can install a global handler for exception with a except-hook(). An example can be found here.

    0 讨论(0)
  • 2020-12-18 10:36

    I see you have a non-object oriented example, so I'll show two variants to solve the problem of exception-catching.

    The key is in the in the tkinter\__init__.py file. One can see that there is a documented method report_callback_exception of Tk class. Here is its description:

    report_callback_exception()

    Report callback exception on sys.stderr.

    Applications may want to override this internal function, and should when sys.stderr is None.

    So as we see it it is supposed to override this method, lets do it!

    Non-object oriented solution

    import tkinter as tk
    from tkinter.messagebox import showerror
    
    
    if __name__ == '__main__':
    
        def bad():
            raise Exception("I'm Bad!")
    
        # any name as accepted but not signature
        def report_callback_exception(self, exc, val, tb):
            showerror("Error", message=str(val))
    
        tk.Tk.report_callback_exception = report_callback_exception
        # now method is overridden
    
        app = tk.Tk()
        tk.Button(master=app, text="bad", command=bad).pack()
        app.mainloop()
    

    Object oriented solution

    import tkinter as tk
    from tkinter.messagebox import showerror
    
    
    class Bad(tk.Tk):
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # or tk.Tk.__init__(*args, **kwargs)
    
            def bad():
                raise Exception("I'm Bad!")
            tk.Button(self, text="bad", command=bad).pack()
    
        def report_callback_exception(self, exc, val, tb):
            showerror("Error", message=str(val))
    
    if __name__ == '__main__':
    
        app = Bad()
        app.mainloop()
    

    The result

    My environment:

    Python 3.5.1 |Anaconda 2.4.1 (64-bit)| (default, Dec  7 2015, 15:00:12) [MSC  
    v.1900 64 bit (AMD64)] on win32
    
    tkinter.TkVersion
    8.6
    
    tkinter.TclVersion
    8.6
    
    0 讨论(0)
  • 2020-12-18 10:40

    You can override Tkinter's CallWrapper. It is necessary to use a named import of Tkinter instead of a wildcard import in order to do so:

    import Tkinter as tk
    import traceback
    
    class Catcher: 
        def __init__(self, func, subst, widget):
            self.func = func 
            self.subst = subst
            self.widget = widget
        def __call__(self, *args):
            try:
                if self.subst:
                    args = apply(self.subst, args)
                return apply(self.func, args)
            except SystemExit, msg:
                raise SystemExit, msg
            except:
                traceback.print_exc(file=open('test.log', 'a'))
    
    # ...
    tk.CallWrapper = Catcher
    b = tk.Button(master, text="OK", command=copydir)
    b.pack()
    master.mainloop()
    
    0 讨论(0)
提交回复
热议问题