A
Andrew Gregory
I've written a Tkinter application (Windows) that uses the threading
module, which I've reduced to the test case below (apologies if it's a
bit long). Using the Queue module to pass messages the callable
function in ClassDoRun can be started and interrupted via 'Start' and
'Abort' buttons. Having started then interrupted the function I
expected to be able to execute the function again from the beginning
using the 'Start' button - but I get an error message saying that the
thread is already running, even though the function has been exited.
How do I fix this?
Andrew.
# threaddemo.py
#
# Python 2.3.4 (requires Tix)
#
import threading, Queue, Tix, time
# In the full program the function below
# controls various instruments and collects data.
# A much-shortened version is given here.
class ClassDoRun:
def __init__(self, CS):
self.CS = CS
def __call__(self):
CS = self.CS
for i in range(10):
CS.Counter.set(i) # write a value for display in GUI
time.sleep(1.0)
if CS.AbortRun: break
class CommonStuff: # to provide two-way common access to variables
# and functions by GUI and 'run' threads
def __init__(self, root):
self.root=root
self.root.title("threaddemo.py, vs 12-Jul-2004")
self.frame = Tix.Frame(root)
self.frame.bind("<Destroy>",self.CleanUp)
self.AbortRun=0
self.Counter=Tix.IntVar()
def myquit(self):
self.root.destroy()
def CleanUp(self, event):
print "Cleaning up after Tkinter closed"
def ButtonBoxWidget(Fquit, Frun, Fabort, frame):
butbox = Tix.ButtonBox(frame, orientation=Tix.HORIZONTAL)
butbox.add('start', text='Start', underline=0, width=6,
command=Frun)
butbox.add('abort', text='Abort', underline=0, width=6,
command=Fabort)
butbox.add('quit', text='Quit', underline=0, width=6,
command=Fquit)
return butbox
class MainWidget(CommonStuff, Tix.TixWidget):
def __init__(self, CS, Qput):
self.CS = CS
self.Qput = Qput
self.Lcounter = Tix.Label(CS.frame, textvariable=CS.Counter)
self.Lcounter.grid(row=0, column=0, pady=5)
self.butbox = ButtonBoxWidget(CS.myquit, self.SendRunMessage,
self.SendAbortMessage, CS.frame)
self.butbox.grid(row=1, column=0)
def SendRunMessage(self):
self.Qput('run')
def SendAbortMessage(self):
self.Qput('abort')
class Application:
def __init__(self, CS):
self.CS = CS
self.Q = Queue.Queue() # Pass messages to separate run
# thread via the queue Q
self.displayedwidget=MainWidget(CS, self.Q.put)
CS.frame.pack()
self.RunnableObject = ClassDoRun(CS)
self.thread = threading.Thread(target=self.RunnableObject)
self.poll()
def MessageQueue(self): # messages e.g. Run, Abort
self.CS.root.update()
while self.Q.qsize():
try:
msg = self.Q.get(0)
if msg=="abort": self.CS.AbortRun = 1
if msg=="run":
self.CS.AbortRun=0
if not self.thread.isAlive(): self.thread.start()
except Queue.Empty: pass
def poll(self):
#print self.thread.isAlive()
self.MessageQueue()
self.CS.root.after(100, self.poll)
if __name__ == '__main__':
root = Tix.Tk()
CS = CommonStuff(root)
mainWin = Application(CS)
root.mainloop()
module, which I've reduced to the test case below (apologies if it's a
bit long). Using the Queue module to pass messages the callable
function in ClassDoRun can be started and interrupted via 'Start' and
'Abort' buttons. Having started then interrupted the function I
expected to be able to execute the function again from the beginning
using the 'Start' button - but I get an error message saying that the
thread is already running, even though the function has been exited.
How do I fix this?
Andrew.
# threaddemo.py
#
# Python 2.3.4 (requires Tix)
#
import threading, Queue, Tix, time
# In the full program the function below
# controls various instruments and collects data.
# A much-shortened version is given here.
class ClassDoRun:
def __init__(self, CS):
self.CS = CS
def __call__(self):
CS = self.CS
for i in range(10):
CS.Counter.set(i) # write a value for display in GUI
time.sleep(1.0)
if CS.AbortRun: break
class CommonStuff: # to provide two-way common access to variables
# and functions by GUI and 'run' threads
def __init__(self, root):
self.root=root
self.root.title("threaddemo.py, vs 12-Jul-2004")
self.frame = Tix.Frame(root)
self.frame.bind("<Destroy>",self.CleanUp)
self.AbortRun=0
self.Counter=Tix.IntVar()
def myquit(self):
self.root.destroy()
def CleanUp(self, event):
print "Cleaning up after Tkinter closed"
def ButtonBoxWidget(Fquit, Frun, Fabort, frame):
butbox = Tix.ButtonBox(frame, orientation=Tix.HORIZONTAL)
butbox.add('start', text='Start', underline=0, width=6,
command=Frun)
butbox.add('abort', text='Abort', underline=0, width=6,
command=Fabort)
butbox.add('quit', text='Quit', underline=0, width=6,
command=Fquit)
return butbox
class MainWidget(CommonStuff, Tix.TixWidget):
def __init__(self, CS, Qput):
self.CS = CS
self.Qput = Qput
self.Lcounter = Tix.Label(CS.frame, textvariable=CS.Counter)
self.Lcounter.grid(row=0, column=0, pady=5)
self.butbox = ButtonBoxWidget(CS.myquit, self.SendRunMessage,
self.SendAbortMessage, CS.frame)
self.butbox.grid(row=1, column=0)
def SendRunMessage(self):
self.Qput('run')
def SendAbortMessage(self):
self.Qput('abort')
class Application:
def __init__(self, CS):
self.CS = CS
self.Q = Queue.Queue() # Pass messages to separate run
# thread via the queue Q
self.displayedwidget=MainWidget(CS, self.Q.put)
CS.frame.pack()
self.RunnableObject = ClassDoRun(CS)
self.thread = threading.Thread(target=self.RunnableObject)
self.poll()
def MessageQueue(self): # messages e.g. Run, Abort
self.CS.root.update()
while self.Q.qsize():
try:
msg = self.Q.get(0)
if msg=="abort": self.CS.AbortRun = 1
if msg=="run":
self.CS.AbortRun=0
if not self.thread.isAlive(): self.thread.start()
except Queue.Empty: pass
def poll(self):
#print self.thread.isAlive()
self.MessageQueue()
self.CS.root.after(100, self.poll)
if __name__ == '__main__':
root = Tix.Tk()
CS = CommonStuff(root)
mainWin = Application(CS)
root.mainloop()