【问题标题】:Tkinter button freezingTkinter 按钮冻结
【发布时间】:2021-06-24 10:28:37
【问题描述】:

假设你有这个程序:

import os
from tkinter import *
from time import sleep

def wait():
    print('waiting 5 seconds...')
    sleep(5)

def close():
    root.destroy()


root = Tk()
b = Button(text='click', command=wait)
close = Button(text='close', command=close)
b.pack()
close.pack()
mainloop()

单击“单击”按钮后,按钮冻结 5 秒钟,然后恢复正常。我怎么做才不会冻僵? (我不想删除按钮动画,我只想让它做按钮动画,然后等待 5 秒)另外,当我单击“单击”时,我无法单击“关闭”。我该如何解决这个问题?

【问题讨论】:

  • sleep 与它所宣传的完全一样——它使整个应用程序进入睡眠状态。

标签: python tkinter


【解决方案1】:

Tkinter 一开始可能会有点混乱。大多数人认为 Tkinter 运行在与他们的代码不同的线程上,但实际上并非如此。这意味着当您使用 time.sleep 时,您的程序会冻结这段时间,其中包括 Tkinter。如果您要达到的目标是 Wait5Seconds();做一点事();我建议执行以下操作:

import time

def wait():
    print('waiting 5 seconds...')
    stop = time.time() + 5
    while time.time() <= stop:
      time.sleep(.001)
    DoSomething()

这也不是完美的解决方案,因为它仍然涉及睡眠,但它更好。您可以将其替换为pass,但我倾向于将 time.sleep 放入专门为等待而设计的 while 循环中的原因是,如果您只有一个 while x: pass,它将提升您的 CPU 核心,这可能会导致显着滞后低核心数的计算机。这里的想法只是在那里放置一些占用一些时间的东西,这样你的 CPU 就不会发疯。我没有测试过自己但可以工作并且不会导致 Tkinter 延迟的一个可能的替代方法是print('\r'),它只是将光标设置回行首。如果您还没有换行,这将覆盖那里的内容,因此请注意。但这可能会更好,因为输出到屏幕实际上在处理能力方面占用了相当多的时间,但不会阻止 Tkinter。要对此进行测试(假设您在 Windows 上),您可以按 LCtrl+LShift+Esc 打开任务管理器,按更多详细信息(如有必要)然后转到性能选项卡。确保选中 CPU,然后右键单击图表并选择 Change Graph To > Logical Processors,这将分别显示每个内核的利用率。默认情况下,python 只会在一个内核中运行,尽管它可以随机切换它正在使用的内核。

【讨论】:

    【解决方案2】:

    time.sleep() 暂停整个应用程序(包括您的 tkinter 窗口),这意味着它会冻结。但是,您可以使用 root.after() 函数。一个工作示例:

    import tkinter
    
    class MyApp:
        def __init__(self, parent):
            self.root = parent
            self.root.geometry("400x400")
            self.frame = tkinter.Frame(parent)
            self.frame.pack()
            self.root.after(3000, self.delayed)
    
        def delayed(self):
            print('I was delayed')
    
    if __name__ == "__main__":
        root = tkinter.Tk()
        app = MyApp(root)
        root.mainloop()
    

    使用root.after 不会在等待期间冻结您的应用程序。 注意:您必须以毫秒为单位指定等待时间(1 秒 = 1000 毫秒)

    希望能帮到你!

    【讨论】:

      【解决方案3】:

      由于time.sleep() 是一个阻塞函数,在函数调用完成之前不会运行任何代码。为避免这种情况发生,您可以使用threading 创建一个等待五秒钟但允许其他代码运行的线程。这称为多处理,您可以阅读更多关于多处理的信息here

      import threading
      import time
      import tkinter
      import os
      
      def wait():
          print('waiting 5 seconds...')
          threading.Thread(target=time.sleep, args=(5,)).start()
      
      def close():
          root.destroy()
      
      
      root = tkinter.Tk()
      b = tkinter.Button(text='click', command=wait)
      close = tkinter.Button(text='close', command=close)
      b.pack()
      close.pack()
      root.mainloop()
      

      【讨论】:

      • 技术上这个例子是正确的,但是对于那个线程目标,它会立即跳到下一行而不是等待。您需要它跳转到另一个具有 time.sleep 的函数,并将该函数的其余部分迁移到 Threading 目标。
      • @tayler6000 然后我想我误解了这个问题。我解释为禁用按钮 5 秒,但我想我错了。
      猜你喜欢
      • 2021-07-04
      • 2018-06-11
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      • 2014-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多