wxPython, pycrust & Threads

Plattformunabhängige GUIs mit wxWidgets.
Antworten
MBR
User
Beiträge: 29
Registriert: Freitag 29. September 2006, 17:03

Hallo zusammen,

ich habe ein Problem und komme einfach nicht weiter, bzw. hab viel probiert und nichts hilft. Folgendes:

In meinem Programm benutze ich EditorShellNotebook aus wx.py.editor. Dieses Notebook ist in eine wxApp eingebettet und ermöglicht es kleine Prozeduren zu schreiben die mittels einer entwickelten API (C-Types DLL) auf diversen intelligenten elektronischen devices (IED) "abgespielt" werden können. Das Ausführen kann direkt im Mainthread erfolgen, oder halt im Editor, bzw. der Shell des Notebooks.

Bei Letzterem hängt meine GUI, trotz Verlagerung des Prozeduraufrufes in einen eigenen Thread. Erst wenn die Prozedur durchgelaufen ist, besteht wieder die Möglichkeit andere Aktionen über die GUI zu initiieren, leider. Ich versuche jetzt schon seit Längerem diesen Zustand zu ändern, nichts hilft.

Vieleicht kann mir hier jemand erklären, warum trotz eigenem Threads die Oberfläche hängt? Und was wäre eine geeignete Variante Abhilfe zu schaffen?

Nachfolgend der Code:

Code: Alles auswählen


#Threadfunction
    def thread_func(self):
        #Funktion ausfuehren in der Shell
        cmd = "result=" + self.editscript.test_reference + "()"
        wx.CallAfter(self.editzone.shell.push, cmd, False)
        #Ergebnis auswerten
        cmd = "erg=_get_function_result(result)"                    
        wx.CallAfter(self.editzone.shell.push, cmd, False)
        #Ergebnis ausgeben
        outline = 'print " |--- Prozedureergebnis ---| "'
        wx.CallAfter(self.editzone.shell.push, outline, False)
        outline = 'print erg'           
        wx.CallAfter(self.editzone.shell.push, outline, False)                     
        #SERVER delete
        wx.CallAfter(self.editzone.shell.push, 'del server', False)

#Aufruffunktion
#Ausführen OK
    def OnButton4Button(self, event):
#...
                (erfolg, py_code) = self.get_python_code(self.sourcecode_filter())                
                if erfolg: 
                    #Servername
                    server_name = self.comboBox1.GetValue()                     
                    #Notebook page
                    self.editzone.SetSelection(1)
                    #Shell page
                    self.editzone.shell.setFocus() 
                    #Funktion lesen  
                    self.editzone.shell.Execute(py_code)
                    #Shell commands
                    #Server erzeugen
                    cmd = "server=gl_get_server('" + server_name + "')"
                    self.editzone.shell.push( cmd, False)
                     
                                                                              
                    #ScriptThread
                    #self.script_thread = KThread(target=self.thread_func)
                    # Thread starten
                    #self.script_thread.start()
                    
                    start_new_thread(self.thread_func, ()) 
                    
                    
                    #Script function existiert
                    self.script_function_exists = True                    
                else:
                    dlg = wx.MessageDialog(self, py_code,'Fehler', wx.OK | wx.ICON_INFORMATION)
                    try:
                        dlg.ShowModal()
                    finally:
                        dlg.Destroy()            
            else:
#...
                

BlackJack

@MBR: Ich sehe nicht so wirklich wo Du langlaufenden Code in einem eigenen Thread laufen lässt. `thread_func()` scheint nichts aufwändiges zu machen!? Da werden doch nur Callbacks in den GUI-Thread gegeben, die auch *im GUI-Thread* ausgeführt werden.
MBR
User
Beiträge: 29
Registriert: Freitag 29. September 2006, 17:03

Hallo BlackJack,

in der thread_func() wird eine Python Prozedur aufgerufen die in der Shell ausgeführt wird. Dies geschieht mit:

Code: Alles auswählen


cmd = "result=" + self.editscript.test_reference + "()"
        wx.CallAfter(self.editzone.shell.push, cmd, False)

In dem Fall ist die test_reference gleich dem Prozedurnamen und der Aufruf könnte folgendermaßen ausschauen:

Code: Alles auswählen


result = testfunc()

testfunc ist zuvor mit

Code: Alles auswählen


#Funktion lesen 
self.editzone.shell.Execute(py_code)

in der Shell bekannt gemacht worden.

Wenn ich diese Funktion jetzt in einem eigenen Thread aufrufe, sollte die GUI doch eigentlich nicht blockieren, oder habe ich da einen gravierenden Denkfehler?

Grüsse,
Markus
MBR
User
Beiträge: 29
Registriert: Freitag 29. September 2006, 17:03

Und was den langlaufenden Code betrifft, die aufgerufene testfunc kann durchaus eine länge von bis zu 400 Zeilen und mehrere Unterfunktionen beinhalten, so dass die Laufzeit durchaus bis zu einer Stunde sein Kann.

Grüsse,
Markus
BlackJack

@MBR: Der langlaufende Code wird eben *nicht* in der `thread_func()` aufgerufen. Da wird nur `wx.CallAfter()` aufgerufen, was den *GUI-Thread* anweist die übergebene Funktion aufzurufen. Und wenn er das tut, dann blockiert er halt solange wie die Funktion läuft.
MBR
User
Beiträge: 29
Registriert: Freitag 29. September 2006, 17:03

Oh, da war mir die eigentliche Bedeutung des wx.CallAfter wohl nicht richtig bewußt. Wenn der Code nicht in der Shell, sondern in der Oberfläche ausgeführt wird verwende ich für die Bildschirmausgabe wx.CallAfter, das allerdings nur für die GUI-Aktionen. Gewaltiger Unterschied zum jetzigen Aufruf, danke für den Hinweis!

Die erste Version beinhaltete auch den Funktionsaufruf in der Shell über die thread_func ohne wx.CallAfter. Allerdings ist es da zu merkwürdigen Phänomenen gekommen, die ich mir nicht erklären konnte, bzw. dachte, dass sie mit dem Zugriff auf GUI-Elemente zusammenhängen. Deshalb dann das wx.CallAfter, lief sofort, Erklärung hast du mir ja gegeben. Problem somit eher nicht gelöst...

Das Problem beim Aufruf ohne wx.CallAfter ist, dass es sobald ich GUI Elemente während des laufenden Thread anfasse (aber auch ohne Aktionen) es nach unregelmässiger Laufzeit zu Zugriffsverletzungen kommt:

Unhandled exception at 0x02946254 in python.exe: 0xC0000005: Access violation writing location 0x00000000.

Die Bildschirmausgabe in der GUI enthält zum Teil auch für mich nicht zu erklärende Ausgaben (NUL, sowie wirre Zeichen), siehe Screenshots:

Bild
Bild

Es würde mir schon sehr helfen, wenn ich die eigentliche Ursache der Access violation, bzw der Bildschirmausgaben wüsste. Muss dazu sagen, dass ich mich das erste Mal mit pyCrust, bzw. einer integrierten Shell versuche. Dementsprechend fehlt vermutlich das nötige Hintergrundwissen.

Danke für jede Hilfe und

@BlackJack

Vielen Dank bis hier her. War nach dem wx.CallAfter froh, dass es lief ohne mir die nötigen Gedanken zu machen warum es denn lief, bzw. die Probleme nicht mehr existent waren...
Antworten