Threading module question

Discussion in 'Python' started by Andrew Gregory, Jul 13, 2004.

  1. 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()
     
    Andrew Gregory, Jul 13, 2004
    #1
    1. Advertising

  2. On 13 Jul 2004 07:40:14 -0700, (Andrew Gregory)
    declaimed the following in comp.lang.python:


    > 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


    Read the manual...
    """
    start( )

    Start the thread's activity.
    This must be called at most once per thread object. It arranges for the
    object's run() method to be invoked in a separate thread of control.

    """

    Note the clause: "... once per thread object"

    > thread is already running, even though the function has been exited.
    > How do I fix this?
    >

    As the old joke finishes: "Stop doing that"

    I'd use a sequence of joining the dead thread, deleting it, and
    creating a whole new thread object.

    --
    > ============================================================== <
    > | Wulfraed Dennis Lee Bieber KD6MOG <
    > | Bestiaria Support Staff <
    > ============================================================== <
    > Home Page: <http://www.dm.net/~wulfraed/> <
    > Overflow Page: <http://wlfraed.home.netcom.com/> <
     
    Dennis Lee Bieber, Jul 13, 2004
    #2
    1. Advertising

  3. Solution: You cannot re-run a threaded function, but you can delete
    the thread
    a create a new one. I'm sure that I should have thought of that
    before, but this is the first occasion that I've tried to use use
    threads, and this was not obvious to me from the docs. I've posted the
    code in case it's useful to others.
    I know that it could be shortened, but it is a model for a much longer
    script.

    General comment: Writing Tkinter and Tix GUI applications is quite
    easy most of the time, quite often I find that I can just copy and
    paste from something that I've written before. It only becomes tricky
    when programming something that you haven't tried before. What would
    really help would be a good selection of examples (PYTHONCARD is quite
    good in this respect). I know there are some (e.g. in the Tix
    download), but more would help. Does anyone know any good sites for
    examples?

    # threaddemo.py
    #
    # Demonstrate a Tix GUI which can launch and interrupt
    # a function in a separate thread.
    #
    # A. P. Gregory, 15th July 2004.
    #
    # Python 2.3.4 (requires Tix)
    #
    import threading, Queue, Tix, time
    import tkFont

    class ClassDoRun:

    def __init__(self, CS):
    self.CS = CS
    CS.Counter.set('Press to start\ncountdown')

    def __call__(self):
    CS = self.CS
    for i in range(0,10):
    CS.Counter.set(str(10-i)+'/10') # write a value for
    display in GUI
    time.sleep(1.0)
    if CS.AbortRun:
    CS.Counter.set('Aborted\n(can re-start)')
    return
    CS.Counter.set('Bang!')


    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 15-Jul-2004")
    self.frame = Tix.Frame(root)
    self.frame.bind("<Destroy>",self.CleanUp)
    self.AbortRun=0
    self.Counter=Tix.StringVar()

    def myquit(self):
    self.root.destroy()

    def CleanUp(self, event):
    print "Cleaning up after Tkinter closed"

    class ButtonBoxWidget:
    def __init__(self, Fquit, Frun, Fabort, frame):
    butbox = Tix.ButtonBox(frame, orientation=Tix.HORIZONTAL)
    self.butbox = butbox
    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)
    def grid(self, **kwargs): self.butbox.grid(kwargs)

    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,
    font=('Sans Serif', 16, 'bold'), fg='blue', bg='white', padx=16,
    height=2, width=8)
    self.Lcounter.grid(row=0, column=0, pady=15)
    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():
    del self.thread # Cannot re-run
    function,
    # but can delete
    thread
    # and run as new.
    self.thread = threading.Thread(
    target=self.RunnableObject)
    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()
     
    Andrew Gregory, Jul 15, 2004
    #3
  4. Andrew Gregory

    Aahz Guest

    In article <>,
    Andrew Gregory <> wrote:
    >
    >Solution: You cannot re-run a threaded function, but you can delete
    >the thread a create a new one. I'm sure that I should have thought of
    >that before, but this is the first occasion that I've tried to use use
    >threads, and this was not obvious to me from the docs. I've posted the
    >code in case it's useful to others.


    Better answer: re-use the thread. Have the thread you create sitting on
    a Queue, waiting for input.
    --
    Aahz () <*> http://www.pythoncraft.com/

    Barbara Boxer speaks for me:
    http://buffaloreport.com/2004/040713.boxer.marriage.html
     
    Aahz, Jul 22, 2004
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Lee Simpson
    Replies:
    0
    Views:
    1,614
    Lee Simpson
    Jul 18, 2003
  2. Maric Michaud
    Replies:
    0
    Views:
    7,212
    Maric Michaud
    Jun 24, 2006
  3. Replies:
    9
    Views:
    1,103
    Mark Space
    Dec 29, 2007
  4. Steven Woody
    Replies:
    0
    Views:
    453
    Steven Woody
    Jan 9, 2009
  5. Steven Woody
    Replies:
    0
    Views:
    468
    Steven Woody
    Jan 9, 2009
Loading...

Share This Page