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.
CodePudding user response:
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!