Dies ist nicht genau meine Anwendung, aber sehr ähnlich. Ich habe diesen Testcode erstellt, um das Problem zu zeigen. Im Grunde versuche ich tcl proc aus Python-Thread aufzurufen. Tcl proc ruft die Python-Funktion zurück, wenn das Ergebnis fertig ist. Dieses Ergebnis wird als Ereignis zu wx frame gepostet. Wenn ich als reiner Python-Code laufe, funktioniert es gut. Wenn ich tcl proc verwende, stürzt die ganze App ohne irgendwelche Informationen ab. Wenn ich wait_time (sagen wir 100) erhöhe, dann funktioniert es auch mit tcl. Ist es die hohe Rate des Rückrufs ein Problem oder fehlt mir etwas anderes. Diese App läuft übrigens auf Windows.Callback zu Python-Funktion von Tkinter Tcl stürzt in 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()
Vielen Dank für Ihre Antwort. Durch Setzen von 'use_per_thread_tcl' auf True in meinem Code erstellt jeder Thread seinen eigenen tcl-Interpreter. 'tcl_worker' proc wird von jedem Thread einzeln ausgewertet. Also ich denke, es gibt keine Interaktionen zwischen Tcl-Interpreten. –
Sie erstellen die Tcl-Interpreter, bevor Sie Ihre run() -Methode eingeben, sodass sie alle im Hauptthread erstellt werden. Verschieben Sie die Erstellung möglicherweise in Ihre Ausführungsmethode. – schlenk
Danke schlenk für den interessanten Vorschlag. Ich habe es nach Ihrem Vorschlag versucht. Der Tcl-Interpreter ruft nicht einmal den 'tcl_worker' proc auf. Die ganze App hängt, ohne etwas zu tun. Es freut mich, wenn ich die Anzahl der Arbeiter auf eins reduziere. Es scheint, als ob der tcl-Interpreter im Haupt- oder Unterthread erstellt werden kann, aber nur ein tcl-Interpreter ist erlaubt. Vielleicht verwendet Tkinter einige globale Variablen, die die Verwendung mehrerer Interpreter-Objekte einschränken. Als nächstes werde ich versuchen mit Multiprocessing-Modul, Erstellen von Tcl-Objekte in untergeordneten Prozessen. –