gtk thread Problem

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Poe
User
Beiträge: 6
Registriert: Dienstag 4. März 2008, 10:10

Hallo allezusammen,

ich habe folgendes Problem. Ich habe eine Klasse zum zählen von Sekunden. Dabei ist in der run() jeweils eine Sekunde Pause dann wird ein counter erhöht und das ganze mittels gtk.gdk.threads_enter() und gtk.gdk.thread_leave() auf einer GUI aktualisiert.

Desweiteren habe ich einen button, der, je nach bedarf einmal start oder pause anzeigt. Wenn der zähler zählt zeigt der Button Pause an, wenn der Zähler nicht zählt zeigt er Start an.

Klappt alles eigentlich super, wenn jetzt aber öfters auf den start/pause button drücke verzögert sich der timer. Er bleibt quasi stehen, der button ist mit farbe unterlegt, also noch gedrückt und umso länger das programm läuft desto größer wird die verzögerung. Nicht so toll, wenn man will das der Zähler jetzt weiterzählt aber erst 20 sekunden brauch um überhaupt wieder anzulaufen.

Ich schreibe das ganze für Maemo auf einem Nokia N810 sollte aber keinen unterschied machen. gtk.gdk.threads_init() wurde auch vor gtk.main aufgerufen.

anbei ein paar Code schnipsel

Code: Alles auswählen

        ...
        # Der Start/Pause Button
        self.buttonTimeStart = gtk.Button("Start")
        
        self.buttonTimeStart.connect("clicked", self.startTimer, None)
        ...

        # Die Methoden dazu
    def startTimer(self, widget, event, *args):
        self.buttonTimeStart.connect("clicked", self.pauseTimer, None)
        self.buttonTimeStart.set_label("Pause")
        
        self.clockThread.go()
    
    def pauseTimer(self, widget, event, *args):
        self.buttonTimeStart.connect("clicked", self.startTimer, None)
        self.buttonTimeStart.set_label("Start")
        
        self.clockThread.pause()

        ...

        # Und die Klasse für den Thread
class timeThread(threading.Thread):
    def __init__(self, timeLabel, window):
        threading.Thread.__init__(self)
        self.clock = 0;
        self.timeLabel = timeLabel
        self.window = window
        self.oldState = 0
        self.state = 0 # 0 = Pause, 1 = Running, 2 = Reset
    
    def setState(self, state) :
        self.state = state
        
    def repaintLabel(self):
        minInt = self.clock / 60
        if minInt < 10 :
            minStr = "0" + str(minInt)
        else :
            minStr = str(minInt)
                    
        sekInt = self.clock - (60 * minInt)
        if sekInt < 10 :
            sekStr = "0" + str(sekInt)
        else :
            sekStr = str(sekInt)
                    
        self.timeLabel.set_markup("<span size=\"xx-large\" weight=\"bold\">"+minStr+":"+sekStr+"</span>")
        
    def step(self, value):

        if (self.clock+value < 0) :
            self.clock = 0
        else :
            if (self.clock+value >= (60*60)) :
                self.clock = 60*60
            else:
                self.clock = self.clock + value
        self.state = 0 
        
    def run(self):
        while 1:
            if self.state == 1 :
                sleep(1)                
                self.clock = self.clock + 1
                gtk.gdk.threads_enter()
                self.repaintLabel()
                gtk.gdk.threads_leave()               

            else:
                sleep(1)                
                gtk.gdk.threads_enter()
                self.repaintLabel()
                gtk.gdk.threads_leave()               
                
    def pause(self):
        self.state = 0
            
    def reset(self):
        self.state = 2
        self.clock = 0
        self.timeLabel.set_markup("<span size=\"xx-large\" weight=\"bold\">00:00</span>")
        self.state = 0
        
    def go(self):
        self.state = 1
Wäre echt super wenn mir jemand ne Info geben könnte. Vielen Danke im vorraus
BlackJack

@Poe: Mal ganz abgesehen von irgendwelchen GUI-Problemen: ``sleep(1)`` bedeutet eine Pause von *mindestens* einer Sekunde. Das kann durchaus auch länger sein. Dazu kommt dann noch der Code der drum herum läuft und auch Zeit benötigt. Wenn Du echte Sekunden zählen möchtest, solltest Du eine "echte" Uhr als Basis benutzen.

Weitere Anmerkungen: Wozu ist Zustand 2 gut? Das klingt weniger nach Zustand, sondern eher nach einem Vorgang. Auf jeden Fall sollte man solche "magischen" Zahlen mindestens an Namen binden, damit der Quelltext verständlicher wird. ``self.state = TimeThread.PAUSE`` ist wesentlich aussagekräftiger als ``self.state = 0``.

In `reset()` würde ich auf `repaint_label()` zurückgreifen, was man übrigens wesentlich kompakter schreiben kann:

Code: Alles auswählen

    def repaint_label(self):
        markup_template = '<span size="xx-large" weight="bold">%02d:%02d</span>'
        self.time_label.set_markup(markup_template % divmod(self.clock, 60))
In der `run()` kann man den gemeinsamen Code aus den beiden Zweigen des ``if`` heraus ziehen. Das könnte dann so aussehen:

Code: Alles auswählen

    def run(self):
        while True:
            sleep(1)
            if self.state == TimeThread.RUNNING:
                self.clock += 1
            gtk.gdk.threads_enter()
            self.repaint_label()
            gtk.gdk.threads_leave()
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Poe hat geschrieben:ich habe folgendes Problem. Ich habe eine Klasse zum zählen von Sekunden. Dabei ist in der run() jeweils eine Sekunde Pause dann wird ein counter erhöht und das ganze mittels gtk.gdk.threads_enter() und gtk.gdk.thread_leave() auf einer GUI aktualisiert.
Kannst du komplett durch ``gobject.timeout_add()`` ersetzen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Poe
User
Beiträge: 6
Registriert: Dienstag 4. März 2008, 10:10

Ok, zugegegeben mein Programmierstil ist in der Anfangsphase immer eher quick and dirty. Jedoch lag mein Problem nicht am Thread. Die Zustandbasierte vorgehensweise innerhalb des Threads klappt sehr gut und mit sleep hab ich keinen abweichung bei der Zeitmessung.

Mein eigentlicher Fehler bestand darin, das ich den Button Start/Stop bei der neuzuweisung mittels connect nicht vom Alten Events gelöst habe. Daher wurde der Prozessort wahrscheinlich mit Threads überschüttet und nichts ging mehr.

Code: Alles auswählen

        self.buttonTimeStart.disconnect(self.buttonConnectID) # Diese Zeile hatte ich vorher nicht dabei, daran lags
        self.buttonConnectID = self.buttonTimeStart.connect("clicked", self.startTimer, None)
        self.buttonTimeStart.set_label("Start")
        
        self.clockThread.pause()
Danke dennoch für eure Ideen und ich gelobe besserung in meinem Programmierstil :-D

Grüße
Poe
User
Beiträge: 6
Registriert: Dienstag 4. März 2008, 10:10

Mal als Frage, wie würdet Ihr denn einen Timer bauen?
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Leonidas hat geschrieben:
Poe hat geschrieben:ich habe folgendes Problem. Ich habe eine Klasse zum zählen von Sekunden. Dabei ist in der run() jeweils eine Sekunde Pause dann wird ein counter erhöht und das ganze mittels gtk.gdk.threads_enter() und gtk.gdk.thread_leave() auf einer GUI aktualisiert.
Kannst du komplett durch ``gobject.timeout_add()`` ersetzen.
So. Und dann jeweils die System Zeit nehmen und vergleichen und nicht hochzählen. time.sleep(1.0) bedeutet nicht eine Sekunde warten sondern mindestens eine Sekunde warten. ;)
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
Antworten