Skip to content Skip to sidebar Skip to footer

Tkinter After That Survives Clock Rewinding

I noticed that in my version of Tkinter, the after() call does not survive system clock rewinding. If the after(x, func) was called, and the system clock was rewinded, func will be

Solution 1:

Unfortunately, neither Tkinter nor Tcl interpreter have an straightforward solution to your problem. The after(ms, func) method is based on the Tcl command of the same name, which creates an internal timer based on the current system time plus the amount of milliseconds passed as parameter.

In case you are curious, you can check it out directly from the Tcl/Tk source code:

Tcl_GetTime(&wakeup);wakeup.sec+=(long)(ms/1000);wakeup.usec+=((long)(ms%1000))*1000;if(wakeup.usec>1000000) {
    wakeup.sec++;wakeup.usec-=1000000;
}
afterPtr->token=TclCreateAbsoluteTimerHandler(&wakeup,AfterProc,afterPtr);

Given this limitation, I would go for a pure Python approach, like using a Timer:

import time
import threading
import tkinter as tk

root = tk.Tk()

defsay_hi():
    print(time.perf_counter(), "-", "Hi after 30sec!")
    root.destroy()

print(time.perf_counter(), "-", "Waiting 30sec")
threading.Timer(30, say_hi).start()
root.mainloop()

It also has the advantage that runs on a separate thread, preventing not only blocking the GUI during the timer interval but also while executing the callback function.

Solution 2:

Explanation

As you suspected, when you use .after() tkinter is scheduling an event to happen at current_time + delay_ms. This means that if system time changes between those events, it will subvert the scheduled event.

This is based on the fact that tkinter is simply calling the after command of tcl (the underlying system that tkinter is communicating to). tcl docs tell us:

after uses the system time to determine when it is time to perform a scheduled event. This means that with the exception of after 0 and after idle, it can be subverted by changes in the system time.

Now notice that after idle is exempt from the system clock coupling as well as after 0 but those probably aren't good replacements for you.


after 0

This schedules a script for immediate execution. It's useful for getting the tightest possible event. Warning: This places the scheduled event at the front of the queue, so a command that perpeually reschedules itself in this manner can lock up the queue.

So using after 0 would not be optimal as it is in the front of the event queue meaning nothing else would happen.


after idle

This is similarly exempt but it probably won't be best either. [docs]

The script will be run exactly once, the next time the event loop is entered and there are no events to process.

So the script would run when the system next becomes idle. You could use after x with after idle and that would wait until the events are clear and the system is idle, then wait x milliseconds before running that command but I suspect that isn't what you want to do either.


Tl;dr

So to your final question, what can you do if the clock could be expected to be reversed? Native to tkinter: not much. Unless you caught the reverse happening and reset your after() event or writing your own event scheduler based on time.process_time() (formerly time.clock()), I don't see a way to have .after() perform differently.

Post a Comment for "Tkinter After That Survives Clock Rewinding"