Reputation: 1090
I was following this tutorial on making a clock with tkinter and the problem I'm having is with the line lbl.after(1000, time)
which makes the function wait one second and then rerun the time function.
If you start your program at 12:00:00.600, the program waits one second and then displays the new time at .600 microseconds after 12:00:01 rather than at 12:00:01 exactly. If you have the windows clock and the program running side by side, the windows clock will always be slightly faster. I'm not sure how they got it to work in their video, I copied and pasted their code and still had the same issue.
I also saw someone else's code using lbl.after(100, time)
which reruns the function every .100 microseconds rather than every second and it sort of works but it's still off a little every now and then.
In my original program that I'm adding a gui to with tkinter, I used the following function to display my time and it would be almost perfectly in sync with the windows clock.
import datetime as dt
def wait_until_new_second():
current_time = dt.datetime.now().time()
previous_time = current_time
while previous_time.second == current_time.second:
current_time = dt.datetime.now().time()
def display_clock():
while True:
wait_until_new_second()
print(dt.datetime.now().strftime('%H:%M:%S'))
display_clock()
Is it possible to have a clock running on tkinter in sync with the windows clock?
and / or
Is there a way to tell tkinter to wait using my wait_until_new_second()
function instead of waiting 1 second, something like lbl.after(wait_until_new_second, time)
?
Upvotes: 0
Views: 266
Reputation: 501
I had this exact problem: Here is my solution
import time
import tkinter as tk
trys = 0
def clicker():
global trys
timenow = time.time()
fraction = timenow % 1.0
if fraction < 0.025 :
root.after(950,clicker) #Tune here
ts = time.localtime()
milli = time.time() % 1.0
string = f'{ts.tm_year}_{ts.tm_mon:02d}_{ts.tm_mday:02d} {ts.tm_hour:02d}:{ts.tm_min:02d}:{(ts.tm_sec+milli):06.3f}'
TimeLabel.config(text = string)
TrysLabel.config(text = f'{trys}')
trys = 0
############
############
## Do your on the second stuff here
############
############
else:
root.after(10,clicker) #Tune here
trys += 1
root = tk.Tk()
TimeLabel = tk.Label(root,text = '*********')
TimeLabel.pack()
TrysLabel = tk.Label(root,text = '****')
TrysLabel.pack()
clicker()
root.mainloop()
This will keep the function clicker within 25 milliseconds of the wall clock.
You can tune the .after calls to get it a little closer.
On my machine this kept things within about 12 milliseconds. Most importantly (for my application) it did what it needed to do exactly once a second as close to the second as possible.
In my application I tweeked the .after timing so that it usually only had 1 miss then hit the time 10 milliseconds later.
The whole point here was to keep the number call backs that were not of interest to a minimum.
Upvotes: 1
Reputation: 46678
Using thread can be almost (not completely) in sync with system clock:
from datetime import datetime
import threading
import time
import tkinter as tk
root = tk.Tk()
clock = tk.StringVar()
tk.Label(root, textvariable=clock, font="Times 20 bold", fg='light green', bg="dark green").pack()
def tick():
while True:
now = datetime.now()
clock.set(now.strftime("%T"))
time.sleep(1-now.microsecond/1000000)
threading.Thread(target=tick, daemon=True).start()
root.mainloop()
Upvotes: 3
Reputation: 764
Use this code :
import sys
from tkinter import *
import time
def tick():
global time1
# get the current local time from the PC
time2 = time.strftime('%H:%M:%S')
# if time string has changed, update it
if time2 != time1:
time1 = time2
clock.config(text=time2)
# calls itself every 200 milliseconds
# to update the time display as needed
# could use >200 ms, but display gets jerky
clock.after(200, tick)
root = Tk()
time1 = ''
clock = Label(root, font=('times', 20, 'bold'), bg='red') clock.grid(row=0, column=0)
tick()
root.mainloop()
This updates every 200 ms
Upvotes: 0