2016-08-04 22 views
1

これは私のアプリケーションではありませんが、非常に似ています。問題を示すためにこのテストコードを作成しました。基本的に私はpythonスレッドからtcl procを呼び出そうとしています。結果が準備できたら、Tcl procはPython関数にコールバックします。この結果は、イベントとしてwxフレームに投稿されます。私が純粋なPythonコードとして実行すると、正常に動作します。 tcl procを使用すると、アプリ全体が情報なしでクラッシュします。私がwait_time(例えば100)を上げると、それはtclでもうまく動作します。それはコールバックの問題が高いか、それとも私が他の何かを逃しているのか?このアプリは途中でウィンドウで実行されます。TkinterのPython関数へのコールバックTclのクラッシュWindowsで

import wx 
from Tkinter import Tcl 
from threading import Thread 
import wx.lib.newevent 
from time import sleep 

CountUpdateEvent, EVT_CNT_UPDATE = wx.lib.newevent.NewEvent() 

tcl_code = 'proc tcl_worker {name max_count delay_time callback} { while {$max_count>0} {after $delay_time; $callback $name $max_count; incr max_count -1}}' 

# Option to use Tcl or not for counter 
# When enabled, Tcl will callback to python to upate counter value 
use_tcl = True 

# Option to create tcl interpreter per thread. 
# Test shows single interpreter for all threads will fail. 
use_per_thread_tcl = True 

count = 5000 
wait_time = 1 ;# in milliseconds 

class Worker: 
    def __init__(self,name,ui,tcl): 
     global use_per_thread_tcl 
     self.name = name 
     self.ui = ui 
     if use_per_thread_tcl: 
      self.tcl = Tcl() 
      self.tcl.eval(tcl_code) 
     else: 
      self.tcl = tcl 
     self.target = ui.add_textbox(name) 
     self.thread = Thread(target=self.run) 
     self.thread.daemon = True 
     self.thread.start() 

    def callback(self, name, val): 
     evt = CountUpdateEvent(name=self.name, val=val, target=self.target) 
     wx.PostEvent(self.ui,evt)   
    def run(self): 
     global count, wait_time, use_tcl 

     if use_tcl: 
      # Register a python function to be called back from tcl 
      tcl_cmd = self.tcl.register(self.callback) 

      # Now call tcl proc 
      self.tcl.call('tcl_worker', self.name, str(count), str(wait_time), tcl_cmd) 
     else: 
      # Convert milliseconds to seconds for sleep 
      py_wait_time = wait_time/1000 
      while count > 0: 
       # Directly call the callback from here 
       self.callback(self.name, str(count)) 
       count -= 1 
       sleep(py_wait_time) 


class MainWindow(wx.Frame): 
    def __init__(self, parent): 
     wx.Frame.__init__(self, parent, title="Decrement Counter", size=(600, 100)) 

     self._DoLayout() 
     self.Bind(EVT_CNT_UPDATE, self.on_count_update) 

    def _DoLayout(self): 
     self.sizer = wx.BoxSizer(wx.HORIZONTAL) 

     self.panels = [] 
     self.tbs = [] 
     self.xpos = 0 

    def add_textbox(self,name): 
     panel = wx.Panel(self, pos=(self.xpos, 0), size=(60,40)) 
     self.panels.append(panel) 
     tb = wx.StaticText(panel, label=name) 
     tb.SetFont(wx.Font(16,wx.MODERN,wx.NORMAL,wx.NORMAL)) 
     self.sizer.Add(panel, 1, wx.EXPAND, 7) 
     self.tbs.append(tb) 
     self.xpos = self.xpos + 70 
     return tb 

    def on_count_update(self,ev): 
     ev.target.SetLabel(ev.val) 
     del ev 

if __name__ == '__main__': 
    app = wx.App(False) 
    frame = MainWindow(None) 
    tcl = Tcl() 
    tcl.eval(tcl_code) 
    w1 = Worker('A', frame, tcl) 
    w2 = Worker('B', frame, tcl) 
    w3 = Worker('C', frame, tcl) 
    w4 = Worker('D', frame, tcl) 
    w5 = Worker('E', frame, tcl) 
    w6 = Worker('F', frame, tcl) 
    w7 = Worker('G', frame, tcl) 
    w8 = Worker('H', frame, tcl) 
    frame.Show() 
    app.MainLoop() 

答えて

1

各Tclインタプリタオブジェクト(Tclプロシージャを実行する方法を知っている、すなわち、コンテキスト)のみ安全に作成OSスレッドから使​​用することができます。これは、TclがPythonのようなグローバルインタプリタロックを使用せず、内部的に必要なロックの数を減らすためにスレッド固有のデータを広範囲に使用するためです。 (よく書かれたのTclコードは、適切なハードウェア上で非常に大きなスケールアップするためにこの利点を取ることができます。)このための

、あなたが必見がシングルからあなただけの今までTclコマンドを実行したり、Tkinterの操作いることを確認してください糸;これは一般的にメインスレッドですが、Pythonとの統合が本当に必要かどうかはわかりません。必要に応じてワーカースレッドを起動することはできますが、TclやTkinterを使用することはできません(それほど問題にならないような特別な予防策はありません)。代わりに、GUIとの対話を処理するためにメインスレッドにメッセージを送信する必要があります。それを行うにはさまざまな方法があります。

+0

ご回答いただきありがとうございます。私のコードで 'use_per_thread_tcl'をTrueに設定すると、すべてのスレッドが独自のtclインタープリタを作成します。 'tcl_worker' procは各スレッドによって個別に評価されます。ですから、私はtclインタプリタ間には相互作用がないと思います。 –

+0

run()メソッドを入力する前にTclインタプリタを作成して、すべてがメインスレッドで作成されるようにします。おそらく、作成メソッドを実行メソッドに移動します。 – schlenk

+0

興味深い提案をいただきありがとうございますschlenk。私はあなたの提案に従って試してみました。 Tclインタプリタは 'tcl_worker' procを呼び出すことさえしません。アプリ全体が何もせずにハングアップします。労働者の数を1人に減らすことは幸いです。 tclインタプリタはメインスレッドや子スレッドで作成できるようですが、tclインタプリタは1つしか使用できません。たぶんTkinterは、複数のインタプリタオブジェクトの使用を制限するいくつかのグローバル変数を使用している可能性があります。次に、マルチプロセスモジュールを使って子プロセスにtclオブジェクトを作成してみましょう。 –

関連する問題