2016-10-10 88 views
0

我最近开始测试Tkinter以制作简单的应用程序,这些应用程序可以挂接到API并为用户提供该API中的数据样本。我想要的一件事就是让这个窗口不时刷新,更新信息以便它永远不会过时。重新创建窗口小部件时,Tkinter窗口不断崩溃

但是,看起来tkinter窗口经常崩溃。没有错误或消息被返回,它只是在不稳定的时间变得没有反应。添加大量的小部件会使崩溃更快发生,但不同数量的小部件最终似乎最终发生。

为了让它自动刷新刷新间隔,我建立了一个可以调用tkinter应用程序redraw方法的线程。从我的研究中我知道tkinter不是线程安全的,但我认为这不会是一个问题,当我每30秒只有一个线程调用重绘方法需要片刻运行时。

这是我的应用程序的一个最小版本,它会在几次尝试重绘后崩溃。我的实际数据最终只有大约15个小部件和崩溃,但将数量提高到非常高的值会使崩溃更早发生并使测试更容易。

from Tkinter import * 

from threading import Thread, Event 

REFRESH_INTERVAL = 30 

class App(Frame): 

    names = ["test"] * 50 

    def __init__(self, master): 
     Frame.__init__(self, master) 

     self.frame = master 
     self.table = None 
     self.redraw() 


    def redraw(self): 
     if self.table: 
      self.table.destroy() 

     new_frame = Frame(self.frame) 
     new_frame.pack() 
     self.table = new_frame 

     for code in self.names: 
      label = Label(new_frame, text=code) 
      label.pack() 


class Refresher(Thread): 

    def __init__(self, event, app): 
     Thread.__init__(self) 
     self.stopped = event 
     self.app = app 

    def run(self): 
     while not self.stopped.wait(REFRESH_INTERVAL): 
      print("Refreshing...") 
      self.app.redraw() 


def main(): 
    root = Tk() 
    app = App(root) 
    app.pack(side=TOP, fill="both", expand=True) 

    stop_flag = Event() 
    refresher = Refresher(stop_flag, app) 
    refresher.start() 
    root.mainloop() 
    stop_flag.set() 


if __name__ == "__main__": 
    main() 
+2

tkinter与线程无法正常工作。您不应该从任何线程调用任何tkinter函数或小部件方法,而只是创建根窗口的线程。只需要一个简单的计时器就不需要线程。阅读'after'方法。 –

+0

@BryanOakley啊,'after'方法似乎正在为我工​​作,现在正在测试它。你想发布这个答案吗? – SuperBiasedMan

回答

0

事实证明,Tkinter不喜欢单独的线程发出这样的调用。相反,建议使用after方法。

This answer详细介绍它,但它实际上是延迟后调用函数的一种方法。它可以被添加到发送的重绘函数中,不断添加触发器以在延迟后重新运行该函数。

def redraw(self): 
    if self.table: 
     self.table.destroy() 

    new_frame = Frame(self.frame) 
    new_frame.pack() 
    self.table = new_frame 

    for code in self.names: 
     label = Label(new_frame, text=code) 
     label.pack() 
    self.master.after(REFRESH_INTERVAL, self.redraw)