之后Tkinter幸存时钟倒带

问题描述 投票:9回答:2

我注意到在我的Tkinter版本中,after()调用不能在系统时钟倒带中存活。

如果调用了after(x,func),并且系统时钟被重新启动,则只有在时钟返回到倒带+ x毫秒之前的时间后才会调用func。

我假设这是因为Tkinter使用系统时钟而不是“time.clock”(程序运行的时间)。

我只在Windows上测试它,也许是因为我有一个旧版本的Tkinter。我希望我的应用程序可以在与网络同步时钟的计算机上运行...

有人有一个简单的解决方案吗?

python windows tkinter
2个回答
5
投票

不幸的是,Tkinter和Tcl解释器都没有直接解决您的问题。 after(ms, func)方法基于同名的Tcl命令,它根据当前系统时间加上作为参数传递的毫秒数创建内部计时器。

如果您感到好奇,可以直接从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);

鉴于这种限制,我会选择纯Python方法,比如使用Timer

import time
import threading
import tkinter as tk

root = tk.Tk()

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

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

它还具有在单独的线程上运行的优点,不仅可以防止在定时器间隔期间阻止GUI,还可以在执行回调函数时阻止GUI。


1
投票

说明

正如您所怀疑的,当您使用.after()时,tkinter会安排在current_time + delay_ms发生的事件。这意味着如果系统时间在这些事件之间发生变化,它将颠覆预定的事件。

这是基于tkinter只是调用tcl的after命令(tkinter正在与之通信的底层系统)的事实。 tcl docs告诉我们:

在使用系统时间之后确定何时执行预定事件。这意味着除了0之后和空闲之后,它可以被系统时间的变化破坏。

现在注意after idle不受系统时钟耦合以及after 0的限制,但那些可能不适合你。


0之后

这会安排脚本立即执行。它对于获得最严重的事件非常有用。警告:这会将计划的事件放在队列的前面,因此以这种方式永久重新计划自身的命令可以锁定队列。

所以使用after 0并不是最优的,因为它位于事件队列的前面,这意味着什么都不会发生。


闲置之后

这同样是免税的,但也可能不是最好的。 [docs]

该脚本将在下次输入事件循环并且没有要处理的事件时运行一次。

因此,当系统下一个空闲时,脚本将运行。您可以将after xafter idle一起使用,并等到事件清除且系统处于空闲状态,然后在运行该命令之前等待x毫秒,但我怀疑这不是您想要做的。


Tl;博士

那么对于你的最后一个问题,如果时钟可以被推翻,你会怎么做?原生于tkinter:不多。除非你发现相反的情况并重置你的after()事件或编写你自己的事件调度程序基于time.process_time()(以前的time.clock()),我没有看到让.after()表现不同的方法。

© www.soinside.com 2019 - 2024. All rights reserved.