Oberfläche friert ein, Konsole läuft weiter

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Kahnbein.Kai
User
Beiträge: 104
Registriert: Mittwoch 24. Juni 2015, 14:12
Wohnort: Bochum

Guten Morgen
in diesem Programm soll man eine Int Zahl eingeben und das Programm soll dann jeweils in Sekunden schritten bis zu dieser Zahl zählen. Auf der Konsole klappt die Ausgabe, in Tkinter nicht.
Die Programmoberfläche friert bei mir ein und zeigt dann nur den letzten Wert an. Kann mir jemand erklären warum die Oberfläche nicht jede Sekunde aktualisiert wird ?

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):
    
    def __init__(self):
        super().__init__()
        
        self.entry1 = tk.IntVar(self)
        
        self.label1 = tk.StringVar(self, "Ausgabe")
        
        self.Entry1 = tk.Entry(self, textvariable=self.entry1)
        self.Entry1.pack()     
        
        self.Label1 = tk.Label(self, textvariable=self.label1)
        self.Label1.pack()
        
        self.Button1 = tk.Button(self, text="Start", command=self.start)
        self.Button1.pack()
        
        
    def start(self):
        runden = int(self.entry1.get())
        if runden > 0:
            self.counter(runden)
        else:
            self.label1.set("Bitte Zahl > 0 eingeben !!")
        
    def counter(self, runden):
        for j in range(runden):
            print(j)
            self.after(1000, self.update(j))
        
    def update(self,zahl):
        self.label1.set(str(zahl))
        
        
def main():
    root = Programm()
    root.mainloop()
        
        
if __name__ == "__main__":
    main()
Gruß Kai
Benutzeravatar
peterpy
User
Beiträge: 188
Registriert: Donnerstag 7. März 2013, 11:35

Hallo Kai,
dir fehlt ein update_idletasks,
bitte schreib die Variablennamen klein.

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):    
    def __init__(self):
        super().__init__()        
        self.entry_var = tk.IntVar(self)        
        self.label_var = tk.StringVar(self, "Ausgabe")        
        self.entry = tk.Entry(self, textvariable=self.entry_var)
        self.entry.pack()             
        self.label = tk.Label(self, textvariable=self.label_var)
        self.label.pack()        
        self.button = tk.Button(self, text="Start", command=self.start)
        self.button.pack()        
        
    def start(self):
        runden = int(self.entry_var.get())
        if runden > 0:
            self.counter(runden)
        else:
            self.label_var.set("Bitte Zahl > 0 eingeben !!")
        
    def counter(self, runden):
        for j in range(runden):
            self.update(j)
            print(j)
            self.after(1000)
        
    def update(self,zahl):        
        self.label_var.set(str(zahl))
        self.update_idletasks()
        
        
def main():
    root = Programm()
    root.mainloop()
        
if __name__ == "__main__":
    main()
Gruss Peter
Sirius3
User
Beiträge: 17797
Registriert: Sonntag 21. Oktober 2012, 17:20

@peterpy: update_ideltasks sollte man nicht benutzen, statt dessen sollte man after benutzen, so wie Du das machst, hat das aber keinen Effekt.

@Kahnbein.Kai: das selbe Thema hatten wir doch schon vor einiger Zeit hier: viewtopic.php?p=384851
Lang laufende Schleifen darf es bei tkinter nicht geben, das Argument von after muß eine Funktion sein, und nicht der Rückgabewert nach dem Aufruf der Funktion update.
Es ist sehr verwirrend, dass es ein Attribut entry1 gibt (was gar kein Entry ist) und eins mit dem selben Namen Entry1. Variablennamen, schreibt man generell komplett klein. Man nummeriert keine Variablen durch, die 1 ist überflüssig und kann weg.

Code: Alles auswählen

import tkinter as tk

class Programm(tk.Tk):    
    def __init__(self):
        super().__init__()        
        self.entry_var = tk.IntVar(self)        
        self.label_var = tk.StringVar(self, "Ausgabe")        
        self.entry = tk.Entry(self, textvariable=self.entry_var)
        self.entry.pack()             
        self.label = tk.Label(self, textvariable=self.label_var)
        self.label.pack()        
        self.button = tk.Button(self, text="Start", command=self.start)
        self.button.pack()        
        
    def start(self):
        runden = int(self.entry_var.get())
        if runden > 0:
            self.counter(0, runden)
        else:
            self.label_var.set("Bitte Zahl > 0 eingeben !!")
        
    def counter(self, aktuelle_runde, runden):
        if aktuelle_runde < runden:
            aktuelle_runde += 1
            self.label_var.set(str(aktuelle_runde))
            self.after(1000, self.counter, aktuelle_runde, runden)
        
        
def main():
    root = Programm()
    root.mainloop()
        
if __name__ == "__main__":
    main()
Benutzeravatar
peterpy
User
Beiträge: 188
Registriert: Donnerstag 7. März 2013, 11:35

oder nur ein einfaches

Code: Alles auswählen

self.label.update()
wenn Du die Methode update vor after aufrufst.
Das vorher Aufrufen hat auch den positiven Effekt, dass die Ausgabe synchron zur Konsolenausgabe angzeigt wird.
Gruss Peter
Benutzeravatar
__blackjack__
User
Beiträge: 13200
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@peterpy: `update()` noch weniger als `update_idletasks()`. Mit so einem Unsinn sollte man gar nicht erst anfangen. Das skaliert nicht, und wenn es etwas komplexer wird, dann hat man schnell undefinierten Zustand, also das Programm läuft oder es stürzt hart ab, oder irgendwas dazwischen. Bei `update()` muss man das mit der Ereignisschleife sehr gut verstehen, weil das Beschränkungen hat was da passieren darf und was nicht wenn man `update()` aus einem Callback heraus aufruft. Und insbesondere bei solchen Fällen wie hier, wo das ja ganz offensichtlich benutzt werden soll um sich gerade nicht mit er Ereignisschleife zu beschäftigen, weil das nicht verstanden worden ist, ist das gefährlich.

Auch bei Korrekter verwendung von `after()` ist eine parallele Ausgabe auf der Konsole synchron. Das ist also kein Argument.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Antworten