我注意到在我的Tkinter版本中,after()调用不能在系统时钟倒带中存活。
如果调用了 after(x,func),并且系统时钟被重新启动,则只有在时钟返回到倒带前的时间后才会调用 func + x 毫秒。
我认为这是因为Tkinter使用系统时钟而不是“time.clock”(程序运行的时间)。
我只在Windows上测试过它,也许是因为我有一个旧版本的Tkinter。 我希望我的应用程序可以在从网络上同步时钟的计算机上工作......
有没有人有一个简单的解决方案?
答案 0 :(得分: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 :(得分:1)
您怀疑,当您使用.after()
时,tkinter正在调度一个事件,发生在current_time + delay_ms
。这意味着,如果系统时间在这些事件之间更改,它将破坏计划的事件。
这是基于以下事实:tkinter只是调用tcl(tkinter与之通信的基础系统)的after
命令。 tcl docs告诉我们:
之后使用系统时间来确定何时该执行计划的事件。这意味着除了0和空闲之后,可以通过系统时间的更改来颠覆它。
现在请注意,after idle
和after 0
都不受系统时钟耦合的约束,但是对于您来说,这些可能不是很好的替代品。
这计划立即执行脚本。这对于获取最紧密的事件很有用。警告:这会将排定的事件置于队列的最前面,因此,这样以永久方式重新排定时间的命令可以锁定队列。
因此使用after 0
并不是最佳选择,因为它位于事件队列的最前面,这意味着不会发生其他事情。
这是类似的豁免,但也可能不是最好的。 [docs]
该脚本将仅在下一次进入事件循环时运行一次,并且没有要处理的事件。
因此,脚本将在下一次系统空闲时运行。您可以将after x
与after idle
一起使用,这将等到事件清除且系统处于空闲状态,然后等待x
毫秒,然后再运行该命令,但我怀疑那不是您想要的想要两者之一。
Tl; dr
对于最后一个问题,如果可以将时钟倒转怎么办? tkinter的本机:不多。除非您发现相反的情况并重置after()
事件或基于time.process_time()
(以前称为time.clock()
)编写自己的事件计划程序,否则我看不到拥有{{1} }表现不同。