Search code examples
pythonuser-interfacetkintercallbackpython-multithreading

triggering and stopping a process with tkinter buttons


(scroll down for the edited version of this question)

I expect the variable "a", made global in the "play" callback function, to update every time I press the "play" or "stop" buttons. As you can see by running the code, the callback function update its value whenever I press the buttons, but if a try to test the value of the global variable within the tkinter mainloop, I only get it updated only when the program is started.

from tkinter import *

a = 0

class gui:

    def __init__(self, window):

        # play button
        self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
        self.play_frame.grid(row=0, column=0, padx=1, pady=1)
        self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
        self.play_button.pack()
        # stop button
        self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
        self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
        self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
        self.stop_button.pack()


    def play(self, switch):
        global a
        a = switch
        print (a)

root = Tk()

if a == 1:
    print ("one")
elif a == 0:
    print ("zero")

app = gui(root)
root.mainloop()

EDIT 1:

I have written the code again in order to make the question clearer and in order to simulate what's going on in the situation I'm trying to reproduce with these simplified examples. I expect the tester function to print out "running" or "not running" according to each button I press. I'm running "tester" inside a thread because in the acutal project I'm working on "tester" is a way more complex process:

from tkinter import *
import threading
import time

a = 0

class gui:

    def __init__(self, window):

        # play button
        self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
        self.play_frame.grid(row=0, column=0, padx=1, pady=1)
        self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
        self.play_button.pack()
        # stop button
        self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
        self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
        self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
        self.stop_button.pack()


    def play(self, switch):
        global a
        a = switch
        print (a)

root = Tk()

def tester(trig):
    while True:


        if trig == 1:
            time.sleep(0.5)
            print ("running")
        elif trig == 0:
            time.sleep(0.5)
            print ("not running")
    


t1 = threading.Thread (target = tester, args = [a], daemon = True)
t1.start()

app = gui(root)
root.mainloop()

Solution

  • Passing a primitive type to a function is using pass by value, so the following line:

    t1 = threading.Thread (target = tester, args = [a], daemon = True)
    

    will pass the value of a (since a is of primitive type int) to tester which is 0.

    You can use IntVar instead for a, so that pass by reference is used:

    from tkinter import *
    import threading
    import time
    
    class gui:
        def __init__(self, window):
            # play button
            self.play_frame = Frame(master=window, relief=FLAT, borderwidth=1)
            self.play_frame.grid(row=0, column=0, padx=1, pady=1)
            self.play_button = Button(self.play_frame, text="play", fg="blue", command=lambda: self.play(1))
            self.play_button.pack()
            # stop button
            self.stop_frame = Frame(master=window, relief=FLAT, borderwidth=1)
            self.stop_frame.grid(row=0, column=2, padx=1, pady=1)
            self.stop_button = Button(self.stop_frame, text="stop", fg="red", command=lambda: self.play(0))
            self.stop_button.pack()
    
        def play(self, switch):
            a.set(switch) # update 'a'
            print(a.get())
    
    root = Tk()
    
    a = IntVar(value=0)
    
    def tester(trig):
        while True:
            value = trig.get()
            if value == 1:
                time.sleep(0.5)
                print ("running")
            elif value == 0:
                time.sleep(0.5)
                print ("not running")
    
    t1 = threading.Thread (target = tester, args = [a], daemon = True)
    t1.start()
    
    app = gui(root)
    root.mainloop()