Verständnisfrage:

Plattformunabhängige GUIs mit wxWidgets.
Antworten
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Startet wx.App einen eigenen Thread ?

Also ist die Mainloop dann ein eigener Thread ?

Wenn ja, dann wuesste ich endlich warum ein paar Sachen mit GUI-Aktualisierungen nicht funktionieren.

Weil die GUI Elemente sind ja nicht Threadsafe, und wenn die GUI Elemente nicht in der App verwaltet werden, wuerde dass ja bedeuten, dass ein Thread parallel zu App versucht, Gui komponenten zu aktualisieren, was natuerlich zu problemen fuehrt.

Wenn nicht, habe ich immer noch ein grosses Verstaendnisproblem, aber das kann ich ja dann immer noch darstellen :)

edit: also laut meinen Tests ist das die einzige erklaerung

beispiel ist mein anderer Thread mit dem Progressdialog

Mach ich ne Klasse, die von App oder PySimpleApp abgeleitet ist und lasse diese Klasse das MainFrame erzeugen und dieses mainframe wiederum den Progressdialog, so laeuft alles einwandfrei

laeuft App quasi parallel dazu (also mainframe wird im hauptprogramm erzeugt und der progressdialog damit auch), so haengt es sich beim ProgressDialog.Destroy() auf
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

maxip hat geschrieben:Startet wx.App einen eigenen Thread ?

Also ist die Mainloop dann ein eigener Thread ?
Nein. Wenn du den Python-Interpreter startest, dann hast du automatisch einen Prozess mit einem Thread. Wenn du in diesem die Mainloop startest, dann läuft die Schleife in diesem einem Thread und die Ausführung hängt in der Schleife.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

maxip hat geschrieben:Startet wx.App einen eigenen Thread ?
Also ist die Mainloop dann ein eigener Thread ?
Hallo maxip!

Startest du ein Python-Programm (egal welches), dann läuft dieses in einem Thread. Ich nenne ihn jetzt mal Hauptthread.

wxPyhon-Programme sind da nicht anders gestrickt. Dort wo du die App-erstellst (initialisierst), dort, in diesem Thread läuft die wxPython-GUI. Und das ist normalerweise (ich kenne nur sehr wenige Ausnahmen) der Hauptthread.

Code: Alles auswählen

import wx
app = wx.PySimpleApp()
f = wxFrame(None)
f.Show()
app.MainLoop()
Dieses kleine Programm stellt ein normales wxPython-Programm dar. Zuerst wird im Hauptthread die App erstellt. Dann wird ein Formular erstellt und für die Anzeige vorgesehen (``..Show()``). Und sobald Python in der Zeile ``app.MainLoop()`` angekommen ist, läuft das Eventsystem von wxPython seinen geregelten Lauf. In dieser Zeile bleibt das Programm stehen, bis das Fenster geschlossen wurde.

Kommt das Python-Programm nie zur ``app.MainLoop()``-Zeile, dann funktioniert auch das Eventsystem von wxPython nicht. Es ist also wichtig, dass man beim Starten des Programms das Programm auch wirklich bis zu dieser Zeile kommen lässt.

Will man jetzt trotzdem, während das Programm in der wxPython-MainLoop läuft, "Arbeit" durchführen, die den Aufbau der GUI bzw. die MainLoop unterbricht, dann muss diese Arbeit in einen zusätzlichen Thread ausgelagert werden.

Es gibt manchmal eine Alternative. Man kann, kurz dauernde Arbeiten periodisch über einen Timer aufrufen. Die vom Timer aufgerufene Funktion läuft in einem neuen Thread.

In allen Fällen muss man die Threads trennen und darf nicht von einem Thread aus auf wxPython-Objekte zugreifen und diese verändern. --> ``wx.CallAfter()``

Mehr fällt mir jetzt nicht ein.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

gerold hat geschrieben:Es gibt manchmal eine Alternative. Man kann, kurz dauernde Arbeiten periodisch über einen Timer aufrufen. Die vom Timer aufgerufene Funktion läuft in einem neuen Thread.

In allen Fällen muss man die Threads trennen und darf nicht von einem Thread aus auf wxPython-Objekte zugreifen und diese verändern. --> ``wx.CallAfter()``
Stimm das wirklich? Ich dachte immer, man könnte aus einem Timer-Event ganz normal auf die GUI zugreifen.
MfG
HWK
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

HWK hat geschrieben:Stimm das wirklich? Ich dachte immer, man könnte aus einem Timer-Event ganz normal auf die GUI zugreifen.
Hallo HWK!

Ich bin mit der Aussage
gerold hat geschrieben:Die vom Timer aufgerufene Funktion läuft in einem neuen Thread.
ein wenig über das Ziel herausgeschossen. :roll: Diese Aussage von mir stimmt nicht.

Richtig ist: Ein Timer löst ein Event aus und das Event wird abgearbeitet, wie alle anderen Events auch. Der Event-Handler (die aufgerufene Funktion) läuft also nicht in einem eigenen Thread. Und somit ist der Event-Handler des Timers für lange laufende Arbeiten, welche den Aufbau der GUI verhindern können, ungeeignet.

Wenn man aber in der mit dem Timer aufgerufenen Funktion, der MainLoop immer wieder Zeit zum Arbeiten lasst (z.B. mit ``wx.YieldIfNeeded()``), dann kann sich die MainLoop erholen, die GUI neu aufbauen und auch auf Tastendrücke reagieren.

Wie komfortabel das GUI-Verhalten ist, hängt dann direkt davon ab, wie oft man ``wx.YieldIfNeeded()`` ausführen kann und wie sehr die Arbeit den Computer auslastet.

Diese Probleme gibt es nicht, wenn man die Arbeit in einen Thread auslagert. Man muss dann zwar mit ``wx.CallAfter`` arbeiten, aber die MainLoop wird nie unterbrochen und das Programm reagiert wie gewohnt auf Eingaben und baut sich automatisch neu auf.

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Danke für die Erläuterung, Gerold!
MfG
HWK
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Hi,

kannst du mir dann nochmal erklaeren, warum deine Version fuktioniert und meine sich aufhaengt ?

http://www.python-forum.de/topic-14581.html

Der einzige Unterschied ist doch, dass du von wx.App ableitest und das Frame sozusagen in der App verankerst und ich das nicht tue.

Ansonsten ist doch alles vom Prinzip her gleich ?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Du darfst nicht direkt aus einem anderen Thread auf die GUI zugreifen. Das funktioniert bei keinem mir bekannten GUI-Toolkit.

Deine ``runWork``-Funktion wird einem eigenen Thread ausgeführt. Also darfst du dort ``_pd.Update(i)`` und ``_pd.Destroy()`` nicht machen, und musst sie stattdessen indirekt durch ``wx.CallAfter``(``gobject.idle_add`` bei gtk) aufrufen. Wenn wx dann wieder die Event-Schleife abarbeitet, wird die übergebene Funktion *im Mainloop-Thread* aufgerufen. Und das ist bei gerolds Beispiel der Fall. `progressdialog_update` wird im MainLoop-Thread ausgeführt und nicht im Worker-Thread.
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Masaru ruft aber im thread auch die update und destroy funktion auf
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

OK, Gerolds Beispiel ist einleuchtend, trotzem wundere ich mich warum in dem Forumsthread mein Beispiel net geht und das von Masaru aber schon

Is wohl dann eher zufall?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Zufall. Bei mir funktioniert es z.B. nicht. Die Xlib bringt irgendwann "unexpected async reply".
maxip
User
Beiträge: 61
Registriert: Dienstag 11. März 2008, 09:43

Ok, danke

werde mich dann an Gerolds Beispiel halten :)
Antworten