Blavejr21 Blavejr21 - 6 months ago 557
Python Question

Python, Tkinter: how can I prevent tkinter gui mainloop crash using threading

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

Answer

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()