Veränderungen nach gtk.main() durch anderen Thread

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

habe nun vor ein GUI zu bauen und möchte das aus einem anderen Thread heraus verändern.
Die Funktionen zum Ändern habe ich auch schon erstellt und ich kann sie auch aufrufen.
Das Problem ist nur, die Änderungen wirken sich nicht auf das GUI aus.
Kann ich das GUI irgendwie refreshen oder so etwas in der Art?
Oder geht das irgendwie anders?
Bin über jeden Tipp dankbar.

Viele Grüße
Maxi
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Vielleicht magst du ja etwas Code zeigen, damit man auch sieht, was du machst. Im Allgemeinen gibt es bei GUIs und Threads doch einiges zu beachten, ich bin mir sicher, dass die Forensuche da einige Beiträge finden dürfte.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

der Code ist etwas lang, ich breche ihn mal herunter.

Code: Alles auswählen

class MainWindow():
    #...
    def show(self):
        self.mainWindow.show()
    #...

class MainThread():
    def __init__(self, interface):
        self.mainWindow = interface
    #...
    def run(self):
        self.mainWindow.show()
    #...

interface = MainWindow()
thread = MainThread(interface)
thread.start()
In einem zusätzlichem Thread läuft auch noch gtk.main().
Natürlich sind die eigentlichen Funktionen viel komplexer, doch ist es hier das gleiche Problem - interface.show() wird ausgeführt und die Eigenschaft des Fensters auf show gestellt, doch es wird einfach nicht angezeigt.
Wenn ich jetzt aber zwei Fenster habe und z.B. bei Klick auf das X des einen Fensters als Callback interface.show() auslöse geht es, das Fenster wird sichtbar.

Viele Grüße
Maxi
Andyh
User
Beiträge: 319
Registriert: Dienstag 8. Januar 2008, 19:52
Kontaktdaten:

Hallo

Code: Alles auswählen

import gtk
import gobject
import threading
import time

gobject.threads_init()

class MainWindow():
    def __init__(self):
        self.mainWindow = gtk.Window()
        self.label = gtk.Label("hallo das da unten ist ein Button")
        self.button = gtk.Button("ok gefunden")
        
        self.layout = gtk.VBox()
        self.mainWindow.add(self.layout)
        
        self.layout.pack_start(self.label)
        self.layout.pack_start(self.button)
        
    def show(self):
        self.mainWindow.show_all()
    
    def change_text(self, new_text):
        self.label.set_text(new_text)
        
class MainThread(threading.Thread):
    def __init__(self, interface):
        threading.Thread.__init__(self)
        self.mainWindow = interface

    def run(self):
        gtk.gdk.threads_enter()
        self.mainWindow.show()
        gtk.gdk.threads_leave()
        time.sleep(5)
        gtk.gdk.threads_enter()
        self.mainWindow.change_text("hallo neuer text")
        gtk.gdk.threads_leave()

interface = MainWindow()
thread = MainThread(interface)
thread.start()
gtk.main()
Gruß
Andyh
Meinen Dickschädel schon bemerkt?
Ich bin jetzt FACHARBEITER (Zerspanungsmechaniker)!!!
[code]import sys

if sys.platform == "win32":
print "this program only runs on operating systems!!!"
sys.TotalError()[/code]
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

sorry, dass ich mich so lange nicht mehr gemeldet habe - Ich hatte viel zu tun.

Dein Beispiel funktioniert bei mir nicht.
Main Rechner hängt sich einfach auf und ich konnte ihn nur noch ausschalten.
Mittlerweile bin ich von Thread auf Multiprogressing umgestiegen.
Hier bekomme ich folgenden Fehler:

Code: Alles auswählen

python: ../../src/xcb_io.c:242: process_responses: Assertion `(((long) (dpy->last_request_read) - (long) (dpy->request)) <= 0)' failed.
Viele Grüße
Maxi
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Unter Windows funktioniert das nicht. Da kannst du nicht die GTK-Widgets aus einem anderen Thread verändern (außer aus Threads, die durch gobject gestartet wurden, vielleicht kommt das für dich auch in Frage).

Prinzipiell entspricht dein Vorgehen aber auch einem schlechten Prinzip. Schau dir mal das MVC-Pattern an. Dann genügt es, wenn du die Daten aus dem anderen Thread veränderst und die GUI aktualisiert diese regelmäßig.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,
Unter Windows funktioniert das nicht.
Habe ich auch nicht benutzt ;).
(außer aus Threads, die durch gobject gestartet wurden, vielleicht kommt das für dich auch in Frage)
Nein, da das Programm auch ohne GTK laufen soll - das GUI ist eigentlich eine Art AddOn.
Prinzipiell entspricht dein Vorgehen aber auch einem schlechten Prinzip. Schau dir mal das MVC-Pattern an. Dann genügt es, wenn du die Daten aus dem anderen Thread veränderst und die GUI aktualisiert diese regelmäßig.
Eigentlich ist es so ähnlich.
Es geht um eine Netztwerk Anwendung, wenn die Verbindung getrennt wurde, soll das angezeigt werden.
Dazu ruft mein Programm eine Funktion im Controller auf, die wiederum eine Funktion im GUI auslöst.
Die Aktion ist ein show auf ein in Glade definiertes Fenster mit einer Fehlermeldung.
Naja, und eben dieses Fenster wird nicht angezeigt.

/* Edit:
Im Grunde funktioniert es so wie: http://de.wikipedia.org/wiki/Beobachter ... _in_Python
Bis darauf, dass der Variable kein _ vorangestellt ist.
Und dass es nur einen Überwacher gibt, der Controller.
*/

Viele Grüße
Maxi
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

maxi_king_333 hat geschrieben:
(außer aus Threads, die durch gobject gestartet wurden, vielleicht kommt das für dich auch in Frage)
Nein, da das Programm auch ohne GTK laufen soll - das GUI ist eigentlich eine Art AddOn.
Aber laut deiner Beschreibung läuft doch schon der GTK Loop?

Ich sehe anhand dieser (zweiten) Beschreibung gar keinen sinnvollen Zusammenhang zum Ausgangsproblem... Vielleicht kannst das alles nochmal zusammenfassen.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

Aufbau des Programms:
  • Netzwerk Monitor
  • Controller
  • GUI
Start des Programms:
Der Controller startet das GUI.
gtk.main wird über Multiprocessing gestartet.
Das GUI erscheint und nun kann man über es den Netzwerk Monitor starten.
Dieser wartet nun im Hintergrund auf Befehle.
Sollte die Verbindung zwischen Netzwerk Monitor und dem anderen Computer abbrechen, dann löst der Monitor im Controller eine Funktion aus.
Diese manipuliert wiederum das GUI - Sie stellt das vorbereitete Error Fenster auf sichtbar und sorgt dafür, dass ich wieder auf den Verbinden Button klicken kann.
Es funktioniert alles auch soweit, nur die Änderungen werden nicht sichtbar.
Rufe ich aber die gleiche Funktion über z.B. das Pressed Event eines Buttons auf funktioniert es ohne Probleme.

Viele Grüße
Maxi
Andyh
User
Beiträge: 319
Registriert: Dienstag 8. Januar 2008, 19:52
Kontaktdaten:

Hallo

Lass doch mal ein bisschen Code sehen.
Der von der gui und die stelle im Controller die die gui anzeigt/verändert.

Gruß
Andyh
Meinen Dickschädel schon bemerkt?
Ich bin jetzt FACHARBEITER (Zerspanungsmechaniker)!!!
[code]import sys

if sys.platform == "win32":
print "this program only runs on operating systems!!!"
sys.TotalError()[/code]
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

hier ist der Code von dem Haupt Fenster und der Initialisierung.

Code: Alles auswählen

import gtk
import gtk.glade
import gtk.gdk

class InterfaceMain():
    def __init__(self, api):
        self.api = api # Der Controller

    def start(self):       
        self.mainWindow = MainWindow(self)
        gtkmainProcess = Process(target=gtk.main, args=())
        gtkmainProcess.start()    
       
    def stop(self):
        gtk.main_quit()

class MainWindow():
    def __init__(self, interface):
        self.interface = interface
        self.gladefile = "./interface/mainWindow.glade"
        self.main = gtk.glade.XML(self.gladefile)
        self.mainWindow=self.main.get_widget("MainWindow")
        
    def show(self): # Wird von dem Controller aufgerufen.
        self.mainWindow.show()
        
    def hide(self): # Wird von dem Controller aufgerufen.
        self.mainWindow.hide()
Viele Grüße
Maxi
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Bitte achte in Zukunft darauf, Probleme möglichst genau zu beschreiben und möglichst auf ein Minimum zu reduzieren, so dass du dann *ausführbaren* Code zeigen kannst, mit dem man das Problem reproduzieren kann.

Bei deinem ersten Post sprichst du noch von Threads -- in deinem letzten Beispiel sind aber Prozesse zu sehen, keine Threads. Das ist ein Unterschied. Du startest die Gtk-Hauptschleife in einem anderen Prozess -- warum?
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

wie ich schon sagte bin ich von Thread auf Multiprocessing umgestiegen.
maxi_king_333 hat geschrieben:Hi,

sorry, dass ich mich so lange nicht mehr gemeldet habe - Ich hatte viel zu tun.

Dein Beispiel funktioniert bei mir nicht.
Main Rechner hängt sich einfach auf und ich konnte ihn nur noch ausschalten.
Mittlerweile bin ich von Thread auf Multiprogressing umgestiegen.
Hier bekomme ich folgenden Fehler:

Code: Alles auswählen

python: ../../src/xcb_io.c:242: process_responses: Assertion `(((long) (dpy->last_request_read) - (long) (dpy->request)) <= 0)' failed.
Viele Grüße
Maxi
Ganz einfach deshalb, weil ich es besser finde, wenn die Prozesse vom Betriebssystem verwaltet werden.
Außerdem wollte die Libnotify Bibliothek nicht mir Threads funktionieren.
Trundle hat geschrieben:Bitte achte in Zukunft darauf, Probleme möglichst genau zu beschreiben und möglichst auf ein Minimum zu reduzieren, so dass du dann *ausführbaren* Code zeigen kannst, mit dem man das Problem reproduzieren kann.

Code: Alles auswählen

from multiprocessing import Process
import time
import gtk
import gtk.glade

class InterfaceMain():
    def __init__(self):
        self.mainWindow = MainWindow(self)
        self.mainWindow.show()
        gtkmainProcess = Process(target=gtk.main, args=())
        gtkmainProcess.start()   
       
    def stop(self):
        gtk.main_quit()

class MainWindow():
    def __init__(self, interface):
        self.interface = interface
        self.gladefile = "./interface/mainWindow.glade"
        self.main = gtk.glade.XML(self.gladefile)
        self.mainWindow=self.main.get_widget("MainWindow")
       
    def show(self): # Wird von dem Controller aufgerufen.
        self.mainWindow.show()
       
    def hide(self): # Wird von dem Controller aufgerufen.
        self.mainWindow.hide()

print "Show"
mainInterface = MainInterface()
print "Sleep"
time.sleep(10)
print "Hide"
mainInterface.mainWindow.hide()
Glade File gibt es hier: http://pastebin.com/m3bff74

Viele Grüße
Maxi
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Ja, meine Frage war wohl eher, warum eigentlich Prozesse. Jedenfalls kannst du nicht einfach die Gtk-Hauptschleife in einem anderen Prozess laufen lassen. Das heißt, können schon, bringt dir aber nichts, weil dein ursprünglicher Prozess auch eine Hauptschleife braucht. Oder alle GUI-Manipulationen müssen im neuen Prozess stattfinden.
Du solltest dir im klaren darüber sein, dass die beiden Prozesse so gesehen nichts miteinander zu tun haben.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Ja, meine Frage war wohl eher, warum eigentlich Prozesse. Jedenfalls kannst du nicht einfach die Gtk-Hauptschleife in einem anderen Prozess laufen lassen. Das heißt, können schon, bringt dir aber nichts, weil dein ursprünglicher Prozess auch eine Hauptschleife braucht.
Ja... Hat er ja, in dem Fall time.sleep(10) und in meiner richtigen Anwendung wird der Prozess über eine Klasse initialisiert welche eine Funktion main_loop() hat, die jeden laufenden Prozess aufruft und wartet bis er beendet ist.
Nötig ist das, weil gtk.main() nicht die Hauptschleife sein darf, da wie schon gesagt, das Programm auch ohne gtk funktionieren soll.
´Oder alle GUI-Manipulationen müssen im neuen Prozess stattfinden.
Du solltest dir im klaren darüber sein, dass die beiden Prozesse so gesehen nichts miteinander zu tun haben.
Dann liegt es bestimmt da dran.
Wie kann ich sie denn im neuen Prozess ausführen?
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Ich würde dir dringend zu irgendeiner Form von IPC (z.B. xmlrpc ist ganz nett und relativ simpel, umfangreichere und ausgereiftere Möglichkeit wäre u.a. noch Pyro) raten und die Prozesse getrennt verwalten und starten.

Das, was du da geplant hast, lässt sich (wenn überhaupt) dann nur sehr umständlich lösen und ist IMHO auch kein schöner Ansatz.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

Du meinst doch: http://docs.python.org/library/xmlrpclib.html
So wie ich das sehe, läuft da die Kommunikation übers Netzwerk und das finde ich eigentlich nicht gut.
Ich werde es mir trotzdem mal ausprobieren und mich wieder melden.

Vielen Dank und Viele Grüße
Maxi
Andyh
User
Beiträge: 319
Registriert: Dienstag 8. Januar 2008, 19:52
Kontaktdaten:

Hallo

Ich habe seit neuestem den dbus für mich entdeckt, ist auch ganz simpel.

http://dbus.freedesktop.org/doc/dbus-py ... orial.html

Gruß
Andyh
Meinen Dickschädel schon bemerkt?
Ich bin jetzt FACHARBEITER (Zerspanungsmechaniker)!!!
[code]import sys

if sys.platform == "win32":
print "this program only runs on operating systems!!!"
sys.TotalError()[/code]
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

das Ding scheint ganz gut zu sein, ist aber nicht Teil er Standard Bibliothek und unter Windows bekomme ich nur schwer zum laufen (so wie ich das sehe).
Ich benutzte zwar kein Windows aber ich habe extra Python als Sprache gewählt, weil sie unter verschiedenen Plattformen läuft und ich so mein Programm auch den Windows Usern zugänglich machen kann.
xmlrpc läuft übers Netzwerk und deshalb möchte ich es eigentlich auch nicht verwenden und Pyro ist wieder nicht Standard.
Habe jetzt aber eine Idee, wenn es wirklich daran liegt, dass gtk.main() in einem Anderen Prozess läuft, dann starte ich einfach mein ganzes Interface als Prozess und kann dort dann direkt gtk.main() als Hauptschleife laufen lassen.

Viele Grüße
Maxi
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Und was ist das Problem mit dem Netzwerk?

Ansonsten könntest du es auch noch mit Pipes (über subprocess) machen, aber das wäre wesentlich komplexer.
Antworten