flaberpengu
flaberpengu

Reputation: 11

How do I update a tkinter label continually until a button is pressed?

I'm attempting to create a simple timer application in which the user presses a start button (or uses the keybind) and the clock continually updates until the user presses a stop button (or uses the keybind), but in trying to create this I have got stuck in an infinite loop.

I've tried creating a boolean flag in which True indicates to end the updating and used a while loop to check this, however the program ends up stuck in an infinite loop. This is all in python 3.x (specifically v3.6.7). In the code provided, I reduced it as far as I could whilst maintaining the error and context of the error. I also removed the keybinds I mentioned earlier as these are not part of the issue.

import tkinter as tk
class cubeTimer():
    def __init__(self):
        ##Defines parts of timer
        self.start_time = 0.0
        self.end_time = 0.0
        self.difference = 0.0
    def get_time(self,num):
        ##Gets time since epoch
        if num == 1:
            self.start_time = time.time()
        elif num == 2:
            self.end_time = time.time()
    def get_difference(self):
        ##Finds difference bwteen start and end times
        self.difference = self.end_time - self.start_time
        return self.difference
class cubeGUI():
    def __init__(self):
        ##Instance variables for later use
        self.num = 0
        self.time_difference = 0
        self.flag = False
        ##Creates instance of timer class
        self.timer = cubeTimer()
        ##Creates GUI
        self.root = tk.Tk()
        ##Label to show the solve time
        self.time_label = tk.Label(text='-',height=5,width=10)
        self.time_label.grid(row=1,columnspan=2,sticky=tk.W)
        ##Button to start timer
        self.start_button = tk.Button(text='Start',height=5,width=10,command=self.start_process)
        self.start_button.grid(row=2,column=0)
        ##Button to end timer, initialised as disabled
        self.end_button = tk.Button(text='End',state=tk.DISABLED,height=5,width=10,command=self.end_process)
        self.end_button.grid(row=2,column=1)
    def start_process(self):
        ##Sets variable
        self.num = 1
        ##Calls timer to get time
        self.timer.get_time(self.num)
        ##Configures necessary buttons
        self.start_button.configure(state=tk.DISABLED)
        self.end_button.configure(state=tk.NORMAL)
        while self.flag == False:
            self.time_difference = self.timer.get_difference()
            self.time_label.configure(text=self.time_difference)
            time.sleep(0.1)
    def end_process(self):
        ##Sets variable
        self.num = 2
        ##Calls timer to get time
        self.timer.get_time(self.num)
        ##Updates flag
        self.flag = True
        ##Configures necessary button
        self.end_button.configure(state=tk.DISABLED)
        ##Calls time difference
        self.time_difference = self.timer.get_difference()
        ##Puts it on screen
        self.time_label.configure(text=self.time_difference)
myTimer = cubeGUI()

I expected the program to update the time label every 0.1 seconds (or any given time period), but instead the program freezes as it encounters an infinite loop and becomes stuck.

Upvotes: 1

Views: 79

Answers (1)

Novel
Novel

Reputation: 13729

You can't use a loop inside a GUI because it interferes with the GUI mainloop. Instead you have to structure your program to integrate into the mainloop. In tkinter, that's done with the after() method. Here is your code fixed (as far as I can infer what you are trying to do), including a proper class:

import tkinter as tk

class CubeTimer(tk.Frame):
    def __init__(self, master=None, **kwargs):
        super().__init__(master, **kwargs)

        self.timer = ' '
        self.time_value = tk.IntVar()

        ##Label to show the solve time
        time_label = tk.Label(self, textvariable=self.time_value,height=5,width=10)
        time_label.grid(row=1,columnspan=2,sticky=tk.W)
        ##Button to start timer
        self.start_button = tk.Button(self, text='Start',height=5,width=10,command=self.start_process)
        self.start_button.grid(row=2,column=0)
        ##Button to end timer, initialised as disabled
        self.end_button = tk.Button(self, text='End',state=tk.DISABLED,height=5,width=10,command=self.end_process)
        self.end_button.grid(row=2,column=1)

    def timer_tick(self):
        self.time_value.set(self.time_value.get() + 1)
        self.timer = self.after(1000, self.timer_tick) # schedule this method to be called again in 1,000 millisconds (1 second)

    def start_process(self):
        ##Configures necessary buttons
        self.start_button.configure(state=tk.DISABLED)
        self.end_button.configure(state=tk.NORMAL)
        self.timer_tick()

    def end_process(self):
        self.after_cancel(self.timer) # cancel the scheduled method call

        ##Configures necessary button
        self.end_button.configure(state=tk.DISABLED)
        self.start_button.configure(state=tk.NORMAL)

def main():
    root = tk.Tk()
    myTimer = CubeTimer(root)
    myTimer.pack()
    root.mainloop()

if __name__ == "__main__":
    main()

Upvotes: 1

Related Questions