2016-10-10 91 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)