【问题标题】:Waiting certain amount of time with Tkinter用 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 也用于您希望将来发生的其他事情呢?

标签: python python-2.7 tkinter


【解决方案1】:

通常让 GUI 等待某些东西是一个非常糟糕的主意。这并不意味着基于事件的程序是如何工作的。或者更准确地说,GUI 已经处于永久等待状态,而您不想用自己的等待来阻止它。

话虽如此,tkinter 有办法等到某些事情发生。例如,您可以使用“等待”函数之一,例如wait_variablewait_windowwait_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://stackoverflow.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://stackoverflow.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()
    

    【讨论】:

      猜你喜欢
      • 2014-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-25
      • 2021-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多