Reputation: 138
I am pretty new to using Tkinter and I am trying to build an app that displays a timer as one of its features. I am updating a label to display the time from a separate thread. On the display the time does not update smoothly. It freezes for short periods of time and then jumps a second or more. It there a way to keep the updates consistent so the timer is smooth?
It's a simple app so far so it doesn't seem like the CPU or main thread should be busy doing anything else. When you press a button it starts a separate thread that periodically sets the label text. I've tried sleeping between 0-0.1 seconds per update and the result is the same.
window = tk.Tk()
frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow")
frame2.pack()
time_display = tk.Label(master=frame2, text="0.0")
time_display.pack()
update_thread = None
def play_pause():
global update_thread, stop_loop
if not update_thread:
stop_loop = False
update_thread = threading.Thread(target=update_timer_loop)
update_thread.start()
else:
stop_loop = True
update_thread = None
stop_loop = False
def update_timer_loop():
global window
start = time.time()
base_time = float(time_display["text"])
while not stop_loop:
current_time = time.time() - start + base_time
window.after(0, lambda: set_text(round(current_time, 2)))
time.sleep(0.1)
def set_text(text):
time_display["text"] = text
btn_play = tk.Button(master=frame1, text="Play/Pause", command=play_pause)
btn_play.pack(side=tk.LEFT)
Upvotes: 1
Views: 159
Reputation: 13729
You don't need threading at all in this. I see you tried to use after, and that's the correct approach. The only thing extra to know is that you can use after_cancel
to cancel an upcoming event that you scheduled with after
. Try this:
import tkinter as tk
import time
window = tk.Tk()
time_display = tk.Label(window, text="0.0")
time_display.pack()
update_thread = None
def play_pause():
global update_thread, start, base_time
if update_thread is None:
start = time.time()
base_time = float(time_display["text"])
update_timer_loop() # start the loop
else:
time_display.after_cancel(update_thread)
update_thread = None
def update_timer_loop():
global update_thread
current_time = time.time() - start + base_time
time_display["text"] = round(current_time, 2)
update_thread = window.after(100, update_timer_loop)
btn_play = tk.Button(master=window, text="Play/Pause", command=play_pause)
btn_play.pack(side=tk.LEFT)
window.mainloop()
Upvotes: 4