2016-10-10 14 views
0

最近、Tkinterをテストして、APIにフックしてそのAPIからデータのサンプルを得ることのできる簡単なアプリケーションを作成し始めました。私が欲しかったことの一つは、このウィンドウを時々リフレッシュして情報を更新して、あまりに古くなったことがないようにすることでした。ウィジェットを再作成するときにTkinterウィンドウがクラッシュする

しかし、tkinterウィンドウが定期的にクラッシュしているようです。エラーやメッセージは返されず、不安定な時間に応答しなくなります。多数のウィジェットを追加すると、クラッシュが早く発生しますが、ウィジェットの数が異なると、最終的に関係なく発生するようです。

間隔を置いて自動リフレッシュさせるために、tkinterアプリケーションのredrawメソッドを呼び出すスレッドを設定します。私の研究から、tkinterはスレッドセーフではないことが分かっていますが、30秒ごとに1つのスレッドしか実行しないで、実行するのに時間がかかる再描画メソッドを呼び出すと問題にはならないと考えました。

ここでは、いくつかの再描画を試みた後にクラッシュするアプリの最小限のバージョンを紹介します。私の実際のデータには約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) 
関連する問題