More on Tk event_generate and threads

Dale Huffman dale_huffman at steris.com
Tue Jul 11 17:26:11 EDT 2006


There have been a number of posts about calling gui methods from other
threads.  Eric Brunel. has reccommended calling the gui's
.event_generate method with data passed thru a queue.  This worked
great for me until trying to write to the gui from multiple threads.
There I had problems: random typesof crashes almost all resulting in
seg faults.  I thought this info might help anyone trying to do the
sameor at least save some time debugging.

I wrote a (hopefully) simple and minimal piece of code to demonstrate
the problem included below.  My knowledge of the Tcl/Tk interface is
minimal.

Another interesting thing is it fails on some systems and not on
others.  All Linux:

Runs fine on:
	Dual Opteron Fedora5
	Linux xxxx 2.6.16-1.2096_FC5 #1 SMP Wed Apr 19 05:14:26 EDT 2006
x86_64 x86_64 x86_64 GNU/Linux

	Singe Pentium Fedora4
	uname info unavailable right now

Fails on:
	Singe Xeon SuSE 10.1
	Linux xxxx 2.6.16.13-4-smp #1 SMP Wed May 3 04:53:23 UTC 2006 i686
i686 i386 GNU/Linux

	Single Celeron 400 (Embedded BlueCat)
	Linux 2.6.7 #45 i686 i686 i386 (not copy pasted)

	Single Celeron 400
	SuSE 10.1 - not available


My solution (others have done the same) was to go back to having the
gui thread call its own event_generate method with the event string
passed in through queue and using a polling loop with after methods.

Would someone please verify that this shouldn't be done for some reason
such as thread-safety or point out what I'm doing wrong?  It seems from
some of the errors that the event data is getting overwritten.  I've
included the code, if anyone wants to see some of the errors I have
saved them.

---------------------------
from Tkinter import *
import threading
import Queue
from time import sleep
import random

class Thread_0(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)

	def run(self):
		count=0
		while True:
			count+=1
			hmi.thread_0_update(count)
			sleep(random.random()/100)

class Thread_1(threading.Thread):
	def __init__(self):
		threading.Thread.__init__(self)

	def run(self):
		count=0
		while True:
			count+=1
			hmi.thread_1_update(count)
			sleep(random.random()/100)

class HMI:
	def __init__(self):
		self.master=Tk()
		self.master.geometry('200x200+1+1')

		f=Frame(self.master)
		f.pack()

		self.l0=Label(f)
		self.l0.pack()
		self.l1=Label(f)
		self.l1.pack()

		self.q0=Queue.Queue()
		self.q1=Queue.Queue()

		self.master.bind("<<Thread_0_Label_Update>>",self.thread_0_update_e)
		self.master.bind("<<Thread_1_Label_Update>>",self.thread_1_update_e)

	def start(self):
		self.master.mainloop()
		self.master.destroy()

	#################################
	def thread_0_update(self,val):
		self.q0.put(val)
		self.master.event_generate('<<Thread_0_Label_Update>>',when='tail')

	def thread_1_update(self,val):
		self.q1.put(val)
		self.master.event_generate('<<Thread_1_Label_Update>>',when='tail')

	def thread_0_update_e(self,e):
		while self.q0.qsize():
			try:
				val=self.q0.get()
				self.l0.config(text=str(val))
			except Queue.Empty:
				pass

	def thread_1_update_e(self,e):
		while self.q1.qsize():
			try:
				val=self.q1.get()
				self.l1.config(text=str(val))
			except Queue.Empty:
				pass

##########################
if __name__=='__main__':
	hmi=HMI()
	t0=Thread_0()
	t1=Thread_1()
	t0.start()
	t1.start()
	hmi.start()

---------------------------




More information about the Python-list mailing list