guylifestyle
guylifestyle

Reputation: 327

python running task in the background while allowing tkinter to be active

When my program executes the python GUI freezes. Here is my main code. Can I get some help in doing threading? So the execution happens in the background and I can still be able to use the "x" button in the GUI if I want to end the execution? Currently I just ask the user to close the cmd to end the program.

if __name__ == "__main__":

    root = Tk() 
    root.title('Log')
    root.geometry("400x220") 
    font1=('times', 15)
    font2=('times', 10)
    #Label inside root 
    Label(root, relief=GROOVE, font=font2, text="level").pack() 
    variable = StringVar(root)
    variable.set("INFO") # default value

    w = OptionMenu(root, variable, "CRITICAL", "DEBUG")
    w.pack()
    Button(root, font=font1, background= "yellow", text='START',command=main).pack()
    Label(root, text="To end just close the CMD window").pack()

    root.mainloop()

Upvotes: 0

Views: 4822

Answers (1)

Al.Sal
Al.Sal

Reputation: 984

UPDATE: Turns out the Button callback was autorunning launch because the function object wasn't being set as the callback, the called function itself was. The fix is to replace the callback lambda: spawnthread(fcn) so that a function object is set as the callback instead. The answer has been updated to reflect this. Sorry for missing that.


The GUI mainloop will freeze when you try to run some other function, and has no way to restart itself (because it's frozen.)

Let's say the command you'd like to run alongside the GUI mainloop is myfunction.

Imports:

import time
import threading
import Queue

You need to set up a ThreadedClient class:

class ThreadedClient(threading.Thread):
    def __init__(self, queue, fcn):
        threading.Thread.__init__(self)
        self.queue = queue
        self.fcn = fcn
    def run(self)
        time.sleep(1)
        self.queue.put(self.fcn())

def spawnthread(fcn):
    thread = ThreadedClient(queue, fcn)
    thread.start()
    periodiccall(thread)

def periodiccall(thread):
    if(thread.is_alive()):
        root.After(100, lambda: periodiccall(thread))

You then want the widget calling the function to instead call a spawnthread function:

queue = Queue.Queue()

Button(root, text='START',command=lambda: spawnthread(myfunction)).pack() #<---- HERE

N.B. I'm adapting this from a multithreaded tkinter GUI I have; I have all my frames wrapped up in classes so this might have some bugs since I've had to tweak it a bit.

Upvotes: 1

Related Questions