Seite 1 von 2

PyGTK: Button Klick verhalten und Prozesse

Verfasst: Dienstag 18. März 2008, 00:14
von Treehouse
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.

Verfasst: Dienstag 18. März 2008, 10:48
von Leonidas
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.

Verfasst: Dienstag 18. März 2008, 11:34
von veers
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:

Verfasst: Dienstag 18. März 2008, 23:00
von Treehouse
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()

Verfasst: Donnerstag 20. März 2008, 13:10
von Treehouse
Veers könntest du mir vielleicht sagen ob du das so gemeint hast. Oder gibts es vielleicht noch eine andere Methode.

Gruß

Treehouse

Verfasst: Donnerstag 20. März 2008, 15:17
von Trundle
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.

Verfasst: Donnerstag 20. März 2008, 15:54
von Treehouse
Ok ich werde da heute abend mal ausprobieren und dann sage ich bescheid bin mal gespannt ob es daran gelegen hat.

Verfasst: Donnerstag 20. März 2008, 21:33
von Treehouse
Hallo,

so ich habe die Funktion jetzt mal auch in die bar_searching Methode gesetzt. Aber leider klappt es so immer noch nicht. :(

Verfasst: Donnerstag 20. März 2008, 22:20
von Trundle
Treehouse hat geschrieben:Aber leider klappt es so immer noch nicht.
Ohne Code ist das ein einziges Ratespiel.

Verfasst: Donnerstag 20. März 2008, 22:22
von Treehouse
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

Verfasst: Donnerstag 20. März 2008, 22:27
von Trundle
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.

Verfasst: Donnerstag 20. März 2008, 22:44
von Treehouse
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.

Verfasst: Freitag 21. März 2008, 19:40
von Treehouse
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.

Verfasst: Freitag 21. März 2008, 20:24
von Trundle
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.

Verfasst: Freitag 21. März 2008, 20:37
von Treehouse
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

Verfasst: Freitag 21. März 2008, 21:20
von Treehouse
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.

Verfasst: Freitag 21. März 2008, 21:23
von Trundle
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.

Verfasst: Freitag 21. März 2008, 21:31
von Treehouse
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

Verfasst: Freitag 21. März 2008, 21:53
von Treehouse
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 ?

Verfasst: Freitag 21. März 2008, 22:12
von Trundle
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)