Andrew Stone
Andrew Stone

Reputation: 1090

Tkinter clock not in sync with Windows clock

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

Answers (3)

Burtski
Burtski

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

acw1668
acw1668

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

Yash Makan
Yash Makan

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

Related Questions