2017-06-15 3 views
0

(私は新しい情報を見つけた後、古い説明を削除しましたが、これはもう必要ないと思います)。Tkinterコールバックでスレッドが開始され、インターフェイス全体がブロックされます

私は実際には、実行、停止、ステップの3つのボタンを持つTkinterインターフェイスを持っています。

[実行]ボタンは、さまざまな機能を実行するスレッドを開始しますが、実行を続行するには[ステップ]ボタンに入力が必要です。これは、ステップボタンを押すと設定されたPythonイベントを待つことによって行われます。

しかし、私のイベントを待っている間にtime.sleepがあるにもかかわらず、私のコードはTkinterインターフェイスに戻らず、プログラム全体がブロックされます。待機状態を解除すると、スレッドは停止するまで実行され、その後、私は自分のインターフェースにアクセスできます。

私は、コールバック関数RunButton()は、スレッドが終了してインターフェイスがロックされるまで返されないと考えています。この問題を解決する方法はありますか?ここで

は私の現在のコードです:

import sys 
import signal 
import inspect 
import threading 
import multiprocessing 
import Queue 
import time 
import Tkinter 

class tk_interface(Tkinter.Tk): 
    def __init__(self,parent): 
     Tkinter.Tk.__init__(self,parent) 
     self.parent = parent 
     self.initialize() 

    def initialize(self): 
     self.grid() 

     self.button1 = Tkinter.Button(self,text=u"Run",command=self.RunButton) 
     self.button1.grid(column=0,row=0) 

     self.button2 = Tkinter.Button(self,text=u"Stop",command=self.StopButton) 
     self.button2.grid(column=1,row=0) 

     self.button3 = Tkinter.Button(self,text=u"Step",command=self.StepButton) 
     self.button3.grid(column=2,row=0) 

     self.cmd_box = Tkinter.Text(self, wrap='word', height=20) 
     self.cmd_box.grid(column=0,row=1,columnspan=3) 

     self.resizable(False,False) 
     self.update() 
     self.ReadReportQueue() 

    def RunButton(self): 
     thr1.run() 

    def StopButton(self): 
     self.destroy() 

    def StepButton(self): 
     cmd_step.set() 

    def ReadReportQueue(self): 
     sys.stdout=sys.__stdout__ 
     if not reportQueue.empty(): 
      catch_message(self.cmd_box,reportQueue) 
     self.after(200, self.ReadReportQueue) 


class debug_thread(threading.Thread): 
    def __init__(self, name): 
     threading.Thread.__init__(self) 
     self.name = name 

    def trace_calls(self, frame, event, arg): 
     if event != 'call': 
      return 

     co = frame.f_code 
     func_name = co.co_name 
     file_name = co.co_filename 

     if func_name in ['loop_func']: 
      return self.trace_lines 

    def trace_lines(self, frame, event, arg): 
     if event != 'line' and event != 'return': 
      return 

     co = frame.f_code 
     func_name = co.co_name 
     file_name = co.co_filename 
     source = inspect.getsourcelines(co)[0] 

     #cmd_step.wait() 

     while not cmd_step.is_set(): 
      time.sleep(0.2) 

     cmd_step.clear() 
     print('Call to %s on line %s of %s' % (func_name, frame.f_lineno, co.co_filename)) 

    def loop_func(self): 
     for i in range(0,3): 
      print("Loop number %d\n" %i) 

    def run(self): 
     print "Started thread" 
     sys.stdout = reportQueue 
     sys.settrace(self.trace_calls) 
     self.loop_func() 
     print "Exiting " + self.name 


class StdoutQueue(Queue.Queue): 
    def __init__(self, *args, **kwargs): 
     Queue.Queue.__init__(self, *args, **kwargs) 

    def write(self, msg): 
     self.put(msg) 

    def flush(self): 
     sys.__stdout__.flush() 


def catch_message(text_widget, queue): 
    text_widget.insert(Tkinter.INSERT,queue.get()) 


if __name__ == "__main__": 
    cmd_step = threading.Event() 
    cmd_step.clear() 

    reportQueue = StdoutQueue() 

    thr1 = debug_thread('Thread 1') 

    app = simpleapp_tk(None) 
    app.title('Debug App') 

    while True: 
     app.update_idletasks() 
     app.update() 
     print "updating..." 
+0

'thr1.start()'ではなく、 'thr1.run()'を直接呼び出すので、実際には2番目のスレッドを起動することはありません。 – zstewart

答えて

0

あなたはthreading.Threadから継承してスレッドを作成するとき、あなたはThread.runをオーバーライドすることにより、スレッドで実行されなければならないコードを提供します。しかし、debug_thread.runは依然として通常の方法です。 thr1.run()を呼び出すと、別のスレッドを開始しないdebug_thread.runで宣言されたコードだけが実行されます。別のスレッドを開始するには、thr1.start()を使用する必要があります。新しいスレッドが作成され、runメソッドが新しいスレッド内で呼び出されます。

threading.Threadから継承する代わりに、新しいスレッドで呼び出す関数を引数としてthreading.Threadコンストラクタに渡すことができます。

def run_in_thread(step_event, stop_event): 
    local_data = 1 
    while not stop_event.is_set(): 
     local_data = do_step(local_data) 
     step_event.wait() 
     step_event.clear() 

step_event = threading.Event() 
stop_event = threading.Event() 

thread = threading.Thread(target=run_in_thread, args=(step_event, stop_event)) 
thread.start() 

スレッド機能をステップに、それを停止し、スレッドを終了させるために、step_eventを設定するには、そのようにループがを中断しますstop_eventを設定するので、スレッドは再びループ状態を確認しますstep_eventを設定します。

2つのEventはスレッド引数として渡されるため、グローバルにする必要はありません。

関連する問題