简体   繁体   中英

How to update multiple labels with tkinter

I am trying to make a stopwatch in python that also shows the elapsed time in decimal hours as worktime , and also converts whatever time elapsed over 8 hrs into overtime .

I got the stopwatch part functional (to be fair I mostly mimicked tutorials), but when I tried to add the additional decimal, then things got wrong. I tried a bunch of stuff that somewhat looked logical but got either more errors at runtime, or my labels aside the stopwatch stayed at zero, or in the present case python crashes after a few seconds (my guess is that the three .after are basically calling running update() exponentially?)

I suppose the bulldozer solution would be for me to just make duplicates of the update() function for each of my labels, I'm 9/10 sure it will work, but that just sounds stupidly redundant.

import tkinter as tk

### Vars
counting = False
hours, minutes, seconds = 0, 0, 0
totalWorktime, overtime = 0, 0

### Funcs

def start():
    global counting
    if not counting:
        update()
        counting = True

def pause():
    global counting
    if counting:
        stopwatch_label.after_cancel(update_time)
        worktime_label.after_cancel(update_worktime)
        overtime_label.after_cancel(update_overtime)
        counting = False

def reset():
    global counting
    if counting:
        stopwatch_label.after_cancel(update_time)
        worktime_label.after_cancel(update_worktime)
        overtime_label.after_cancel(update_overtime)
        counting = False
    global hours, minutes, seconds, totalWorktime, overtime
    hours, minutes, seconds = 0, 0, 0
    totalWorktime, overtime = 0, 0
    stopwatch_label.config(text='00:00:00')

# update stopwatch label
def update():
    global hours, minutes, seconds, totalWorktime, overtime
    # time counting system
    seconds += 1
    if seconds ==60:
        minutes += 1
        seconds = 0
    if minutes == 60:
        hours += 1
        minutes = 0
    totalWorktime = hours + (minutes / 60)
    if totalWorktime > 8:
        overtime = totalWorktime - 8
    # format with zero padding
    hours_string = f'{hours}' if hours > 9 else f'0{hours}'
    minutes_string = f'{minutes}' if minutes > 9 else f'0{minutes}'
    seconds_string = f'{seconds}' if seconds > 9 else f'0{seconds}'
    # building label
    stopwatch_label.config(text=hours_string + ':' + minutes_string + ':' + seconds_string)
    global update_time, update_worktime, update_overtime
    update_time = stopwatch_label.after(10, update)
    update_worktime = worktime_label.after(10, update)
    update_overtime = overtime_label.after(10, update)

### UI
# main window
root = tk.Tk()
root.geometry('512x256')
root.title('Worktime Stopwatch')

# time display
stopwatch_label = tk.Label(text='00:00:00', font=('Arial', 80))
stopwatch_label.pack()
worktime_label = tk.Label(text='Work time : ' + str(totalWorktime), font=('Arial', 20))
worktime_label.pack()
overtime_label = tk.Label(text='Overtime : ' + str(overtime), font=('Arial', 20))
overtime_label.pack()

# buttons
start_button = tk.Button(text='Start', height=4, width=8, font=('Arial', 20), command=start)
start_button.pack(side=tk.LEFT)
pause_button = tk.Button(text='Pause', height=4, width=8, font=('Arial', 20), command=pause)
pause_button.pack(side=tk.LEFT)
reset_button = tk.Button(text='Reset', height=4, width=8, font=('Arial', 20), command=reset)
reset_button.pack(side=tk.LEFT)

# run
root.mainloop()

PS: Yes I know I could have used the time module, I just like simple funny math in my scripts.
PS2: Yes I know my current stopwatch counts time 100 times too fast, that's to make it easy to test. I will get back to 1000ms when it works.

Thanks to @acw1668's point out, I looked again at my code with a fresh mind and noticed how I misunderstood the usage of .after() and was expecting it was responsible for refreshing the labels, while all it really does is calling update() at a fixed interval.

The important part was just two lines above, under my "building label" comment:

stopwatch_label.config(text=hours_string + ':' + minutes_string + ':' + seconds_string)

For some reason, my brain though this just combined the different parts of the stopwatch label into one. But the core usage of .config() is precisely to change the text.

So all i had to do was to duplicate this line twice, and change it to impact my worktime and overtime counters:

worktime_label.config(text='Work time : ' + str(totalWorktime))
overtime_label.config(text='  Overtime : ' + str(overtime))

I also had to remove their respective .after_cancel() methods in reset() and pause() , and after some more cosmetic changes, here's the whole functional code:

import tkinter as tk

### Vars
counting = False
hours, minutes, seconds = 0, 0, 0
totalWorktime, overtime = 0, 0

### Funcs

def start():
    global counting
    if not counting:
        update()
        counting = True

def pause():
    global counting
    if counting:
        stopwatch_label.after_cancel(update_time)
        counting = False

def reset():
    global counting
    if counting:
        stopwatch_label.after_cancel(update_time)
        counting = False
    global hours, minutes, seconds, totalWorktime, overtime
    hours, minutes, seconds = 0, 0, 0
    totalWorktime, overtime = 0, 0
    stopwatch_label.config(text='00:00:00')
    worktime_label.config(text='Work time : 0')
    overtime_label.config(text='  Overtime : 0')

# update stopwatch label
def update():
    global hours, minutes, seconds, totalWorktime, overtime
    # time counting system
    seconds += 1
    if seconds ==60:
        minutes += 1
        seconds = 0
    if minutes == 60:
        hours += 1
        minutes = 0
    totalWorktime = round(hours + (minutes / 60), 2)
    if totalWorktime > 8:
        overtime = totalWorktime - 8
    # format with zero padding
    hours_string = f'{hours}' if hours > 9 else f'0{hours}'
    minutes_string = f'{minutes}' if minutes > 9 else f'0{minutes}'
    seconds_string = f'{seconds}' if seconds > 9 else f'0{seconds}'
    # refresh labels with new text content
    stopwatch_label.config(text=hours_string + ':' + minutes_string + ':' + seconds_string)
    worktime_label.config(text='Work time : ' + str(totalWorktime))
    overtime_label.config(text='  Overtime : ' + str(overtime))
    global update_time, update_worktime, update_overtime
    update_time = stopwatch_label.after(1000, update)

### UI
# main window
root = tk.Tk()
root.geometry('512x256')
root.title('Worktime Stopwatch')

# time display
stopwatch_label = tk.Label(text='00:00:00', font=('Arial', 80))
stopwatch_label.pack()
worktime_label = tk.Label(text='Work time : 0', font=('Arial', 20), anchor="w", width=20)
worktime_label.pack()
overtime_label = tk.Label(text='  Overtime : 0', font=('Arial', 20), anchor="w", width=20)
overtime_label.pack()

# buttons
start_button = tk.Button(text='Start', height=4, width=8, font=('Arial', 20), command=start)
start_button.pack(side=tk.LEFT)
pause_button = tk.Button(text='Pause', height=4, width=8, font=('Arial', 20), command=pause)
pause_button.pack(side=tk.LEFT)
reset_button = tk.Button(text='Reset', height=4, width=8, font=('Arial', 20), command=reset)
reset_button.pack(side=tk.LEFT)

# run
root.mainloop()

Thanks for the help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM