当试图中断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相同的结果
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 doc没有解决这个问题。
是否有一种简单的方法可以获得明显的预期结果?
答案 0 :(得分:5)
time.sleep(...)
与after(...)
调用的结果不同。
time.sleep
方法运行,但在第二个方法通过之前没有显示任何内容。您可以通过在睡眠呼叫之前放置print('before')
来查看此信息。在创建窗口之前,您将看到print语句执行一秒钟。这是因为Tkinter在睡眠期间没有更新任何内容。因此,在睡眠结束之前不会发生任何事情,之后标签会立即更新。而且,在睡眠结束之前不会调用mainloop,因此Tkinter还没有在它的主循环中。您可以在睡眠呼叫之前使用self.parent.update()
强制Tkinter更新。您将看到窗口显示标签'before'
,等待一秒钟,标签变为'after'
。然而,在这一秒钟内窗口没有响应,这就是为什么和Tkinter一起使用睡眠是一个坏主意。
而不是睡眠,后来几乎总是更好的选择,因为它立即返回,因此它不会阻止进一步的执行,并安排指定的函数在指定的时间后执行。但是,在期望传递函数名称之后,却传递函数调用。这意味着在评估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的主循环。
答案 1 :(得分:0)
不起作用并不奇怪。想想Tk框架正在做什么:它正在创建你的窗口,display = Display()
,并且在那一刻线程停止。该对象未完全创建。你是否希望Tk渲染一个通过其构造函数一半的对象?
首先,尝试将任何与线程相关的代码移出构造函数。应该允许构造函数正常完成,并且线程代码应该在另一个函数中。
其次,在正常逻辑的中间使用thread.sleep是不好的做法。通常,您启动一个单独的线程,该线程可以等待并在需要时休眠。 (虽然我不知道Tk是否有特殊的做事方式。)