4

Hi I have a small python gui interface with two buttons, start(That starts a counter) and stop (that is suppose to stop the counter), the counter is an infinite loop since I do not want it to end unless the second button is clicked. The problem is the second button cannot be clicked while the function from the first one is still running. I read that I need to use threading and I have tried but I do not fully understand how I can do this. Please help.

from Tkinter import *
import threading


class Threader(threading.Thread):
    def run(self):
        for _ in range(10):
            print threading.current_thread().getName()

    def main(self):
        import itertools
        for i in itertools.count(1, 1):
            print i

    def other(self):
        print "Other"

m = Threader(name="main")
o = Threader(name="other")

try:
    '''From here on we are building the Gui'''
    root = Tk()

    '''Lets build the GUI'''
    '''We need two frames to help sort shit, a left and a right vertical frame'''
    leftFrame = Frame(root)
    leftFrame.pack(side=LEFT)
    rightFrame = Frame(root)
    rightFrame.pack(side=RIGHT)
    '''Widgets'''
    '''Buttons'''
    playButton = Button(leftFrame, text="Play", fg="blue", command=m.main)
    stopButton = Button(rightFrame, text="Stop", fg="red", command=o.other)
    playButton.pack(side=TOP)
    stopButton.pack(side=BOTTOM)

    root.mainloop()
except Exception, e:
    print e

2 Answers 2

5

Here's a short example of using threading. I took out your other function and I don't know why your using itertools here. I took that out as well and simply setup using a simple threading example.

A few things:

You setup using threading.Thread as the base class for Threader, but you never actually initialized the base class.

Whenever you use threading you generally want to define a run method and then use start() to start the thread. Calling start() will call run.

You need to use threading to prevent your GUI blocking, because tkinter is just one thread on a giant loop. So, whenever you have some long running process it blocks this thread until the current process is complete. That's why it's put in another thread. Python has something called the GIL, which prevent's true parallelization (I made up that word) since it only one thread can ever be used at a time. Instead, it uses time slicing, the GIL sort of "polls" between them to give the appearance of multiple tasks running concurrently. For true parallel processing you should use multiprocessing.

In the below code I have used self.daemon = True. Setting the thread to be a daemon will kill it when you exit the main program (In this case the Tk GUI)

from tkinter import *
import threading, time

class Threader(threading.Thread):

    def __init__(self, *args, **kwargs):

        threading.Thread.__init__(self, *args, **kwargs)
        self.daemon = True
        self.start()

    def run(self):

         while True:
            print("Look a while true loop that doesn't block the GUI!")
            print("Current Thread: %s" % self.name)
            time.sleep(1)

if __name__ == '__main__':

    root = Tk()
    leftFrame = Frame(root)
    leftFrame.pack(side=LEFT)
    rightFrame = Frame(root)
    rightFrame.pack(side=RIGHT)
    playButton = Button(leftFrame, text="Play", fg="blue", 
        command= lambda: Threader(name='Play-Thread'))
    stopButton = Button(rightFrame, text="Stop", fg="red", 
        command= lambda: Threader(name='Stop-Thread'))
    playButton.pack(side=TOP)
    stopButton.pack(side=BOTTOM)
    root.mainloop()
Sign up to request clarification or add additional context in comments.

Comments

2

For something as simple as a counter, Tkinter's after() method is usually a better choice. You can use an instance variable to set it to on and off.

class TimerTest():
    def __init__(self, root):
        self.root=root

        Button(root, text="Play", fg="blue",
                            command=self.startit).grid(row=1, column=0)
        Button(root, text="Stop", fg="red",
                            command=self.stopit).grid(row=1, column=1)

        self.is_running=True
        self.count=IntVar()
        Label(root, textvariable=self.count,
              bg="lightblue").grid(row=0, column=0, columnspan=2, sticky="ew")

    def startit(self):
        self.is_running=True
        self.increment_counter()

    def increment_counter(self):
        if self.is_running:
             c=self.count.get()
             c += 1
             self.count.set(c)
             root.after(1000, self.increment_counter)  ## every second

    def stopit(self):
        self.is_running = False

root = Tk()
TT=TimerTest(root)
root.mainloop()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.