用 Tkinter 等待一定的时间
Posted
技术标签:
【中文标题】用 Tkinter 等待一定的时间【英文标题】:Waiting certain amount of time with Tkinter 【发布时间】:2019-01-16 18:51:09 【问题描述】:我有一个 Tkinter 程序,我想暂停 3 秒。
time.sleep
不起作用,after
方法并不能完全按照我的意愿行事。
这是一个示例代码:
from Tkinter import *
def waithere():
print "waiting..."
root = Tk()
print "1"
root.after(3000,waithere)
print "2"
root.mainloop()
输出:
1
2
*3 seconds*
waiting...
我想要的输出:
1
waiting...
*3 seconds*
2
谢谢。
【问题讨论】:
暂停有什么意义?这真的是您需要做的,还是您只是认为暂停与您真正想做的事情相似?换句话说,您真的希望 GUI 暂停,还是只想在三秒钟内发生一些事情?您的小示例程序根本不是 tkinter 设计的工作方式。 @BryanOakley 我希望程序在等待 3 秒后执行一些操作。 你没有真正回答这个问题。为什么需要等待?您已经知道如何使用after
,那么为什么不将after
也用于您希望将来发生的其他事情呢?
【参考方案1】:
通常让 GUI 等待某些东西是一个非常糟糕的主意。这并不意味着基于事件的程序是如何工作的。或者更准确地说,GUI 已经处于永久等待状态,而您不想用自己的等待来阻止它。
话虽如此,tkinter 有办法等到某些事情发生。例如,您可以使用“等待”函数之一,例如wait_variable、wait_window 或wait_visibility。
假设您希望waithere
进行等待,您可以使用wait_variable 进行等待,并在给定时间后使用after
设置变量。
这是基于您的原始代码的解决方案:
from Tkinter import *
def waithere():
var = IntVar()
root.after(3000, var.set, 1)
print("waiting...")
root.wait_variable(var)
root = Tk()
print "1"
waithere()
print "2"
root.mainloop()
使用这些方法的好处是您的代码在等待时仍然能够响应事件。
【讨论】:
【参考方案2】:仅供参考,请勿在Tkinter
中使用长循环或无限循环;它们将阻止 UI 响应用户事件(AKA 冻结)。我被教导的方法是使用after()
函数定期更新字段。
after()
函数创建一个警报回调,这意味着在调用时(使用正确的参数)它将对目标方法的调用进行排队(在下面的示例中 def update(self)
与我们输入的延迟。您可以在退出循环的类。在__init__
上创建,然后当设置为False
时不再调用after()
。
这是一个创建继承 Tkinter.Frame 的类以继承功能的示例。
try:
import tkinter as tk
except:
import Tkinter as tk
import datetime
class DelayedUpdateUI(tk.Frame):
def __init__(self, master=None, **kw):
# Create widgets, if any.
tk.Frame.__init__(self, master=master, **kw)
self.timeStr = tk.StringVar()
self.lblTime = tk.Label(self, textvariable=self.timeStr)
self.lblTime.grid()
# Call update to begin our recursive loop.
self.update()
def update(self):
self.timeStr.set(datetime.datetime.now())
# We use after( milliseconds, method_target ) to call our update
# method again after our entered delay. :)
self.after(1000, self.update)
if __name__ == '__main__':
root = tk.Tk()
DelayedUpdateUI(root).grid()
root.mainloop()
【讨论】:
这并不能回答我的问题 它不会直接回答您的问题,但会为您提供所需的信息,让您可以做自己想做的事。有什么令人困惑的吗?我非常愿意回答问题。 是的,这很令人困惑。如果你能告诉我如何制作一个程序以这种方式输出1 waiting... *3 seconds* 2
,我会很高兴。
小心你的术语。 tkinter 对 after
所做的不是任何意义上的“线程”。 Tkinter 是单线程的,即使使用 after
。【参考方案3】:
基于Bryan's answer的建议:
我从基于事件的角度理解推荐的方式,但对我来说感觉不是很直观。每次需要时,我都必须查找技巧。因此,我创建了一个小的 mixin 类,使使用更加直观:
import tkinter as tk
class TkWaitMixin:
"""Simple wait timer that makes Tk waiting functionality
more intiutive. Applies the recommended way according to
https://***.com/a/51770561/12646289.
"""
def start_wait_timer(self, milliseconds):
self.resume = tk.BooleanVar(value=False)
self.master.after(milliseconds, self.resume.set, True)
# Assume master attribute is available:
# https://***.com/a/53595036/12646289
def wait_on_timer(self):
self.master.wait_variable(self.resume)
示例用法:
import tkinter as tk
class MyWindow(tk.Tk, TkWaitMixin):
def __init__(self, master):
self.master = master
self.message_label = tk.Label('')
self.message_label.pack(padx=50, pady=50)
def show_message(self, message, milliseconds):
self.start_wait_timer(milliseconds)
self.message_label['text'] = message
self.wait_on_timer()
self.message_label['text'] = ''
root = tk.Tk()
mywin = MyWindow(master=root)
mywin.show_message('Hello world', 2000)
root.mainloop()
显然,这仅在您在 tkinter 代码中使用类时才有用。另请注意,master
属性应该在添加了 mixin 的主类中可用。
编辑
或者,使用上下文管理器可以使使用变得更加容易:
import tkinter as tk
class TkWait:
def __init__(self, master, milliseconds):
self.duration = milliseconds
self.master = master
def __enter__(self):
self.resume = tk.BooleanVar(value=False)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.master.after(self.duration, self.resume.set, True)
self.master.wait_variable(self.resume)
请注意,退出上下文管理器时开始等待。
示例用法:
import tkinter as tk
class MyWindow(tk.Tk):
def __init__(self, master):
self.master = master
self.message_label = tk.Label('')
self.message_label.pack(padx=50, pady=50)
def show_message(self, message, milliseconds):
with TkWait(self.master, milliseconds):
self.message_label['text'] = message
self.message_label['text'] = ''
root = tk.Tk()
mywin = MyWindow(master=root)
mywin.show_message('Hello world', 2000)
root.mainloop()
【讨论】:
以上是关于用 Tkinter 等待一定的时间的主要内容,如果未能解决你的问题,请参考以下文章