B
Bryan Olson
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, )
app.mainloop()
Thanks,
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, )
app.mainloop()
Thanks,