PyGTK: Button Klick verhalten und Prozesse

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Hallo zusammen,

ich habe da ein Problem wo ich jetzt schon seit 4 Stunden einfach nicht so richtig eine Lösung zu finde. Es ist auch nicht einfach zu erklären ich hoffe aber es gelingt mir so das ihr es verstehen könnt.

Also ich habe auf einer Oberfläche einen Button und eine Progressbar. Soweit so gut :lol: (natürlich sind noch mehr dinge vorhanden aber es geht um das zusammenspiel der beiden). Sobald der Button gedrückt wird startet eine Suchanfrage im Internet. Nun habe ich mir überlegt das es ja ganz nett wäre wenn man eine kleine Progressbar einbaut die Pulsed solange die Suchanfrage dauert.

So und nun kommts....... Das Problem ist das wenn ich den Button drücke bleibt dieser gedrückt weil ja die Suchanfrage noch arbeitet und das Pulsen startet auch erst danach wenn der Button wieder loslässt aber dann ist ja der eigentliche Gag vorbei. Dann soll die Bar ja gar nicht mehr Pulsen.

Hier mal ein bisschen Beispiel Code ist aber wirklich nur Pseudo damit ihr ein bisschen ein Bild davon bekommt.

def on_click_search:
pulsen
suchanfrage
pulsen ende

Ich habe schon verschiedenste varianten probiert die PyGtk her gibt. Die da wären.

gobject.timeout(), gobject.idle_add; gtk.gdk.threads_enter() u. leave()

und Python os.fork() <- führt aber in schwere Fehler

Also wie gesagt ich will das das Pulsen nur solange läuft wie die Suchanfrage dauert.

Kann mir vielleicht jemand dabei helfen ..... sonst lass ich den Effekt einfach weg wenn das nicht möglich ist.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Mit ``gobject.idle_add`` eine die Suche starten und die Progressbar auf pulsierend stellen. Wenn die Funktion die die Suche ausführt fertig ist, von dieser die Progressbar zurückstellen lassen.

Edit: Thread verschoben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Ich habe in meinen gtk helpern die folgende Funktion:

Code: Alles auswählen

def gtk_yield():
    """process all the peding events in the mainloop"""
    while gtk.events_pending():
         gtk.main_iteration()
Versuch mal diese Funktion aufzurufen nach dem du die Progressbar auf pulsierend gesetzt hast. Könnte funktionieren. :wink:
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

erst mal danke euch beiden für eure Antworten. Puhh ich bin froh das jemand das verstehen konnte was ich meine.... :lol:

Ok also ich versuche gerade mit deiner Funktion zu arbeiten veers. Aber leider funktioniert diese auch so das erst wenn die suche zu ende ist und der Button los lässt geht das Pulsieren los.

Hier mal der Code ausschnitt:

Code: Alles auswählen

def on_search(self, data):
		gobject.timeout_add(100,self.bar_searching)
		self.gtk_yield()
		text = self.search_entry.get_text() 
		self.aur.clear_information()        
		self.aur.search(text)
		
		if self.aur.is_package():          
			self.on_information()
		else:
			self.no_pack_dialog()
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Veers könntest du mir vielleicht sagen ob du das so gemeint hast. Oder gibts es vielleicht noch eine andere Methode.

Gruß

Treehouse
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Die `bar_searching`-Methode könnte eventuell noch solch einen `gtk_yield`-Aufruf vertragen. Prinzipiell eben überall dort, wo du die GUI veränderst und "blockierst", also nicht sofort zur gtk-Hauptschleife zurückspringst.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Ok ich werde da heute abend mal ausprobieren und dann sage ich bescheid bin mal gespannt ob es daran gelegen hat.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Hallo,

so ich habe die Funktion jetzt mal auch in die bar_searching Methode gesetzt. Aber leider klappt es so immer noch nicht. :(
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Treehouse hat geschrieben:Aber leider klappt es so immer noch nicht.
Ohne Code ist das ein einziges Ratespiel.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Das da oben ist der Code den der Button ausführt und die Bar hat diesen Code nichts dolles soll halt nur ein bisschen Pulsen.

Code: Alles auswählen

	def bar_searching(self):
		self.gtk_yield()
		self.progressbar.set_text('Searching...')
		self.progressbar.pulse()
		return True
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Das ``self.gtk_yield()`` muss nach dem ``self.progressbar.pulse()`` stehen. Aber die `bar_searching`-Methode wird wahrscheinlich eh erst aufgerufen, nachdem die Suche bereits vorbei ist.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Ja auch wenn ich es so schreibe wie du es sagst geht es leider nicht. Ich bastel schon die ganze Zeit mir nen Wolf wie ich das hinbekomme das es vorher läuft. Aber leider komme ich keinen Schritt weiter. Das einzige was geklappt hat war ein fork aber danach bekomme ich so schwere Fehler das das Programm unbrauchbar ist.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Hat vielleicht noch irgend jemand eine Idee wie ich es anstellen könnte das es funktioniert?

Meine vermutung ist das ich irgendwie das Suchen und das Pulsen als Subprocess laufen lassen muss. So das der Button früher los läßt und das Pulsen überhaupt anzeigt.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Deine `on_search`-Methode wird beim Klicken auf den Button von gtk aufgerufen. Solange jetzt diese Methode ausgeführt wird, reagiert gtk nicht auf Events, löst keine aus und zeichnet die GUI auch nicht neu. Das bedeutet auch, dass solange keine mit `gobject.timeout_add` registrierten Timeout-Funktionen aufgerufen werden, folglich deine `bar_searching`-Methode nie aufgerufen wird.

Du könntest die Suche in einen Thread auslagen, dabei ist aber zu beachten, dass die wenigstens GUI-Toolkits, und dazu gehört auch gtk, es mögen, wenn man mit mehr als einem Thread auf die GUI zugreift. Du müsstest also immer vom gleichen Thread aus auf die GUI zugreifen.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Danke erst mal für deine Antwort. Vielleicht klingt das jetzt ja total doof aber wie macht man sowas? :oops:

Vielleicht kannst du mir ja ein kleines Beispiel geben
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Meinst du das so Trundle ? :

Code: Alles auswählen

	def on_search(self, data):
		text = self.search_entry.get_text() 
		self.aur.clear_information()       
		gtk.gdk.threads_enter()
		self.aur.search(text)
		gtk.gdk.threads_leave()
		if self.aur.is_package():          
			self.on_information()
		else:
			self.no_pack_dialog()
Falls du das so meinst, muss ich leider sagen das das Programm so hängt. Es friert ein.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Mit dem `[mod]threading[/mod]`-Modul kann man neue Threads starten. Das könnte dann vllt so irgendwie aussehen:

Code: Alles auswählen

from threading import Thread

def search_function(text):
    # Anstatt search_function eben die richtige Suchfunktion
    import time
    time.sleep(10)

def on_search(self, data):
    text = self.search_entry.get_text()
    search = Thread(target=search_function, args=[text])
    search.start()
    gobject.timeout_add(100, lambda: self.progressbar.pulse() or search.isAlive())
Bevor man dann `gtk.main` aufruft, sollte man dann noch `gtk.gdk.threads_init` aufrufen.
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

huii das sieht super aus ich teste das mal eben an ob es so geht.

Ich danke dir aber schonmal für deine Mühe
Treehouse
User
Beiträge: 39
Registriert: Freitag 14. Dezember 2007, 00:40

Hey super es klappt !!! :wink:

Ein Problem noch...... ich muss warten darauf das die Suche beendet ist. Weil je nachdem ob die Suche erfolgreich war folgen Funktionen.
Ich habe es schon mit os.wait() probiert aber das ist nur für forks wie mir scheint.
Ist es nun auch möglich auf den Thread zu warten ?
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

Das ginge schon (im konkreten Fall mit ``search.join()``). Aber dann wäre ja genau wieder das Problem, dass die GUI "blockiert" wird (eben weil die `on_search`-Methode erst nach geraumer Zeit wieder zurückkehrt) und du könntest den Thread auch einfach weglassen. Einfach die Funktionen im Suche-Thread aufrufen, nach der Suche eben. Jedoch dürfen die Funktionen dann nicht direkt auf die GUI zugreifen (weil anderer Thread), sondern müssen dies z.B. über `gobject.idle_add` tun.

Beispiel (angenommen, das steht in einer Klasse):

Code: Alles auswählen

def suche(self, word):
    # Such-Code eben
    # Jetzt soll das ergebnis angezeigt werden
    ergebnis = 'Spam'
    gobject.idle_add(self.ausgabe, ergebnis)

def ausgabe(self, ergebnis):
    # Hier könnte man jetzt auf die GUI zugreifen
    self.resultentry.set_text(ergebnis)
Antworten