之后的 Tkinter 在时钟倒带中幸存下来

Posted

技术标签:

【中文标题】之后的 Tkinter 在时钟倒带中幸存下来【英文标题】:Tkinter after that survives clock rewinding 【发布时间】:2011-03-05 05:52:39 【问题描述】:

我注意到在我的 Tkinter 版本中,after() 调用无法在系统时钟倒带后继续存在。

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

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

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

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

【问题讨论】:

我找到了适用于我的程序的解决方案,但它不是通用的。我的程序有几个线程,所以其他线程之一可以做: app.event_generate(">", when='tail') 然后我根本不需要调用“after”... 您能否给出一个小代码示例来说明您在说什么。我知道你说过你有一个适合你的解决方案,但也许有更好/标准的方法来解决这个问题。 【参考方案1】:

不幸的是,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,而且在执行回调函数时也不会阻塞。

【讨论】:

【参考方案2】:

说明

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

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

after 使用系统时间来确定执行计划事件的时间。这意味着除了0之后和idle之后,都可以被系统时间的变化所颠覆。

现在请注意,after idleafter 0 不受系统时钟耦合的影响,但它们可能不是您的好替代品。


0 后

这会安排一个脚本立即执行。这对于获得尽可能紧凑的事件很有用。警告:这会将已安排的事件放在队列的前面,因此以这种方式永久重新安排自身的命令可以锁定队列。

因此使用after 0 并不是最佳选择,因为它位于事件队列的前面,这意味着不会发生任何其他事情。


空闲后

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

脚本将只运行一次,下次进入事件循环并且没有要处理的事件时。

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


Tl;dr

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

【讨论】:

以上是关于之后的 Tkinter 在时钟倒带中幸存下来的主要内容,如果未能解决你的问题,请参考以下文章

Android 地理围栏是不是能够在重启后幸存下来?

Stream.listen 在 web 上完全重启 Flutter 后幸存下来

类成员函数中的静态本地在类重新分配中幸存下来?

Python tkinter时钟的主题和夜主题

pyqt tkinter哪个好用

如何在 tkinter 中安排更新(f/e,更新时钟)?