Multithreaded tkinter GUI application exception handling

Eric Brunel eric_brunel at despammed.com
Thu Oct 14 04:30:31 EDT 2004


Michael Zhang wrote:
> My project uses Python-2.3.4 + Tkinter + PIL-1.1.4 to retrieve images 
> from server and display those images.
> 
> I created a thread (also a separate toplevel window) for displaying 
> images and another thread for recording the frame rates (using a 
> progress bar for visulization). The whole application worked very well 
> once it received image data from the socket.
> 
> The problem is when I tried to close that display window (click on the 
> standard "X" button on the right-upper corner), I got this exception:
> 
> --------
> Exception in thread Thread-1:
> Traceback (most recent call last):
>   File "/usr/local/lib/python2.3/threading.py", line 436, in __bootstrap
>     self.run()
>   File "realtime.py", line 188, in run
>     UI(self.master)
>   File "realtime.py", line 238, in __init__
>     self.canvas.create_image(0, 0, image=self.image, anchor=NW)
>   File "/usr/local/lib/python2.3/lib-tk/Tkinter.py", line 2060, in 
> create_image
>     return self._create('image', args, kw)
>   File "/usr/local/lib/python2.3/lib-tk/Tkinter.py", line 2049, in _create
>     return getint(self.tk.call(
> TclError: invalid command name ".1080883980.1080884908"
> ---------

Not knowing your application exactly, I can only guess what's happening: it 
seems that your secondary thread is not aware that the window is closed and it 
still tries to draw things in a canvas that has already been destroyed (module 
realtime.py, line 238, according to the traceback).

So you should either make sure your secondary thread is stopped before the 
window is closed, or enclose the canvas.create_image in a try except handling 
the TclError. This last solution is far from optimal, since any mistake in a 
Tkinter call always raises a TclError, so you may catch other errors as well. 
I'd go for the first, using for example an Event (in module threading) to 
indicate the window is closed.

> Then after I closed the main windows, I got another exception:
> 
> ---------
> Exception in thread Thread-2:
> Traceback (most recent call last):
>   File "/usr/local/lib/python2.3/threading.py", line 436, in __bootstrap
>     self.run()
>   File "realtime.py", line 198, in run
>     self.bar.updateProgress(i)
>   File "/home/michael/usyd/soft3700/py/realtime/progressbar.py", line 
> 73, in updateProgress
>     self.update()
>   File "/home/michael/usyd/soft3700/py/realtime/progressbar.py", line 
> 91, in update
>     float(value) / self.max * self.width, self.height)
>   File "/usr/local/lib/python2.3/lib-tk/Tkinter.py", line 2039, in coords
>     self.tk.splitlist(
> TclError: invalid command name 
> ".1080881420.connectionPane.1080883148.1080883340"
> ---------
> 
> Why is the "TclError"? How can I handle those exceptions and exit the 
> applicaton gracefully?

Same problem here, this time for the progress bar apparently: you try to do 
things in a widget that is already destroyed.

BTW, mixing Tkinter calls in several threads is not a good idea: Tkinter is not 
thread-safe and I experienced quite a lot of crashes when trying to do that. The 
best solution I could find was to handle all the GUI stuff in the main thread 
and make the secondary threads post events in Tkinter's event queue via the 
event_generate method to serialize things at GUI level. I'm quite surprised to 
hear that you succeeded in making things work, but be aware that you may 
experience weird crashes in the future...

> Could someone help me out about those thread stuff? Thanks!
> 
> Michael

HTH
-- 
- Eric Brunel <eric (underscore) brunel (at) despammed (dot) com> -
PragmaDev : Real Time Software Development Tools - http://www.pragmadev.com



More information about the Python-list mailing list