Crashing Python.exe with TkInter

Bryan Olson fakeaddress at nowhere.org
Sat Jul 24 06:25:41 CEST 2004


I've run into a problem with Python/TkInter crashing, with an
attempt to read illegal addresses, on Win on Win2000 and WinXP.

With some web-searching, I found that people say not to
manipulate TkInter from multiple threads, except that the call
event_generate() is safe.  I assume the call adds an event to
Tk's queue, and later the thread running mainloop will() pick up
the event.

I tried to write a general-purpose thread-runner.  In the code
below, 'thread-it' runs a given function in a new thread, saves
the result, and triggers event_generate().  When the mainloop
handles the event, it calls a second given function with the
saved result.

The code below *seems* to work.  It requires only the current
Python distribution.  To make it usually crash, un-comment the
'print' statement in add_to_listbox().  That's all.

With no prints, it works.  Adding prints in various places
causes it usually to crash with an unreadable-memory error from
the OS.  I get the same behavior on the command-line and in
Idle.  Occasionally, it prints something like:

     TclExecuteByteCode: abnormal return at pc 45: stack top 39 < entry 
stack top 59
     TclExecuteByteCode execution failure: end stack top < start stack top

which is usually also accompanied by the read addressing error.

I'm sure I've previously been able to print from multiple Python
threads.  Can anyone tell what is going on?



from Tkinter import *
import thread
import time

class Application(Frame):

     def __init__(self):
         Frame.__init__(self, None)
         self.event_name_cntr = 0
         self.pack()
         self.listbox = Listbox(self)
         self.listbox.pack(side=TOP)

     def add_to_listbox(self, msg):
         self.listbox.insert(END, msg)
         # print "Adding", msg

     def thread_it(self, finish_func, asyc_func, args=()):
         """ Create a thread that calls asyc_func(*args), then trigger
             an even with event_generate().  When the event loop handles
             the event, call finish_func with the return value of asyc_func.
         """
         event_name = "<<j7A5x0VjqZ3b_%x>>" % self.event_name_cntr
         self.event_name_cntr += 1
         result = [None]
         def _on_finish(_):
             self.unbind(event_name)
             finish_func(result[0])
         def do():
             result[0] = asyc_func(*args)
             self.event_generate(event_name)
         self.bind(event_name, _on_finish)
         thread.start_new_thread(do, ())

app = Application()

def fin(s):
     """ When the async function returns, we'll add its output
         to the list box.
     """
     app.add_to_listbox(s)

def async(n):
     """ In the threads, we'll just sleep then return a string.
     """
     time.sleep(2.0)
     return "Got %d asyncronously." % n

for i in range(5):
     #  Run a few threads
     app.thread_it(fin, async, [i])

app.mainloop()



Thanks,
-- 
--Bryan



More information about the Python-list mailing list