【问题标题】:python time.sleep() delays previous commandspython time.sleep() 延迟之前的命令
【发布时间】:2015-03-25 18:20:52
【问题描述】:

当试图中断一个 tkinter 应用程序时,似乎 time.sleep() 会暂停一些先前的命令。根据我的理解和以往的经验,label1的文字应该设置为“之前”一秒钟,然后改为“之后”。但是,“before”值永远不会出现,“after”会在执行一秒后正常打印。

from tkinter import *
import time
class Display(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        label1 = Label(text = "before")
        label1.grid()
        self.after(1000, label1.config(text = "after"))

def main():
    root = Tk()
    Display(root)
    root.mainloop()

if __name__ == '__main__':
    main()

请注意,使用 time.sleep(...) 产生与 tkinter after(...) 相同的结果

from tkinter import *
import time
class Display(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        label1 = Label(text = "before")
        label1.grid()
        time.sleep(1)
        label1.config(text = "after")

def main():
    root = Tk()
    Display(root)
    root.mainloop()

if __name__ == '__main__':
    main()

我想 tkinter 正在等待执行图形工作的东西(控制台没有问题),但我看不到什么,并且 tkinter 文档没有解决这个问题。
有没有一种简单的方法可以获得明显的预期结果?

【问题讨论】:

  • 您的代码中没有任何地方调用 Thread.sleep 或任何其他线程代码。
  • Oups.. 我修复了词汇表。我的印象是这两个概念是一回事。

标签: python time tkinter sleep


【解决方案1】:

time.sleep(...) 给出的结果与after(...) 调用不同。

time.sleep 方法运行,但在第二个过去之前不显示任何内容。你可以通过在你的睡眠电话之前输入print('before') 来看到这一点。您将看到在创建窗口前一秒执行了打印语句。这是由于 Tkinter 在睡眠期间没有更新任何内容。所以在睡眠结束之前什么都不会发生,之后标签会立即更新。此外,在睡眠结束之前不会调用主循环,因此 Tkinter 还没有进入它的主循环。您可以在睡眠呼叫之前使用self.parent.update() 强制 Tkinter 更新。您将看到带有标签'before' 的窗口显示,等待一秒钟后标签变为'after'。但是,在这一秒内,窗口没有响应,这就是为什么将 sleep 与 Tkinter 一起使用是一个坏主意。

与睡眠相比,after 几乎总是更好的选择,因为它立即返回,因此它不会阻止进一步的执行,并安排指定的函数在指定的时间后执行。但是, after 期望传递一个函数名,但是您传递了一个函数调用。这意味着在评估后调用时,函数运行并且返回值(None)作为函数名传递。因此,您会看到在窗口打开时标签已更改,因为更改是在评估后调用时进行的。您应该做的是将函数名称传递给 after:

def update_label:
    label1.config(text = "after")

self.after(1000, update_label)

或者更短,通过创建一个匿名函数:

self.after(1000, lambda: label1.config(text = "after"))

这将为您提供显示带有 'before' 的标签的预期结果,该标签在一秒钟后更改为 'after',而不会阻塞 Tkinter 的主循环。

【讨论】:

    【解决方案2】:

    不起作用也就不足为奇了。想想 Tk 框架在做什么:它正在创建你的窗口,display = Display(),而在那一刻线程停止了。对象未完全创建。您是否希望 Tk 渲染一个已完成其构造函数的对象?

    首先,尝试将任何与线程相关的代码移出构造函数。应该允许构造函数正常完成,线程代码应该在另一个函数中。

    其次,在正常逻辑的中间使用 thread.sleep 是不好的做法。通常,您启动一​​个单独的线程,并且该线程可以等待和休眠,如果它愿意。 (虽然我不知道Tk是否有特殊的做事方式。)

    【讨论】:

    • 您对构造函数问题是正确的,但我知道这不是问题的原因。我这样说是为了压缩代码,但是在 Display 类中的任何方法中都会出现同样的问题。
    • 明白了。自从我用 Tk 做任何事情以来已经有好几年了,但请记住,一些(全部?)GUI 工具包不会在您设置变量/调用函数后立即改变事情。他们有一个更新循环,就像一个游戏。你做一些逻辑,你做更多的逻辑,然后 UI 重绘,合并最新的数据。因此,无论您以何种方式设置这些新数据,都不需要干扰更新。一种方法是让调用逻辑 100% 发生在辅助线程中。但是 tkinter 线程安全吗?
    猜你喜欢
    • 1970-01-01
    • 2018-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    相关资源
    最近更新 更多