如何轻松避免Tkinter冻结?

发布于 2021-01-29 18:05:54

我开发了一个简单的Python应用程序来做一些事情,然后决定使用Tkinter添加一个简单的GUI。

问题在于,当main函数正在执行其工作时,窗口会冻结。

我知道这是一个普遍的问题,我已经读过我应该使用多线程(非常复杂,因为该函数还会更新GUI)或将我的代码划分为不同的函数,每个函数工作一段时间。无论如何,我不想为这样一个愚蠢的应用程序更改代码。

我的问题是:有没有简便的方法可以每秒更新我的Tkinter窗口?我只想应用KISS规则!

我在下面给我一个伪代码示例,我尝试过但没有用:

    class Gui:
        [...]#costructor and other stuff

        def refresh(self):
            self.root.update()
            self.root.after(1000,self.refresh)

        def start(self):
            self.refresh()
            doingALotOfStuff()

    #outside
    GUI = Gui(Tk())
    GUI.mainloop()

它只会执行一次刷新,而我不明白为什么。

非常感谢你的帮助。

关注者
0
被浏览
50
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    Tkinter陷入困境mainloop。基本上,这意味着它会不断刷新窗口,等待单击按钮,键入单词,运行回调等。当您在打开的同一线程上运行某些代码mainloop时,在此mainloop之前,其他任何操作都不会执行代码部分完成。一个非常简单的解决方法是将一个长时间运行的进程生成到单独的线程上。这仍然可以与Tkinter进行通信并更新其GUI(大部分情况下)。

    这是一个简单的示例,不会彻底修改您的伪代码:

    import threading
    
    class Gui:
        [...]#costructor and other stuff
    
        def refresh(self):
            self.root.update()
            self.root.after(1000,self.refresh)
    
        def start(self):
            self.refresh()
            threading.Thread(target=doingALotOfStuff).start()
    
    #outside
    GUI = Gui(Tk())
    GUI.mainloop()
    

    该答案将详细介绍mainloop如何阻止代码。

    这是另一种方法,方法是在自己的线程上启动GUI,然后再运行不同的代码。

    61

    Bjorn发布的解决方案在我的计算机(RedHat Enterprise 5,python 2.6.1)上显示“ RuntimeError:从不同的公寓呼叫Tcl”消息。Bjorn可能没有得到此消息,因为据我检查过的一个地方,使用Tkinter处理线程是不可预测的且依赖于平台。

    问题似乎是可以将其app.start()视为对Tk的引用,因为app包含Tk元素。我通过替换app.start()为self.start()inside来解决此问题__init__。我也这样做了,以便所有Tk引用都在调用mainloop()的函数内,或者在调用的函数所调用的函数内mainloop()(这对于避免“不同单元”错误很关键)。

    最后,我添加了带有回调的协议处理程序,因为如果没有此处理程序,则当用户关闭Tk窗口时,程序会退出并出现错误。

    修改后的代码如下:

    # Run tkinter code in another thread
    
    import tkinter as tk
    import threading
    
    class App(threading.Thread):
    
        def __init__(self):
            threading.Thread.__init__(self)
            self.start()
    
        def callback(self):
            self.root.quit()
    
        def run(self):
            self.root = tk.Tk()
            self.root.protocol("WM_DELETE_WINDOW", self.callback)
    
            label = tk.Label(self.root, text="Hello World")
            label.pack()
    
            self.root.mainloop()
    
    
    app = App()
    print('Now we can continue running code while mainloop runs!')
    
    for i in range(100000):
        print(i)
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看