Liebe Leute,
ich versuche schon den halben Tag ein aktuelles Tutorial zu finden, das beispielhaft ein Programm mit GTK-GUI und Threads o.Ä. erklärt.
Und zwar aus dem Grund, dass ich schnellstmöglich was am Laufen haben möchte, ohne mich tiefgründig einzuarbeiten.
Die Tutorials, die ich gefunden habe, sind zum einen etwas veraltet, zum anderen werden die Zusammenhänge nicht weiter erklärt.
Der erste Reinfall ist ja schonmal, dass man Threads und GTK nicht ohne weiteres mischen kann, weil GTK natürlich selbst Threads benutzt.
(Anscheinend ist das Modul threads sowieso ein ausaufmodell und man sollte lieber mit threading eigene thread-klassen erstellen.)
Dann habe ich irgendwo gelesen, dass man sowieso besser Methoden aus gobjects benutzen sollte.
Ein Versuch mit gtk.gdk.threads_init() und gtk.threads_enter() bzw. gtk.threads_leave() führte ins Leere
Hier ist ein Snippet, wo es vermutlich richtig gemacht wird:
http://www.python-forum.de/topic-2711.html
[edit: auch schon veraltet:
GtkDeprecationWarning: gtk.threads_enter is deprecated, use gtk.gdk.threads_enter instead]
Hier ein ausführlicher Thread zu, Thema gobject
http://www.python-forum.de/topic-17662, ... ht=gobject
Hier noch ein paar Links:
http://www.pygtk.org/docs/pygtk/
http://docs.python.org/library/thread.html besser:
http://docs.python.org/library/threading.html
http://www.pygtk.org/docs/pygobject/
Hat zufällig jemand Lust die Allgemeinheit mit einem kleinen Tutorial zu bereichern?
GTK und Threads / Threading, Gobject !?
-
- Python-Forum Veteran
- Beiträge: 16025
- Registriert: Freitag 20. Juni 2003, 16:30
- Kontaktdaten:
Die Frage ist: was willst du denn machen? Ich brauche in GTK-Applikationen quasi nie Threads, dafür reicht oftmals schon ``gobject.idle/timeout_add``.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Hi,
Ziel ist ein Programm, das mehrere physikalische Größen über serielle Verbindungen steuert und ausliest, grafisch darstellt (evtl. matplotlib ?), die Daten bewertet und sowohl roh als auch ein Bericht evtl. als PDF auf Festplatte schreibt. (http://www.python-forum.de/topic-18055.html)
Die Kommunikation könnte man wahrscheinlich mit entsprechender Wartezeit in einer Schleife auch nacheinander machen.
Ich hatte allerdings angenommen, dass mit einem Thread vielleicht die Auslesefrequenz eines Gerätes maximiert werden kann, weil nicht auf anderes gewartet werden muss.
Es sollte nämlich eine Abbruchbedingung von einem Gerät so schnell wie möglich erfasst und daraufhin ein Befehl an ein anderes Gerät gesendet werden.
Bislang habe ich Klassen für die verschiedenen Geräte geschrieben, die die serielle Verbindung herstellen, und den Datenaustausch bzw. die Umwandlung von Werten durchführen. Knackpunkt ist da eigentlich nur die Prüfung auf Fehler (Nicht eingeschaltet oder falsch eingestellt -> timout der Verbindung -> Rückgabewert_Fehler = True), daher auch die Frage im anderen Thread. Die Behandlung dieses Problems ist wahrscheinlich mit "try: except:" eleganter als mit "if gerät.read()[3] dann nochmal"..
Wenn man auf Basis dieser Klassen ein Thread Objekt erstellen könnte, mit denen nebem dem Hauptthread auch die anderen Threads kommunizieren könnten, wär' das doch eine "gute" Lösung?
Jetzt möchte ich allerdings klein anfangen und erstmal eine einzelne Verbindung als Thread auslesen und anzeigen, mit dem genannten Codesnippet bin ich da mittlerweile auf einem funktionierenden Weg.
Wahrscheinlich sollte ich jedoch auf das Modul Threading umsteigen und die "Funktion" als Klasse schreiben.
Also, falls du mein Vorhaben durchschaust, würde ich mich über Tipps freuen!
Ziel ist ein Programm, das mehrere physikalische Größen über serielle Verbindungen steuert und ausliest, grafisch darstellt (evtl. matplotlib ?), die Daten bewertet und sowohl roh als auch ein Bericht evtl. als PDF auf Festplatte schreibt. (http://www.python-forum.de/topic-18055.html)
Die Kommunikation könnte man wahrscheinlich mit entsprechender Wartezeit in einer Schleife auch nacheinander machen.
Ich hatte allerdings angenommen, dass mit einem Thread vielleicht die Auslesefrequenz eines Gerätes maximiert werden kann, weil nicht auf anderes gewartet werden muss.
Es sollte nämlich eine Abbruchbedingung von einem Gerät so schnell wie möglich erfasst und daraufhin ein Befehl an ein anderes Gerät gesendet werden.
Bislang habe ich Klassen für die verschiedenen Geräte geschrieben, die die serielle Verbindung herstellen, und den Datenaustausch bzw. die Umwandlung von Werten durchführen. Knackpunkt ist da eigentlich nur die Prüfung auf Fehler (Nicht eingeschaltet oder falsch eingestellt -> timout der Verbindung -> Rückgabewert_Fehler = True), daher auch die Frage im anderen Thread. Die Behandlung dieses Problems ist wahrscheinlich mit "try: except:" eleganter als mit "if gerät.read()[3] dann nochmal"..
Wenn man auf Basis dieser Klassen ein Thread Objekt erstellen könnte, mit denen nebem dem Hauptthread auch die anderen Threads kommunizieren könnten, wär' das doch eine "gute" Lösung?
Jetzt möchte ich allerdings klein anfangen und erstmal eine einzelne Verbindung als Thread auslesen und anzeigen, mit dem genannten Codesnippet bin ich da mittlerweile auf einem funktionierenden Weg.
Wahrscheinlich sollte ich jedoch auf das Modul Threading umsteigen und die "Funktion" als Klasse schreiben.
Also, falls du mein Vorhaben durchschaust, würde ich mich über Tipps freuen!
Leider stoße ich selbst bei dem Minimalprogramm an meine Grenzen.
Es startet einen Thread und läuft erwartungsgemäß, allerdings wird der Thread nicht beendet und auch sonst weiß ich nicht, ob das so im Sinne des Erfinders ist..
Hilfe!?
Hier das mit
für gkt.builder() konvertierte XML:
Es startet einen Thread und läuft erwartungsgemäß, allerdings wird der Thread nicht beendet und auch sonst weiß ich nicht, ob das so im Sinne des Erfinders ist..
Hilfe!?
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import threading
import Queue
import types
import time
import gtk # GTK+ 2
class mySerialDevice(object):
def __init__(self, port):
self.port = port
def connect(self):
pass
#~ try:
#~ self.serial = serial.Serial( port=self.port,
#~ baudrate=9600,
#~ bytesize=serial.EIGHTBITS,
#~ parity=serial.PARITY_NONE,
#~ stopbits=serial.STOPBITS_ONE,
#~ timeout = 2
#~ )
#~ except serial.SerialException:
#~ exit("Cannot open serial port")
def disconnect(self):
pass
#~ self.serial.close()
class myThread(threading.Thread):
def __init__(self, label):
"""
"""
threading.Thread.__init__(self)
self.label = label
self.counter = 0
self.dev = mySerialDevice("/dev/ttyS0")
self.dev.connect()
def run(self):
"""
"""
while True:
self.counter += 1
time.sleep(1)
gtk.gdk.threads_enter()
self.label.set_text("%d" % self.counter)
gtk.gdk.threads_leave()
def stop(self):
self.dev.disconnect()
self.kill() # WIE?
class myGUI:
"""
"""
def __init__(self):
builder = gtk.Builder()
builder.add_from_file("test.xml")
builder.connect_signals(self)
self.window = builder.get_object("window")
self.label = builder.get_object("label")
self.test = myThread(self.label)
self.test.start()
self.counter = 0
def run(self):
pass
def main(self):
gtk.main()
def on_window_destroy(self, widget, data=None):
self.test.stop()
gtk.main_quit()
def on_button_clicked(self, widget, data=None):
self.label.set_text("button_clicked")
if __name__ == "__main__":
gtk.gdk.threads_init()
app = myGUI()
gtk.gdk.threads_enter()
app.main()
gtk.gdk.threads_leave()
Code: Alles auswählen
gtk-builder-convert test.glade test.xml
Code: Alles auswählen
<?xml version="1.0"?>
<!--Generated with glade3 3.4.5 on Sat Mar 7 12:31:49 2009 -->
<interface>
<object class="GtkWindow" id="window">
<property name="visible">True</property>
<property name="title" translatable="yes">Window Title</property>
<signal handler="on_window_destroy" name="destroy"/>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="label" translatable="yes">label</property>
</object>
</child>
<child>
<object class="GtkButton" id="button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">button</property>
<signal handler="on_button_clicked" name="clicked"/>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
Nach einem sinnlosen Versuch mit threading.event liest man bei
http://docs.python.org/library/threading.html#id1
Trotzdem würde mich interessieren, ob das so der richtige Weg ist.
http://docs.python.org/library/threading.html#id1
Die endlose Whileschleife in myThread.run() braucht also eine Abbruchbedingung, damit der Thread beendet werden kann.Once the thread’s activity is started, the thread is considered ‘alive’. It stops being alive when its run() method terminates – either normally, or by raising an unhandled exception. The is_alive() method tests whether the thread is alive.
Trotzdem würde mich interessieren, ob das so der richtige Weg ist.
Ein Code Bsp. findest Du unter http://www.python-forum.de/topic-18028.html 2. Post. Hier siehst Du, wie Du die Abbruchbedingung in Dein while packst. Da Du mit einem Klassenansatz fährst, müßtest Du das ein wenig umstricken (Event-Initialisierung in Konstruktor, set() anstelle Deines kills, while wie gehabt usw.)
Schon ergibt sich die nächste Frage:
Im Beispiel bekommt myThread ein Handle zum Zugriff auf das Widget label.
Im echten Programm sind allerdings noch weitere, abhängige Labels zu ändern, myThread soll auf Anweisung vom GUI auch noch andere Funktionen ausführen, usw.
Wie entscheide ich, was in den Thread, und was in die GUI gehört?
Lässt sich der Datenaustausch zwischen Thread und GUI irgendwie allgemeiner durchführen?
Im Beispiel bekommt myThread ein Handle zum Zugriff auf das Widget label.
Im echten Programm sind allerdings noch weitere, abhängige Labels zu ändern, myThread soll auf Anweisung vom GUI auch noch andere Funktionen ausführen, usw.
Wie entscheide ich, was in den Thread, und was in die GUI gehört?
Lässt sich der Datenaustausch zwischen Thread und GUI irgendwie allgemeiner durchführen?
Hm, das läßt sich nicht so einfach beantworten. Für Multithreading-Anwendungen gehört alles gui-spezifische definitiv in die "Gui-Klasse", sollte also gui-nah behandelt werden. Die Komunikation würde ich dann über signals realisieren, so können mehrere konkurrierende Threads ohne lästige Locks weiterarbeiten (was halt passiert, wenn ein zweiter Thread versucht, einen Mutex zu erhalten während ein anderer ihn noch hält). Die Signale würden dann einfach von event-handler der Gui nacheinander abgearbeitet.Wie entscheide ich, was in den Thread, und was in die GUI gehört?
Lässt sich der Datenaustausch zwischen Thread und GUI irgendwie allgemeiner durchführen?
Den Weg, den Du im Moment gehts, holt sich den Mutex (threads_enter()), beschreibt eine "globale" Variable (Dein Label) und gibt den Mutex wieder frei (threads_leave()). Das ist vollkommen legitim, könnte aber bei mehreren Threads halt zu den beschrieben Locks führen (die Threads müßten dann unnötig angehalten werden). Für Deine Anwendung würde das meiner Meinung nach in Ordnung gehen, da Du myThread nur einmal anwirfst. Trotzdem würde ich eher den Signalweg bevorzugen. Dann hättest Du auch eine bessere Trennung von Gui-stuff und Thread-Logik.
"Hi" und danke für deine Unterstützung.
Ich bin leider nicht sicher, dass ich dich verstanden habe:
myThread macht sein Ding, bekommt ein Ergebnis, schreibt das in eine echte, globale Variable und setzt ein Signal.
myGUI (=gtk) wartet auf dieses Signal (wie?) und aktualisiert dann "label" mit dem Datum aus der globalen Variable.
So? Dann müsste ich ja GTK noch beibringen, auf das Signal zu reagieren.
In irgendeiner Newsgroup habe ich eben auch etwas davon gelesen, ein globales Dictionary anzulegen, dass dann für den Datenaustausch dient. Ist das der "beste" Weg? Geht das nicht in Richtung Queue?
http://docs.python.org/library/queue.html
Ich bin leider nicht sicher, dass ich dich verstanden habe:
myThread macht sein Ding, bekommt ein Ergebnis, schreibt das in eine echte, globale Variable und setzt ein Signal.
myGUI (=gtk) wartet auf dieses Signal (wie?) und aktualisiert dann "label" mit dem Datum aus der globalen Variable.
So? Dann müsste ich ja GTK noch beibringen, auf das Signal zu reagieren.
In irgendeiner Newsgroup habe ich eben auch etwas davon gelesen, ein globales Dictionary anzulegen, dass dann für den Datenaustausch dient. Ist das der "beste" Weg? Geht das nicht in Richtung Queue?
http://docs.python.org/library/queue.html
Habe ein sehr gutes Tutorial zum Thema Threading und Queues gefunden:
http://www.ibm.com/developerworks/aix/l ... index.html
Queues sind wohl nicht Lösung für mein Problem.
Ich suche wohl eher den Zusammenhang zwischen einem "eigenen" Event und dem builder.connect_signals(self).
http://www.ibm.com/developerworks/aix/l ... index.html
Queues sind wohl nicht Lösung für mein Problem.
Ich suche wohl eher den Zusammenhang zwischen einem "eigenen" Event und dem builder.connect_signals(self).
Hui, ich sagst mal so, viele Wege führen nach Rom.
Entweger:
Globaler Zugriff, wie Du es mit dem Label schon machst, nur sinnvoll für eins/zwei direkte Gui-"Eingriffe", sonst hast Du zuviel Gui-Code im Thread, wie selbst schon bemerkt hast. Für wenige Manipulationen fänd ich das noch azeptabel, andere würden Dir sofort schlechten Programmierstil vorwerfen und mit zig Programmierparadigmen wedeln. Eine gute Trennung zw. Thread- und Guilogik hättest Du so halt nicht, was definitiv auf Kosten der Wartbarkeit geht.
Oder:
Du nutzt Signale. Das tolle Gui-Libraries wie Gtk, Qt ist doch, das das Eventhandling Dir quasi geschenkt wird und mit Signal/Slot-Konzepten die Kommunikation zw. verschiedenen Objekten sehr einfach wird. Unten findest Du ein grobes Schema für PyGTK, wie man das in Deinem Falle nutzen könnte.
Editiere Deinen Code doch mal wie folgt:
Mit der zusätzlichen Ableitung von GObject erhält MyThread einige Basisfunktionalitäten der Gtk-Objekte, unter anderen kann MyThread jetzt Signale absetzen, die Deine Gui-Klasse versteht. Dadurch kannst Du mit MyThread wie mit jedem anderen Gtk-Objekt (genauer GObject) via Signal/Slot reden. Großer Vorteil, die Signale sind Events die normal via connect() verbunden und vom eingebauten Eventhandling automatisch abgearbeitet werden.
Grüße, Jerch
Naja nicht so ganz, ich meinte eher -Ich bin leider nicht sicher, dass ich dich verstanden habe:
myThread macht sein Ding, bekommt ein Ergebnis, schreibt das in eine echte, globale Variable und setzt ein Signal.
Entweger:
Globaler Zugriff, wie Du es mit dem Label schon machst, nur sinnvoll für eins/zwei direkte Gui-"Eingriffe", sonst hast Du zuviel Gui-Code im Thread, wie selbst schon bemerkt hast. Für wenige Manipulationen fänd ich das noch azeptabel, andere würden Dir sofort schlechten Programmierstil vorwerfen und mit zig Programmierparadigmen wedeln. Eine gute Trennung zw. Thread- und Guilogik hättest Du so halt nicht, was definitiv auf Kosten der Wartbarkeit geht.
Oder:
Du nutzt Signale. Das tolle Gui-Libraries wie Gtk, Qt ist doch, das das Eventhandling Dir quasi geschenkt wird und mit Signal/Slot-Konzepten die Kommunikation zw. verschiedenen Objekten sehr einfach wird. Unten findest Du ein grobes Schema für PyGTK, wie man das in Deinem Falle nutzen könnte.
Auch hiermit würdest Du zum Ziel kommen, müßtest Dich aber selbst um das Eventdispatching kümmern. Da Deine Grundbibliotheken aber schon alles mitbringen, ist das viel zu umständlich. (siehe oben Oder:)In irgendeiner Newsgroup habe ich eben auch etwas davon gelesen, ein globales Dictionary anzulegen, dass dann für den Datenaustausch dient. Ist das der "beste" Weg? Geht das nicht in Richtung Queue?
Editiere Deinen Code doch mal wie folgt:
Code: Alles auswählen
import gobject
...
class myThread(threading.Thread, gobject.GObject):
# signale deklarieren
__gsignals__ = {"update":(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [gobject.TYPE_INT])}
def __init__(self, label):
...
gobject.GObject.__init__(self)
def run(self):
...
# in der while-Schleife anstelle der
# gtk.gdk.threads_enter()
# self.label.set_text("%d" % self.counter)
# gtk.gdk.threads_leave()
self.emit("update", self.counter) # signal "update" absetzen
class myGUI:
def __init__(self):
...
self.test = myThread(self.label)
# signal test.update mit updateLabel verbinden
self.test.connect("update", self.updateLabel)
self.test.start()
...
def updateLabel(self, widget, num):
self.label.set_text("%d" % num)
Grüße, Jerch
Vorsicht: Man darf grundsätzlich nicht mit mehreren Threads auf die GUI zugreifen. Die Signal-Handler werden jedoch im gleichen Thread aufgerufen, in dem das Signal ausgelöst wurde. Das bedeutet also, dass Signal-Handler des "update"-Signals des ``myThread``-Objekts nicht direkt auf die GUI zugreifen dürfen.
Außerdem ist noch anzumerken, dass `gtk.gdk.threads_enter()`/`gtk.gdk.threads_leaver()` unter Windows nicht zuverlässig funktioniert, da man nur von einem Thread aus (und nicht einfach nur nicht mit mehreren gleichzeitig) auf GDK-Funktionen zugreifen sollte, also sollte man lieber `gobject.idle_add` verwenden (siehe auch "Functions for using GDK in multi-threaded programs", Abschnitt "Description").
Außerdem ist noch anzumerken, dass `gtk.gdk.threads_enter()`/`gtk.gdk.threads_leaver()` unter Windows nicht zuverlässig funktioniert, da man nur von einem Thread aus (und nicht einfach nur nicht mit mehreren gleichzeitig) auf GDK-Funktionen zugreifen sollte, also sollte man lieber `gobject.idle_add` verwenden (siehe auch "Functions for using GDK in multi-threaded programs", Abschnitt "Description").
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Also das Programm läuft jetzt schon ein bisschen mehr :-)
Wenn es unter Windows nicht liefe, ist das für mich nicht schlimm.
Zu deinem 2. Satz: Ich bin der Meinung, dass dem nicht so ist. Das Signal/Event wird in "MyThread" emitted, und in myGUI ist der Handler. ?
Unabhängig davon habe ich noch eine Frage:
In "myGUI" ist "label" durch "updateLabel" aktualisiert worden.
Den übermittelten Wert kann ich in updateLabel ja auch noch für meine anderen Zwecke Nutzen. Gibt es theoretisch auch die Möglichkeit, durch self.label.set_markup() ein Event auszulösen?
Wenn es unter Windows nicht liefe, ist das für mich nicht schlimm.
Zu deinem 2. Satz: Ich bin der Meinung, dass dem nicht so ist. Das Signal/Event wird in "MyThread" emitted, und in myGUI ist der Handler. ?
Unabhängig davon habe ich noch eine Frage:
In "myGUI" ist "label" durch "updateLabel" aktualisiert worden.
Den übermittelten Wert kann ich in updateLabel ja auch noch für meine anderen Zwecke Nutzen. Gibt es theoretisch auch die Möglichkeit, durch self.label.set_markup() ein Event auszulösen?
@Tundle:
Das mit dem Gui-Zugriff aus nur einem Thread heraus verstehe ich anders. Vielmehr scheint der zeitgleiche Zugriff über mehrere Threads nicht sicher zu sein, was mit threads_enter/leave eben abgesichert werden soll (also prinzipiell möglich ist).
Siehe hierzu: http://research.operationaldynamics.com ... eness.html
und http://research.operationaldynamics.com ... -java.html
(Von Qt her war ich davon ausgegangen, daß signal-emitting thread-safe ist, was in Gtk eben nicht der Fall ist.)
Ich bin mir des Windows-Problemes bewußt, nur spielt Gtk auf Windows für mich keine Rolle. Trotzdem hab ich mal das Script angepaßt und hätte gern mal eine Stellungnahme von Dir hierzu.
Code
Das Signal wird jetzt per idle_add() abgesetzt, ein direkter Threadzugriff erfolgt nicht mehr. Verbesserungsvorschläge?
Grüße, Jerch
Das mit dem Gui-Zugriff aus nur einem Thread heraus verstehe ich anders. Vielmehr scheint der zeitgleiche Zugriff über mehrere Threads nicht sicher zu sein, was mit threads_enter/leave eben abgesichert werden soll (also prinzipiell möglich ist).
Siehe hierzu: http://research.operationaldynamics.com ... eness.html
und http://research.operationaldynamics.com ... -java.html
(Von Qt her war ich davon ausgegangen, daß signal-emitting thread-safe ist, was in Gtk eben nicht der Fall ist.)
Ich bin mir des Windows-Problemes bewußt, nur spielt Gtk auf Windows für mich keine Rolle. Trotzdem hab ich mal das Script angepaßt und hätte gern mal eine Stellungnahme von Dir hierzu.
Code
Das Signal wird jetzt per idle_add() abgesetzt, ein direkter Threadzugriff erfolgt nicht mehr. Verbesserungsvorschläge?
Grüße, Jerch
@whaeva:
Wo das im Code steht, sagt doch rein gar nichts darüber aus, in welchem Thread das ausgeführt wird. Der Code hier wirft einen `AssertionError`, was er aber nicht tun dürfte, wenn der Handler im gleichen Thread aufgerufen würde, in dem auch die Mainloop läuft.
@jerch:
Das kommt eben auf die Plattform an. Unter X11 darf man mit mehreren Threads zugreifen, nur eben nicht gleichzeitig. Unter Windows darf man aber wohl überhaupt nicht mit mehreren Threads zugreifen. Ob man Windows unterstützen mag oder nicht, muss jeder mit sich selbst ausmachen. Ich persönlich mag jedoch schon, dass meine Programme unter Windows laufen und habe auch keine Lust, versehentlich einen Deadlock einzubauen, weshalb ich `idle_add()` benutze.
Zum Code: Sieht bis auf die ``on_window_destroy``-Methode gut aus. In `on_window_destroy()` ist jedoch die Hauptschleife von GTK+ unterbrochen und es wird gewartet, bis der Thread beendet wird, dh in der Zeit, in der der Thread noch lebt, wird die GUI nicht neu gezeichnet und das Fenster verschwindet auch nicht, wodurch sich die GUI eben "unschön" anfühlen könnte, da sie nicht gleich reagiert. Und das ``data=None`` in den Handlern ist auch überflüssig und eher irreführend. In C wird den Handlern immer noch ein Zeiger mit Userdaten übergeben (der auch "NULL" sein kann), in Python können aber noch beliebig viele Argumente folgen, nämlich die, die man beim Verbinden des Signals angegeben hat. Hat man da keine angegeben, wird auch kein weiteres übergeben.
Wo das im Code steht, sagt doch rein gar nichts darüber aus, in welchem Thread das ausgeführt wird. Der Code hier wirft einen `AssertionError`, was er aber nicht tun dürfte, wenn der Handler im gleichen Thread aufgerufen würde, in dem auch die Mainloop läuft.
@jerch:
Das kommt eben auf die Plattform an. Unter X11 darf man mit mehreren Threads zugreifen, nur eben nicht gleichzeitig. Unter Windows darf man aber wohl überhaupt nicht mit mehreren Threads zugreifen. Ob man Windows unterstützen mag oder nicht, muss jeder mit sich selbst ausmachen. Ich persönlich mag jedoch schon, dass meine Programme unter Windows laufen und habe auch keine Lust, versehentlich einen Deadlock einzubauen, weshalb ich `idle_add()` benutze.
Zum Code: Sieht bis auf die ``on_window_destroy``-Methode gut aus. In `on_window_destroy()` ist jedoch die Hauptschleife von GTK+ unterbrochen und es wird gewartet, bis der Thread beendet wird, dh in der Zeit, in der der Thread noch lebt, wird die GUI nicht neu gezeichnet und das Fenster verschwindet auch nicht, wodurch sich die GUI eben "unschön" anfühlen könnte, da sie nicht gleich reagiert. Und das ``data=None`` in den Handlern ist auch überflüssig und eher irreführend. In C wird den Handlern immer noch ein Zeiger mit Userdaten übergeben (der auch "NULL" sein kann), in Python können aber noch beliebig viele Argumente folgen, nämlich die, die man beim Verbinden des Signals angegeben hat. Hat man da keine angegeben, wird auch kein weiteres übergeben.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Danke Trundle.
Der Kritik an dem data=None nehm ich mich mal nicht an, war nur copy&paste von whaeva (zugegebenermaßen ohne Verstand lol).
ok join hinter main_quit, vllt. mit timeout. Wenn der join hier nicht mehr gelingt, liegt eh ein problem mit dem thread vor , dann kann nur noch das OS helfen.
Der Kritik an dem data=None nehm ich mich mal nicht an, war nur copy&paste von whaeva (zugegebenermaßen ohne Verstand lol).
ok join hinter main_quit, vllt. mit timeout. Wenn der join hier nicht mehr gelingt, liegt eh ein problem mit dem thread vor , dann kann nur noch das OS helfen.
Ich habe die Diskussion jetzt nur überflogen, aber hier noch meine persönliche Erfahrung zum Thema GTK und Threads unter Windows.
Threads und GTK funktionieren auch unter Windows problemlos, solange man nicht aus mehreren Threads auf die GUI Elemente zugreifen will. Und das willst du ja auch nicht, soweit ich das mitbekommen habe.
Um Threads im GTK Mainloop nutzen zu können, reicht ein
Da muss man dann auch nicht mit dem thread_enter und thread_leave arbeiten, sondern kann die Standard Threads mit den Standard Lock Funktionen nutzen, falls man in kritische Bereiche kommt.
Hier ist ein Beispiel (allerdings funktioniert die Loggerklasse so nicht)
http://www.python-forum.de/topic-17844.html
Threads und GTK funktionieren auch unter Windows problemlos, solange man nicht aus mehreren Threads auf die GUI Elemente zugreifen will. Und das willst du ja auch nicht, soweit ich das mitbekommen habe.
Um Threads im GTK Mainloop nutzen zu können, reicht ein
Code: Alles auswählen
gobject.threads_init()
Hier ist ein Beispiel (allerdings funktioniert die Loggerklasse so nicht)
http://www.python-forum.de/topic-17844.html
Manchmal ist es hilfreich, Diskussionen nicht nur zu überfliegen. Es gibt die Dokumentation von GDK, die klar sagt, dass man unter Windows besser nicht versuchen sollte, von mehreren Threads aus auf GDK-Funktionen zuzugreifen. Und zwar grundsätzlich, nicht nur nicht gleichzeitig (was hier auch von Owen Taylor bestätigt wird: "Using GTK+ from multiple threads on Windows doesn't work at all, and would require substantial work and complexity to fix"). Das gefährliche an Threads und GUI ist, dass Fehler nicht deterministisch auftreten, wodurch das mit der eigenen praktischen Erfahrung immer so eine Sache ist.
Desweiteren ist mir nicht klar, wie du nur mit eigenen Locks verhindern willst, dass nicht gleichzeitig auf die GUI zugegriffen wird. Damit verhinderst du, dass dein eigener Code in den Threads vllt nicht gleichzeitig auf die GUI zugreift. Aber dabei wird doch die Mainloop von GTK+ nicht unterbrochen. Das heißt, es wird auf Events reagiert, Signale werden ausgelöst, etc. pp. Und jetzt soll auf einmal GTK+ nicht mehr von sich aus auf die GUI zugreifen, nur weil du einen eigenen Lock hast, von dem GTK+ überhaupt nichts weiß?
Desweiteren ist mir nicht klar, wie du nur mit eigenen Locks verhindern willst, dass nicht gleichzeitig auf die GUI zugegriffen wird. Damit verhinderst du, dass dein eigener Code in den Threads vllt nicht gleichzeitig auf die GUI zugreift. Aber dabei wird doch die Mainloop von GTK+ nicht unterbrochen. Das heißt, es wird auf Events reagiert, Signale werden ausgelöst, etc. pp. Und jetzt soll auf einmal GTK+ nicht mehr von sich aus auf die GUI zugreifen, nur weil du einen eigenen Lock hast, von dem GTK+ überhaupt nichts weiß?
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)