Seite 1 von 1

Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Mittwoch 29. April 2015, 18:25
von Umsteiger73
Hallo
Ich mache meine ersten Versuche mit Tkinter. Die Idee ist ein Objekt, dass den Text eines Labels während der Laufzeit ändert. Das versuche ich mit folgendem Code:

Code: Alles auswählen

import tkinter as tk

class TextSetter():

    def __init__(self):
        self.single_label = SingleLabel()

        self.change_label()

    def change_label(self):
        self.single_label.label_variable.set("Come on!") # does not work
        self.single_label.set_label("What the ...!") # does not work either

class SingleLabel():

    def __init__(self):
        self.root = tk.Tk()
        self.label_variable = tk.StringVar()
        
        self.start()

    def set_label(self,text):
        self.label_variable.set(text)

    def start(self):
        self.label = tk.Label(self.root,textvariable=self.label_variable)
        self.label.pack()
        self.label_variable.set("So far so good")
        
        self.root.mainloop()

def main():
    TextSetter()
    

if __name__ == '__main__':
    main()
Ich erwarte eigentlich, dass der Text im Label "What the ...!" oder zumindest "Come on!" sein sollte. Statdessen bleibt der Text "So far so Good".
Es kommt keine Fehlermeldung. Ich mache irgend ein Denkfehler. Kann mir allenfall jemand auf die Sprünge helfen?

Danke im Voraus

Gruss

Thomas

Re: Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Mittwoch 29. April 2015, 19:24
von BlackJack
@Umsteiger73: Der Aufruf von `mainloop()` kehrt erst zurück wenn das Fenster geschlossen wird, deshalb wird nichts ausgeführt was nach dem Aufruf von `SingleLabel()` kommt bis das Fenster geschlossen wird.

Re: Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Mittwoch 29. April 2015, 21:05
von Umsteiger73
@BlackJack

Danke für den Hinweis. Genau das habe ich gebraucht. Jetzt kann ich in der richtigen Richtung nach einer Lösung suchen.

Gruss

Thomas

Re: Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Freitag 1. Mai 2015, 09:50
von Umsteiger73
Nachtrag, für alle die es interessiert.

Ein rekursiver Aufruf der after Methode und das umstellen der Mainloop hat mir weiter geholfen. Spannenderweise ist auch das erwartete Problem mit der Rekursionstiefe nicht aufgetreten.

Code: Alles auswählen

import tkinter as tk

class SingleLabel(object):
    
    def __init__(self,root):
        self.root = root
        self.data = Countdown()
    
    def draw_label(self):
        frame=tk.Frame(self.root)
        frame.pack()
        self.text=tk.Label(frame)
        self.text.pack()

    def refresh_label(self):
        self.number= self.data.number()
        self.text.configure(text=self.number)
        self.root.after(1000, self.refresh_label) # des Pudels Kern

class Countdown(object):
    
    def __init__(self):
        self.countdown = [1,2,3,4,5,6,7,8,9,10]
    
    def number(self):
        if self.countdown:
            return self.countdown.pop()
        else:
            self.countdown = [1,2,3,4,5,6,7,8,9,10]
            return [0]

root=tk.Tk()
sl = SingleLabel(root)
sl.draw_label()
sl.refresh_label()
root.mainloop()
@BlackJack: Nochmals danke für den Fingerzeig.

Gruss Thomas

Re: Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Freitag 1. Mai 2015, 10:18
von Sirius3
@Umsteiger73: Außerhalb von __init__ sollten keine neuen Attribute mehr hinzugefügt werden. Warum gibt es die draw_label-Methode überhaupt separat? number an self zu binden ist schlicht weg falsch. number in Countdown sollte besser next_number heißen, um den zustandsändernden Charakter der Methode deutlich zu machen. Auf Modulebene sollten kein Code direkt ausgeführt werden, das heißt, ab Zeile 32 sollte alles in eine Funktion wandern, die üblicherweise main genannt wird und per

Code: Alles auswählen

if __name__ == '__main__':
    main()
aufgerufen wird.

Re: Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Freitag 1. Mai 2015, 12:08
von BlackJack
@Umsteiger73: Man könnte/sollte aus dem `Countdown` vielleicht auch ein iterierbares Objekt machen statt eine Methode zur Verfügung zu stellen die nicht nicht den dafür üblichen Namen trägt. Und das mit der Liste und `pop()` ist eine *sehr* eigenartige Implementierung. Das die literale Liste zweimal im Quelltext steht ist zudem auch fehleranfällig. Eigentlich bräuchte man gar keine eigene Klasse dafür sondern höchstens eine Funktion die ein passendes iterierbares Objekt mit eingebauten Funktionen (`range()`, `reversed()`) und Mitteln aus der Standardbibliothek (`itertools.cycle()`) erstellt.

Es ist eher ungewöhnlich das die Initialisierung eines Objekts GUI-Elemente auf einem Widget erstellt das als Argument übergeben wurde. Da hat der Aufrufer ja keinerlei Kontrolle was diese Methode in einem eigentlich *übergeordneten* Widget anstellt. Normalerweise würde man von einem Container-Widget erben, dort *drin* anzeigen was man lustig ist, und dem *Aufrufer* die Entscheidung überlassen wie und wo er das dann in einem übergeordneten Widget anordnet. Kann ja sein das am Ende mehrere solcher `SingleLabel` (was soll das eigentlich bedeuten?) in einem Frame in einer `grid()`-Anordnung angezeigt werden sollen. Dazu sollte man die `SingleLabel`-Klasse nicht ändern müssen.

Ungetestet:

Code: Alles auswählen

import tkinter as tk
from itertools import cycle


class CountdownUI(tk.Frame):

    def __init__(self, root):
        tk.Frame.__init__(self, root)
        self.countdown_label = tk.Label(self)
        self.countdown_label.pack()
        self.countdown_numbers = cycle(reversed(range(11)))
        self.event_id = None

    def _update_countdown(self):
        self.countdown_label['text'] = next(self.countdown_numbers)
        self.event_id = self.after(1000, self._update_countdown)

    def start_countdown(self):
        if self.event_id is None:
            self._update_countdown()

    def stop_countdown(self):
        if self.event_id is not None:
            self.after_cancel(self.event_id)


def main():
    root = tk.Tk()
    countdown_ui = CountdownUI(root)
    countdown_ui.pack()
    countdown_ui.start_countdown()
    root.mainloop()


if __name__ == '__main__':
    main()

Re: Ändern des Textes eines Labels durch eine anderes Objekt

Verfasst: Sonntag 3. Mai 2015, 11:08
von Umsteiger73
Hallo

Wow, danke für die Denkanstösse. Werde sie berücksichtigen.

Gruss

Thomas