progressbar und pulse

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
weißned
User
Beiträge: 36
Registriert: Freitag 26. Februar 2010, 21:42

Hi Leute,

ich möchte gerne eine progressbar und der Funktion pulse() verwenden. Wenn ich die Methode aufrufe, wird der Balken ja aber nur einmal weiter gerückt, und wandert nicht die ganze Zeit hin und her so wie ich es zB aus Ubuntu gewohnt bin. Die Frage ist jetzt, gibt es eine Möglichkeit diesen Balken automatisch immer weiter wandern zu lassen? Ich möchte eigentlich nicht immer die pulse() Funktion aufrufen, ich finde das eigentlich etwas störend. Ich glaube auch das die Dialoge bei Ubuntu das nciht tuen, da der Balken sich dort sehr gleichmäßig bewegt.

Freue mich schon auf Antworten!

MFG, weißned
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Das ist doch totaler Blödsinn. Warum eine Fortschrittsanzeige benutzen, wenn du eh keinen Fortschritt anzeigen kannst? Verarsch doch deine Nutzer nicht :)

Wenn es sein muss: GTK bietet an, bestimmten Code nach N Millisekunden aufzurufen (Timeouts).
lunar

@Dauerbaustelle: Es zeigt dem Benutzer wenigstens an, dass die Anwendung etwas tut. Das ist für den Benutzer durchaus wichtig zu wissen, und zudem dauert eine Wartezeit gefühlt weniger lang, wenn man etwas beim Arbeiten zusehen kann.
BlackJack

@weißned: Lies mal die Dokumentation zu gtk.ProgressBar.puls, dann glaubst Du vielleicht, dass andere Anwendungen die Funktion auch immer wieder aufrufen (müssen!) um den Balken am Laufen zu halten.

@Dauerbaustelle: Fortschritt anzeigen zu wollen und zu wissen wie weit man fortgeschritten ist, sind ja zwar paar verschiedene Schuhe. Den Benutzer mit einem "pulsierenden" Balken darüber zu informieren, dass das Programm etwas tut was länger dauert, würde ich nicht als verarschen bezeichnen. Die Alternative ist doch, dass der Benutzer denkt das Programm ist abgestürzt, wenn sich nichts mehr tut.
weißned
User
Beiträge: 36
Registriert: Freitag 26. Februar 2010, 21:42

Weils lässig aussieht? Weil ichs kann?
Ne eigentlich finde ich es ganz praktisch, da ich am Anfang nicht weiß wie lange es dauert bis zB ein Prozess beendet wurde. Mit dem pulse() kann ich dem User aber symbolisieren, das die Anwendung gerade etwas macht. Ich könnte natürlch auch einfach ein Label nehmen wo Bitte warten steht, aber wieso soll ich so etwas nicht nutzen?

Und wenn man so eine Funktion eingebaut hat, kann sie eigentlich gar nicht so daneben sein.

Wie meinst du das mit Timeouts? Aber jetzt wo du es sagst, könnte man es mit Timern machen :D

edit:
@BlackJack: Okay ja ich glaube des schon, aber irgendwie finde ich das bisschen doof. Wenn ich wirklich nen Prozess am laufen habe, dann kann ich das ja eigentlich gar nicht immer aufrufen >.<

Ich hab mal in den GDebi Source geschaut, blicke aber nicht wirklich durch. Die rufen pulse() aber auch nur 3 mal auf, und der Balken wandert aber öfter als 3 mal denke ich ^^
BlackJack

@weißned: Wenn Du `pulse()` nicht aufrufst, sondern Dein Code an der Stelle etwas längeres anderes macht, dann friert die GUI komplett ein und sieht für den Benutzer (und das Betriebssystem) so aus, als wenn das Programm abgestürzt ist. Irgendwie musst Du ja regelmässig die GUI-Hauptschleife anstossen.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Das war doch irgendwas mit ``gtk.idle_add`` oder so.
weißned
User
Beiträge: 36
Registriert: Freitag 26. Februar 2010, 21:42

@BlackJack:
Hmm, naja eigentlich brauche ich doch sowieso einen Hintergrund Thread oder nicht? Ich hatte mir das so gedacht, wie ich es aus Java und Swing kenne. Ich setze set_pulsing(True), dann mache ich meinen Langwierigen Prozess in einem andern Thread, wenn ich damit fertig bin mache ich set_pulsing(False) und bin fertig. Die Progressbar lässt dann den Balken einfach von alleine hin und her wandern.

@Dauerbaustelle:
Ja das braucht man um die GUI von einem anderen Thread aus zu aktualisieren.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Ah, ich meinte ``gtk.timeout_add``.

Code: Alles auswählen

~ PAGER=cat python -c 'help("gobject.timeout_add")'
Help on built-in function timeout_add in gobject:

gobject.timeout_add = timeout_add(...)
    timeout_add(interval, callable, user_data=None,
                priority=None) -> source id
      callable receives (user_data)
    Sets a callable be called repeatedly until it returns False.
weißned
User
Beiträge: 36
Registriert: Freitag 26. Februar 2010, 21:42

Dauerbaustelle hat geschrieben:Ah, ich meinte ``gtk.timeout_add``.

Code: Alles auswählen

~ PAGER=cat python -c 'help("gobject.timeout_add")'
Help on built-in function timeout_add in gobject:

gobject.timeout_add = timeout_add(...)
    timeout_add(interval, callable, user_data=None,
                priority=None) -> source id
      callable receives (user_data)
    Sets a callable be called repeatedly until it returns False.
Achso okay, werde ich mir mal ansehen. Aber wie returne ich dann False? ^^

Ist es nicht einfacher einen Timer zu starten, und den dann wenn die Operation fertig ist wieder zu stoppen?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Indem du z.B. in einer Instanzvariable speicherst, ob das Zeug fertig ist.

Das ist ja im Prinzip das Gleiche wie ein Timer, eben mit GTK-Bordmitteln.
weißned
User
Beiträge: 36
Registriert: Freitag 26. Februar 2010, 21:42

Achso, okay. Ist es besser den gtk Sachen zu verwenden oder kann man auch einfach Sachen aus der Standard Bibliothek nutzen?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Klar kann man. Ich weiß jetzt auch nicht wirklich, ob ein zusätzlicher Timer-Thread irgendwie einen großen Einfluss hat, aber ich würde der Einfachkeit halber die GTK-Bordmittel nutzen.
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Prinzipiell kann man das auch recht leicht als Klasse realisieren - dann hat man die Instanzvariable und die Callback-Funktion von timeout_add() nicht in seiner Haupt-GUI-Klasse rumfliegen. Außerdem ist das ja echt eine Sache, die man immer wieder mal braucht.

Code: Alles auswählen

import gtk
import gobject

class PulseBar(gtk.ProgressBar):
    def __init__(self, interval=120):
        gtk.ProgressBar.__init__(self)
    
        self.interval = interval
        self._pulsing = False
    
    def switch_pulsing(self):
        if self._pulsing:
            self.stop_pulsing()
        else:
            self.start_pulsing()
    
    def start_pulsing(self):
        self._pulsing  = True
        gobject.timeout_add(self.interval, self.do_pulse)
    
    def do_pulse(self):
        self.pulse()
        return self._pulsing
    
    def stop_pulsing(self):
        self._pulsing = False

Code: Alles auswählen

window = gtk.Window()
vbox = gtk.VBox()
window.add(vbox)
pulsebar = PulseBar()
vbox.pack_start(pulsebar)
btn = gtk.Button("Start/Stop")
pulsebar.set_text("hallo")
pulsebar.set_pulse_step(0.2)
btn.connect("clicked", lambda widget: pulsebar.switch_pulsing())
vbox.pack_start(btn)
window.show_all()
gtk.main() 
Besten Gruß,

brb
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

``stop_pulsing`` würde man dann eher so implementieren, dass der Timer (``timeout_add``) beendet wird (``timeout_add`` gibt eine ID zurück).
Barabbas
User
Beiträge: 349
Registriert: Dienstag 4. März 2008, 14:47

Ja, stimmt, habe auch erst überlegt, das mit source_remove() zu machen. Das hat zudem den Vorteil, das tatsächlich sofort beendet wird und nicht - wie bei mir jetzt - noch ein weiterer Puls durchlaufen wird. Fand es jetzt aber auch nicht so dramatisch. Zudem bräuchte man für die Timeout-ID ebenfalls eine Instanzvariable, so dass ich jetzt nicht den Eindruck hatte, dass das eine sonderlich "besser" ist. Letztlich habe ich die jetzige Variante aber nur aus "Gewohnheit" so gemacht, so dass dein Einwand durchaus willkommen ist :)

Besten Gruß,

brb
weißned
User
Beiträge: 36
Registriert: Freitag 26. Februar 2010, 21:42

Okay das hilft mir. Danke euch allen.
Antworten