tk.Label während Schleife aktualisieren ?

Fragen zu Tkinter.
Antworten
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Hallo,
ich möchte ein tk.Label nach jeder Sekunde aktualisieren lassen.
Das würde auch mit der Methode after(1000, self.Methode) funktionieren, das kann ich leider nicht einsetzen. Da zwei Parameter beim ausführen der Methoden übergeben werden sollen. Daher möchte ich in einer Schleife die time.sleep Methode einsetzten.
Der Code sieht folgendermaßen aus:

Code: Alles auswählen

    def zählerGlobal(self, text, Zeit):
        for self.zählerUni in range(Zeit):
            print(f"{self.zählerUni} / {Zeit}")
            self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} {text} {self.zählerUni+1} / {Zeit}")
            self.zählerUni += 1
            time.sleep(1)
Leider wird die self.ausgabe.set() erst nach Ablauf der Schleife aktualisiert, springt also von 0 Sekunden auf den Endwert.
Ist es Möglich tkinter und die time.sleep Methode zusammen sinvoll zu nutzen ?

Gruß Kai
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

Man darf kein sleep benutzen. Statt dessen ist es schon richtig, after zu verwenden. Und da kann man neben einem, drei, vier, fünf, ... dreitausendsiebenhundertneunzehn Parametern auch zwei übergeben. Was aber eigentlich nicht nötig sein sollte, wenn Du eine Klasse hast.
Was hast Du versucht und was hat nicht funktioniert? Zeige vollständigen Beispielcode.
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ok, hier ist die Version A, ich möchte das Programm "optimieren", sodass nicht mehr jede Funktion 'run' und 'unterbrechung' selbst "Zählt" und die Ausgabe aktualisiert.

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):
    
    def __init__(self):
        super().__init__()
        
        self.title("Test Ablaufprogramm")
        
        self.runde = tk.IntVar(self, 3)
        
        self.arbeit = tk.IntVar(self, 4)
   
        self.pause = tk.IntVar(self, 2)
        
        self.ausgabe = tk.StringVar(self, "Ausgabe")
        
        tk.Label(self, text='Runden:').grid(row=0,column=2)
        
        tk.Entry(self,width=5, textvariable=self.runde).grid(row=0, column=3)
        
        tk.Label(self, text='Arbeit:').grid(row=0,column=4)
        
        tk.Entry(self,width=5, textvariable=self.arbeit).grid(row=0, column=5)
        
        tk.Label(self, text='Pause:').grid(row=0,column=6)
        
        tk.Entry(self,width=5, textvariable=self.pause).grid(row=0, column=7)
        
        tk.Label(self, textvariable=self.ausgabe).grid(row=2,columnspan=8)
        
        tk.Button(self, text="Start", width=10, command=self.start).grid(row=3, columnspan=8)
     
        self.zählerRunde = 0
     
    def start(self):
        self.zählerArbeit = 0 
        self.zählerPause = 0
        self.zählerRunde += 1
        self.run()
        
    def run(self):
        if self.zählerRunde < self.runde.get():
            if self.zählerArbeit < self.arbeit.get()+1:
                self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} Arbeit {self.zählerArbeit} / {self.arbeit.get()}")
                self.after(1000, self.run)
                self.zählerArbeit += 1
            else:
                self.unterbrechnung()
        else: 
            if self.zählerArbeit < self.arbeit.get()+1:
                self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} Arbeit {self.zählerArbeit} / {self.arbeit.get()}")
                self.after(1000, self.run)
                self.zählerArbeit += 1
            else:
                self.reset()
             
    def unterbrechnung(self):
        if self.zählerPause < self.pause.get()+1:
            self.after(1000, self.unterbrechnung)
            self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} Pause {self.zählerPause} / {self.pause.get()}")
            self.zählerPause += 1
        else:
            self.start()
        
    def reset(self):
        self.ausgabe.set("Ausgabe")
        self.zählerRunde = 0
Soweit bin ich im "optimierten" Code

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):
    
    def __init__(self):
        super().__init__()
        
        self.title("Test Ablaufprogramm")
        
        self.runde = tk.IntVar(self, 3)
        
        self.arbeit = tk.IntVar(self, 4)
   
        self.pause = tk.IntVar(self, 2)
        
        self.ausgabe = tk.StringVar(self, "Ausgabe")
        
        tk.Label(self, text='Runden:').grid(row=0,column=2)
        
        tk.Entry(self,width=5, textvariable=self.runde).grid(row=0, column=3)
        
        tk.Label(self, text='Arbeit:').grid(row=0,column=4)
        
        tk.Entry(self,width=5, textvariable=self.arbeit).grid(row=0, column=5)
        
        tk.Label(self, text='Pause:').grid(row=0,column=6)
        
        tk.Entry(self,width=5, textvariable=self.pause).grid(row=0, column=7)
        
        tk.Label(self, textvariable=self.ausgabe).grid(row=2,columnspan=8)
        
        tk.Button(self, text="Start", width=10, command=self.start).grid(row=3, columnspan=8)
     
        self.zählerRunde = 0
        
          def start(self):
        self.zählerRunde += 1
        if self.zählerRunde <= self.runde.get():
            self.zählerUni = 0
            self.zählerGlobal("Arbeit", self.arbeit.get())
            
    def zählerGlobal(self, text, Zeit):
        if self.zählerUni < Zeit:
           self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} {text} {self.zählerUni+1} / {Zeit}")
           self.zählerUni += 1
           self.after(1000,self.zählerGlobal)
     
Mein Problem ist die letzte Zeile, hier kommt die Fehlermeldung da zwei Argumente benötigt werden. Die Parameter in der Methode Start sind aber nicht die einzigen es gibt noch andere. Daher kann ich die letzte Zeile nicht in

Code: Alles auswählen

 self.after(1000, self.zählerGlobal("Arbeit", self.arbeit.get()))
ändern.

Ich weiß nicht wie ich die Methode rekursiv aufrufen soll und die Parameter nur einmal übergeben muss.

Gruß Kai
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ah mir fällt gerade die Methode in einer Methode ein.
Die "Muttermethode" bekommt die PArameter und die "Kindmethode" wird rekursiv ausgeführt.

Könnte das die Lösung des Problem sein ?

Gruß Kai
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

So rufst Du ja auch zählerGlobal direkt auf, und nicht per after!

Code: Alles auswählen

self.after(1000, self.zählerGlobal, "Arbeit", self.arbeit.get())
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Das mit der Methode innerhalb der Methode geht auch nicht. Das tk.Label wird wieder nicht aktualisiert. :(

Hier ist der neue Code

Code: Alles auswählen

 def start(self):
        self.zählerRunde += 1
        if self.zählerRunde <= self.runde.get():
            self.zählerUni = 0
            self.zählerGlobal("Arbeit", self.arbeit.get())
            
    def zählerGlobal(self, text, Zeit):
            def zählerIntern():
                self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} {text} {self.zählerUni+1} / {Zeit}")
                self.zählerUni += 1
                if self.zählerUni < Zeit:
                    self.after(1000, zählerIntern())
            zählerIntern() 
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

Hast Du meinen Beitrag gelesen?
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ja, ich habe es mal versucht es klappt leider nicht.
hier ist der Code:

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):
    
    def __init__(self):
        super().__init__()
        
        self.title("Test Ablaufprogramm")
        
        self.runde = tk.IntVar(self, 3)
        
        self.arbeit = tk.IntVar(self, 4)
   
        self.pause = tk.IntVar(self, 2)
        
        self.ausgabe = tk.StringVar(self, "Ausgabe")
        
        tk.Label(self, text='Runden:').grid(row=0,column=2)
        
        tk.Entry(self,width=5, textvariable=self.runde).grid(row=0, column=3)
        
        tk.Label(self, text='Arbeit:').grid(row=0,column=4)
        
        tk.Entry(self,width=5, textvariable=self.arbeit).grid(row=0, column=5)
        
        tk.Label(self, text='Pause:').grid(row=0,column=6)
        
        tk.Entry(self,width=5, textvariable=self.pause).grid(row=0, column=7)
        
        tk.Label(self, textvariable=self.ausgabe).grid(row=2,columnspan=8)
        
        tk.Button(self, text="Start", width=10, command=self.start).grid(row=3, columnspan=8)
     
        self.zählerRunde = 0
        
        
    def start(self):
        self.zählerRunde += 1
        if self.zählerRunde <= self.runde.get():
            self.zählerUni = 0
            self.zählerGlobal("Arbeit", self.arbeit.get())
            
    def zählerGlobal(self, text, Zeit):
        self.ausgabe.set(f"Runde: {self.zählerRunde} / {self.runde.get()} {text} {self.zählerUni+1} / {Zeit}")
        self.zählerUni += 1
        if self.zählerUni < Zeit:
            self.after(1000, self.zählerGlobal,"Arbeit", self.arbeit.get())
        else:
            self.unterbrechung()
    
    def unterbrechung(self):
        self.zählerUni = 0
        self.zählerGlobal("Pause", self.pause.get())              

def main():
    root = Programm()
    root.mainloop()
    
if __name__ == "__main__":
    main()
        
   
        
Die Abfolge der Ausgabe ist dann so
Arbeit 1/4
Arbeit 2/4
Arbeit 3/4
Arbeit 4/4
Pause 1/2
Arbeit 2/4
Arbeit 3/4
Arbeit 4/4
Pause 1/2
Arbeit 2/4
Arbeit 3/4
...

Die Parameter der Methode 'unterbrechung' werden von der after Methode überschrieben.
Habe ich da ein generellen Denkfehler mit den zwei Methoden für Arbeit und Unterbrechung ?

Gruß Kai
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

Kannst Du mal beschreiben, was Du eigentlich erreichen willst?
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Ich möchte eine (Zähl) Methode schreiben die zwei Parameter entgegen nimmt. Ein String ("Arbeit" oder "Pause") und je einen Integerwert.
Die Methode 'Start' überträgt die Parameter "Arbeit" und 4 Sekunden. Die Zähl Methode soll jetzt jede Sekunde das Label ändern, mit der Ausgabe "Arbeit" und "X/4 Sekunden".
Im Anschluss soll die Zähl Methode die Parameter "Pause" und 2 Sekunden entgegennehmen, und das Label in "Pause" und "X/2 Sekunden" ändern.
Danach wieder "Arbeit" und "X/4 Sekunden".
Wieder "Pause" und "X/2 Sekunden".
Bis alle Runden durchgelaufen sind, aber das ist erstmal nicht so wichtig. Der große Codeblock im dritten Post läuft.
Darin ist aber alles dreifach jede Methode ändern die Ausgabe und so weiter, ich wollte einfach ein wenig üben und nur eine Methode schreiben, die das Label ändern und Zählt.
Dafür muss diese halt die Parameter übernehmen und die Ausgabe und die Zeit anpassen.

Gruß Kai
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

Du rufst ja auch after immer nur mit "Arbeit" auf, da kann der Pausenzähler also auch nie zählen.

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)        
        self.title("Test Ablaufprogramm")
        self.runde = tk.IntVar(self, 3)
        self.arbeit = tk.IntVar(self, 4)
        self.pause = tk.IntVar(self, 2)
        self.ausgabe = tk.StringVar(self, "Ausgabe")
        tk.Label(self, text='Runden:').grid(row=0,column=2)
        tk.Entry(self,width=5, textvariable=self.runde).grid(row=0, column=3)
        tk.Label(self, text='Arbeit:').grid(row=0,column=4)
        tk.Entry(self,width=5, textvariable=self.arbeit).grid(row=0, column=5)
        tk.Label(self, text='Pause:').grid(row=0,column=6)
        tk.Entry(self,width=5, textvariable=self.pause).grid(row=0, column=7)
        tk.Label(self, textvariable=self.ausgabe).grid(row=2,columnspan=8)
        tk.Button(self, text="Start", width=10, command=self.start).grid(row=3, columnspan=8)
        
    def start(self):
        self.zaehler_runde = 1
        self.zaehler_uni = 1
        self.zaehlen("Arbeit", self.arbeit.get())

    def zaehlen(self, text, zeit):
        self.ausgabe.set(f"Runde: {self.zaehler_runde} / {self.runde.get()} {text} {self.zaehler_uni} / {zeit}")
        self.zaehler_uni += 1
        if self.zaehler_uni > zeit:
            self.zaehler_uni = 1
            if text == "Arbeit":
                text = "Pause"
                zeit = self.pause.get()
            else:
                self.zaehler_runde += 1
                if self.zaehler_runde > self.runde.get():
                    # Ende
                    return
                text = "Arbeit"
                zeit = self.arbeit.get()
        self.after(1000, self.zaehlen, text, zeit)


def main():
    root = Programm()
    root.mainloop()
    
if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die letzte Zeile von `zaehlen` ist falsch eingerückt. Die gehört mit in den ``if``-Zweig.

Hier werden jetzt die beiden `zaehler_*` ausserhalb der `__init__()` als Attribute eingeführt. Die Frage ist, ob die tatsächlich zum Zustand des Objekts gehören, dann sollte man sie in der `__init__()` (mit `None`) einführen, oder ob man die als Argumente an `zaehlen()` übergeben sollte.

Zwei weitere Fragen: Soll man während das läuft die Werte für `self.arbeit` und `self.pause` ändern dürfen? Und ist es tatsächlich in Ordnung wenn man während das läuft noch weitere Vorgänge parallel starten darf, weil ja der Button weiterhin anklickbar ist.

Edit: Und es wird nirgends behandelt, dass der Benutzer auch etwas anderes als Zahlen eintippen könnte und die entsprechenden `get()`-Methoden dann eine Ausnahme auslösen.

Edit 2: Kreative Nutzer könnten auch auf die Idee kommen negative Zahlen einzugeben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

@Sirius3
Danke du bist ein Genie !! Ich bin einfach nicht auf die Idee gekommen die übergebenen Parameter in Variablen zu packen und danach die Funktion damit zu füttern.
Vielen Dank !

@__blackjack__
Hier werden jetzt die beiden `zaehler_*` ausserhalb der `__init__()` als Attribute eingeführt. Die Frage ist, ob die tatsächlich zum Zustand des Objekts gehören, dann sollte man sie in der `__init__()` (mit `None`) einführen, oder ob man die als Argumente an `zaehlen()` übergeben sollte.
Naja, darüber habe ich mir noch keine Gedanken gemacht. Ich bin froh, das dass Programm läuft. Wie meinst du das mit '__init__()' und 'None', reicht es nicht wenn ich diese als '=0' deklariere ?
Zwei weitere Fragen: Soll man während das läuft die Werte für `self.arbeit` und `self.pause` ändern dürfen? Und ist es tatsächlich in Ordnung wenn man während das läuft noch weitere Vorgänge parallel starten darf, weil ja der Button weiterhin anklickbar ist.
Nein das ist nicht gewollt. Ich bin noch nicht auf die Idee gekommen zweimal auf Start zu drücken. Ich weiß aber auch nicht wie man das verhindert. Gibt es eine "Art" Sperrfunktion ?

Code: Alles auswählen

Edit: Und es wird nirgends behandelt, dass der Benutzer auch etwas anderes als Zahlen eintippen könnte und die entsprechenden `get()`-Methoden dann eine Ausnahme auslösen.
Darauf bin ich auch noch nicht gekommen, sowas haben wir mal in der Uni gemacht, ich meine das war 'try' und dann wie eine If-Abfrage oder ?

Code: Alles auswählen

Edit 2: Kreative Nutzer könnten auch auf die Idee kommen negative Zahlen einzugeben.
Siehe letzte Antwort :)


Gruß Kai
Antworten