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.
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.
You can install a global handler for exception with a except-hook(). An example can be found here.
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
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()