Label im applet aktualisieren?

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

Hallo,

Da ich mich grad langweile, wollte ich mal aus spaß ein applet fürs gnome unter python schreiben. Soweit hab ichs auch fertig. Allerdings krieg ich es nicht hin das das Applet sich aktualisiert. Ich möchte das das Label jede Sekunde geändert wird. Zuletzt hab ich es mit threads versucht.
Hier mal der Factory code.
Als beispiel soll hier einfach nur eine zahl im label stehen. Fängt bei 0 an und wird pro sekunde um 1 erhöht.

Code: Alles auswählen

def factory(applet, iid):
	class count(threading.Thread):
		def __init__(self):
			threading.Thread.__init__(self)
		def run(self):
			zahl = 0
			while 1:		
				label = gtk.Label(str(zahl))				
				applet.add(label)
				applet.show_all()
				time.sleep(1)
				zahl+=1
	thread=count()
	thread.start()
	return True
aber das führt nur dazu das das Label 0 zeigt und nicht höher geht. Wie krieg ich nun das Label dazu sich zu aktualisieren?
thx.
deets

GUI & threads == schlechte idee.

Fuer sowas nimmt man einen Timer. Du kannst auch Threads nehmen, aber dann musst du einen vom Toolkit abhaengigen Weg finden, wie du GUI-updates aus anderen Threads antriggerst. Das kannst du in der Doku nachlesen (ich benutze kein GTK)
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

Threaads waren von mir jetzt nur so angedacht. Ich hab eh nicht sonderlich viel Erfahrung mit Threads.
Zuerst wollte ich ja in der Factory einfach die while 1: schleife laufen lassen aber dann hängt das halbe system und es passiert gar nichts :)

ich glaube, das warten, bzw. wie man das anstellt ist sogar noch das sekundäre problem hierbei. Primär seh ich noch keine möglichkeit wie das einmal gezeichnete Label überschrieben werden soll.
Yaso
User
Beiträge: 30
Registriert: Freitag 1. April 2011, 08:00

Schau mal hier nach:
http://unpythonic.blogspot.com/2007/08/ ... pygtk.html
Das hat mir mit Threads geholfen und dein Vorhaben sollte funktionieren.
Wobei GUI und Threads wirklich am besten zu vermeiden sind. Aber manchmal kommt man nicht drum herum:
BlackJack

In diesem Falle sollte man aber darum herum kommen mit `gobject.timeout_add()`.
lunar

@Arp: Du brauchst keine Threads. Es reicht ein Timer, der einmal in der Sekunde eine Funktion zur Aktualisierung des Labels ausführt.

Und wieso erzeugst Du jedes Mal ein neues Label, statt einfach nur mit ".set_label()" den Text des existierenden Labels anzupassen?!
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

Hi,

Das mit dem set_label klappt auch nicht.

Ich hab den code jetzt stark vereinfacht, nur um es hinzukriegen das das existierende Label von 0 auf 1 springt.

Code: Alles auswählen

def factory(applet, iid):
	label = gtk.Label(0)			
	applet.add(label)
	time.sleep(1)	
	label = gtk.Label(1)			
	applet.set_label(label)
	applet.show_all()
	return True
Seht ihr da einen offensichtlichen Fehler?
deets

Ja. Das du nicht tust, was man dir sagt: benutze Timer!

Eine GUI wird in nur *EINEM* thread aufgebaut & dargestellt. Wenn du also ein Label erzeugst, und dann einfach ne Sekunde wartest - dann passiert halt ne Sekunde nix. Und dann setzt du das um auf den neuen Wert, und *danach*, wenn der GUI-thread wieder die Kontrolle hat, weil die factory-fukntion beendet wurde, *dann* werden die Sachen dargestellt.

Darum "frieren" Programme ein, wenn man laengere Berechnungen macht, ohne zwischendurch die Kontrolle wieder an den GUI-thread zurueckzugeben - wenigstens fuer kurze Zeit, um eben updates der GUI darzustellen.
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

Achso. Dann muss ich den Timer, bzw. das warten außerhalb der def factory machen und immer wieder def factory aufrufen? Wie gesagt, das primäre Problem ist, das das Label überhaupt erstmal sich aktualisiert. Wie man nun darauf wartet sollte ja nebensächlich sein.
deets hat geschrieben:... *dann* werden die Sachen dargestellt.
Das ist übrigens in diesem Fall nicht richtig. Denn das würde bedeuten das im obigen Beispiel erstmal nichts passieren würde, und dann im Label die 1 steht. Aber es steht immer die 0 da.
deets

Jein. Du setzt den Timer, und der bekommt nen Callback. Darin updatest du das Label. Aber die Factory wird vermute ich mal ja nur *einmal* aufgerufen.

In pseudo-code (weil, wie gesagt, kein GTK-Kenner) etwa so:

Code: Alles auswählen


counter = 0
label = None

def timer_callback(timer):
      global counter, label
      counter += 1
      label.set_text(str(counter))

def factory(...):
      ...
      label = Label(...)
      add_timer(timer_callback, 1.0)
      ...

add_timer musst du natuerlich ersetzen durch das, was du da wirklich in GTK benoetigst, das wurde ja glaube ich schon gepostet.

Und der globale Zustand ist jetzt auch nicht sooo dolle, aber erstmal fuer den Anfang. Normalerweise wuerde man da wohl eher ein komplettes Objekt erzeugen in der Factory, welches dann eine Methode hat, die als callback dient. Aber das kann dann ja noch kommen.
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

hmmm....

Code: Alles auswählen

zahl = 0
label = None

def timer_callback(timer):
	global zahl,label
	zahl+=1

def factory(applet, iid):
	Label = gtk.Label(label)			
	applet.add(Label)
	applet.show_all()
	gobject.timeout_add(1000,timer_callback)	
	return True
führt jetzt dazu das kurz was aufblinkt und gar nichts angezeigt wird.
deets

Das sieht alles etwas wurstig aus.

Dein timer-callback macht ja nix. Er zaehlt hoch, na fein. Aber er setzt den Text nicht.

Und das globale label muss ein gtk.Label-objekt sein, nicht ein Text oder sowas. Das weisst du dem ja aber gar nicht zu. Ich hatte in der factory da aber auch das global vergessen.

Code: Alles auswählen


def factory(..):

     global label
     label = Label(str(counter))


Und was passiert, wenn
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

Sorry, das eine label hatte ich beim schreiben vergessen. Das macht aber auch nichts.

Code: Alles auswählen

zahl = 0
label = gtk.Label(str(zahl))

def timer_callback(timer):
	global zahl,label
	zahl+=1
	label.set_text(str(zahl))

def factory(applet, iid):
	global label		
	applet.add(label)
	applet.show_all()
	gobject.timeout_add(1000,timer_callback)	
	return True
zeigt mir nur die 0 im label an und es tut sich nichts. An der 1000 liegts nicht, da, soweit ich weiss man das da in ms statt s angeben muss.
deets

Aber das Label steht schon da? Gibt's noch ne Art redraw-Befehl?
deets

Ah, und ausserdem ist der callback falsch. Ich hab' den nur prophylaktisch mit nem timer-argument versehen, weil ich das von anderen toolkits so kenne. Aber laut Doku bekommt der keinen. Nimm also mal das Argument timer weg.
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Die Callback-Funktion muss "True" zurück geben, damit sie erneut ausgelöst wird. Außerdem kann man timeout_add() weitere Parameter übergeben, die dann auch an die Callback-Funktion durchgereicht werden. So spart man sich bspw. die globale label-Variable.

Besten Gruß,

brb
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

Danke, aber das bringts auch nicht.... scheinbar bedarf es hier wirklich einer redraw befehl da ansonsten der einmal geschriebene Text auf teufel komm raus sich nicht ändert.
BlackJack

@Arp: Also ich brauche hier keinen redraw-Befehl. Es wäre vielleicht hilfreich wenn Du mal ein funktionierendes Minimalbeispiel zeigen würdest.

Code: Alles auswählen

from itertools import count
import gobject
import gtk


def main():
    def destroy(window):
        window.hide()
        gtk.main_quit()
    
    window = gtk.Window()
    window.connect('destroy', destroy)
    
    label = gtk.Label()
    label.set_text('-')
    window.add(label)
    
    def set_label_counter(counter):
        label.set_text(str(counter.next()))
        return True
        
    gobject.timeout_add_seconds(1, set_label_counter, count())
    
    window.show_all()
    gtk.main()


if __name__ == '__main__':
    main()
Arp
User
Beiträge: 65
Registriert: Dienstag 15. März 2011, 13:21

@Blackjack

Ich glaube es macht wohl einen unterschied ob man ein gnome applet schreibt oder ein "richtiges" programm.

Das ist das letzte was ich probiert habe, und es führt nur dazu das im gnome panel eine 0 steht, und sich dann nichts tut.

Code: Alles auswählen

zahl = 0
label = gtk.Label(str(zahl))

def timer_callback():
	global zahl,label
	zahl+=1
	label.set_text(str(zahl))
	return True

def factory(applet, iid):
	global label,zahl		
	label = gtk.Label(str(zahl))
	applet.add(label)
	applet.show_all()
	gobject.timeout_add(1,timer_callback,applet)	
	return True


if __name__ == '__main__':
	print "Starting factory"	
	gnomeapplet.bonobo_factory("OAFIID:Gnome_Panel_Example_Factory", gnomeapplet.Applet.__gtype__, "Simple gnome applet example", "1.0", factory)
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Deinen Code kann man ja mit 2, 3 Änderungen so abändern, dass er ein normales Fenster erzeugt. Und dann funktioniert er immer noch nicht, was deine These in Frage stellt.

Stellen wir uns also die Frage, warum dein Code nicht funktioniert. Und da fällt die Funktion "timer_callback" ins Auge. Du über gibst "gobject.timeout_add" den Zusatz-Parameter "applet". Die Callback-Funktion kennt aber keine Parameter und schmeißt daher den Fehler

Code: Alles auswählen

TypeError: timer_callback() takes no arguments (1 given)
^CTraceback (most recent call last):
  File "tst.py", line 23, in <module>
    gtk.main()
KeyboardInterrupt
Wenn du den Fehler behebst, geht auch dein Code.

Gruß,

brb
Antworten