Labels über Listeneintrag konfigurieren

Fragen zu Tkinter.
Antworten
elofranz
User
Beiträge: 6
Registriert: Sonntag 8. September 2019, 12:39

Ich habe eine Liste mit 2 bis 7 Einträgen die sich ständig ändern. Die Einträge werden in einer Listbox angezeigt.

Zur besseren Übersicht möchte ich die Einträge auf Labels aufteilen (um später auch entsprechende Logos anzuzeigen).

Mein Grundgedanke ist die Label in eine Liste einzutragen und in einer Schleife über die Anzahl der Listboxeinträge aufzurufen.

Code: Alles auswählen

from tkinter import *

fenster = Tk()
fenster.geometry("500x500")



lblListe = ["lbl_a","lbl_b","lbl_c","lbl_d","lbl_e","lbl_f","lbl_g"]

wertX = 3 # anzahl


def btn1_click():
    print("__________________")
    wertLen = len(lblListe)
    print("lblListe : ",wertLen)
    print("wertX    : ",wertX)
    print("------------")
    counter = 0
    while counter < wertX:
        print("counter: ", counter)
        lblZw = lblListe[counter]
        print("lblZw:", lblZw)
        print("....")
        lblZw.config(font=("ARIAL",18)) ##
        
        counter = counter +1
    
    
    


lbl_a = Label(fenster, text="AAAAAAA")
lbl_a.place(x=20, y=20)

lbl_b = Label(fenster, text="BBBBBBB")
lbl_b.place(x=20, y=50)

lbl3 = Label(fenster, text="CCCCCCC")
lbl3.place(x=20, y=80)


btn1 =Button(fenster,text="click", command= btn1_click)
btn1.place(x=250 , y=300)



fenster.mainloop()
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Gibt es auch eine Frage?

Benutze keine *-Importe, kein place und keine globalen Variablen.
elofranz
User
Beiträge: 6
Registriert: Sonntag 8. September 2019, 12:39

:oops: Ja die Frage.

Ich dachte das es möglich ist über die Listeneinträge die Labels aufzurufen.
Wie kann ich die Labels in der Schleife aufrufen.
In meinem Testprogramm erscheint: "AttributeError: 'str' object has no attribute 'config' "

Danke.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt Strings in eine Liste zu packen, kannst Du die Label-Objekte in einer Liste sammeln und benutzen.
Populus
User
Beiträge: 1
Registriert: Mittwoch 11. September 2019, 19:39

Hallo zusammen,

in diese Diskussion steige ich gerne mit ein, da ich mich gerade mit einem ähnlichen Problem rumschlage.
@Sirius3: Wie kann ich mir das vorstellen Label Objekte in einer Liste zu nutzen?
um im Beispiel von elofranz zu bleiben: Ich lege eine Liste von Label Objekten an:
lblListe = [Label(fenster, text="AAAAAAA"), Label(fenster, text="BBBBBB"), Label(fenster, text="CCCCCC")]
Dann funktioniert auch lbl_001 = lblListe[0] problemlos
für mich bleibt noch die Frage wie kann ich die Labels für eine unbekannte Zahl von Labels erstellen?
Kennst du auch einen Trick, wie ich die Variabel lbl_001 usw. automatisch (sagen wir mal in abhängigkeit von einer Dateianzahl x in einem Verzeichnis) generieren kann?

Freue mich auf jede Hilfe - Populus
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mit einer for-Schleife und dem anhängen an eine Liste.
elofranz
User
Beiträge: 6
Registriert: Sonntag 8. September 2019, 12:39

Hallo,

danke für euer Interesse an meinen Problemchen.

Mit dem Code kann ich nun die gewünschte Anzahl von Labels in einen Frame erstellen und ein Bild zuordnen.

Jetzt muss noch das Frame geleert werden, um es mit neuen Daten zu generieren.

Hat jemand eine Idee?

Code: Alles auswählen

from tkinter import *



fenster = Tk()
fenster.geometry("500x500")

bildStart = PhotoImage(file= "StartBild.png")

lblListe = ["lbl_a","lbl_b","lbl_c","lbl_d","lbl_e","lbl_f","lbl_g"] 

labels=[] #leere Labelliste 

def btn1_click():
    print("-----")
    for x in lblListe: 
        zw = x 
        label = Label(frame1,text=zw, image = bildStart, compound = CENTER ) 
        label.pack()
        labels.append(label) 



frame1 = Frame(fenster, bg ="SKYBLUE")
frame1.place(x = 10, y =10, width = 150, height =400)

lblText1 = Label(frame1, text="Hallo",image = bildStart)
lblText1.pack()



btn1 =Button(fenster,text="click", command= btn1_click)
btn1.place(x=250 , y=300)
 
fenster.mainloop()

elofranz
User
Beiträge: 6
Registriert: Sonntag 8. September 2019, 12:39

:) Das sollte funktionieren.

Code: Alles auswählen

from tkinter import *

fenster = Tk()
fenster.geometry("500x500")


# das Bild muss im Ordner vorhanden sein
bildStart = PhotoImage(file= "StartBild.png")

# Liste die vor dem Aufruf der Funktion aktualisiert wird
lblListe = ["wert 1","wert 2","wert 3","wert 100","wert 101","wert Hallo","usw"] 

# leere Liste für die LabelNamen
labels=[]                   




# löscht die erstellte Liste in frame1
# nach Aktualisierung der der lblListe können die Labels neu erstellt werden 
def btn2_click():           
    print("Labels löschen")
    for widget in frame1.winfo_children():
        widget.destroy()


# Labels in frame1 erstellen
# Text der Labels wir von LblListe übernommen
# 
def btn1_click():           
    print("Labels erstellen")
    
    for x in lblListe: 
        zw = x 
        label = Label(frame1,text=zw, image = bildStart, compound = CENTER ) 
        label.pack()
        labels.append(label) 



frame1 = Frame(fenster, bg ="SKYBLUE")
frame1.place(x = 10, y =10, width = 150, height =400)

lblText1 = Label(frame1, text="Hallo",image = bildStart)
lblText1.pack()

btn1 = Button(fenster,text="click", command= btn1_click)
btn1.place(x=250 , y=300)

btnClear = Button(fenster,text="Clear", command= btn2_click)
btnClear.place(x=250 , y=350)
 
fenster.mainloop()



Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Es gilt immer noch: Benutze keine *-Importe, kein place und keine globalen Variablen.
Jetzt ist es sogar noch schlimmer, da Du globale Listen änderst.

Statt die Labels einzeln zu zerstören ist es wesentlich einfacher, alles in einen Frame zu packen und diesen dann zu entfernen.

Variablennamen sollten aussagekräftig sein, keine Abkürzungen enthalten und nicht durchnummeriert werden. Texte in einer Liste an die Variable `lblListe` zu binden ist aus drei Gründen schlecht. Erstens ist lbl eine kryptische Abkürzung, die man sich erst erschließen muß. Der Datentyp sollte nicht im Namen vorkommen, und label_liste ist so nichtssagend, und zudem noch falsch, weil gar keine Labels in dieser Liste sind.

Weiter geht es mit den Texten in dieser Liste, diese werden in einer for-Schleife an die Variable `x` gebunden. Nun gibt es nichts schlechteres als `x` wenn es sich nicht um eine Fließkommazahl handelt, die man in einfachen Rechnungen verwenden will, weil jeder assoziiert das mit einer Zahl. Warum nennst Du dann die Variable in `zw` um, statt gleich `zw` zu benutzen? `zw` ist eine Abkürzung, wo ich beim besten Willen nicht erraten kann, für was das steht.

Dann gibt es noch ein paar handfeste Probleme: Du erzeugst ständig neue Labels, und sammelst alle in einer Liste, ohne diese jemals wieder zu leeren.

Bei GUI-Programmierung kommt man um Klassendefinitionen nicht drumrum.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das mag ”funktionieren” ist aber ziemlich schrecklicher Code und löst das Problem auch falsch. Wozu ist denn in diesem Code die `labels`-Liste da? Genau um die geht es doch eigentlich, es wird aber gar nichts sinnvolles damit gemacht.

Aber der Reihe nach: Sternchen-Importe sind Böse™. Man müllt sich damit den Namensraum des aktuellen Moduls unnötig voll.

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Keine Variablen. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Keine kryptischen Abkürzungen wie `lbl` oder `btn` oder `zw`. Keine nummierten Namen. Und keine Grundatentypen in Namen.

Bei nummerierten Namen will man die Nummer einfach weglassen wenn es sowieso nur eine 1 gibt. Ansonsten will man sich *sinnvolle* Namen ausdenken oder gar keine Einzelnamen sondern eine Datenstruktur. Oft eine Liste. Bei `btn1_click()` und `btn2_click()` sind die Namen besch…eiden und man möchte dort sinnvolle Namen verwenden die beschreiben was die Funktionen machen. Oder, weil es Rückruffunktionen sind, Namen die Beschreiben bei welchem Ereignis die Funktion ausgeführt werden soll.

Es ist ja eine Sache sich was Leerzeichensetzung angeht nicht an den Style Guide for Python Code zu halten, aber offenbar einen Würfel zu benutzen um an jeder einzelnen Stelle zu entscheiden ob man ein Leerzeichen setzt oder nicht ist extrem.

Die Fenstergrösse gibt man nur vor wenn die GUI mindestens ein Element enthält das sich in der Grösse verändert wenn der Benutzer manuell die Fenstergrösse verändert. Ansonsten ergibt sich die Fenstergrösse automatisch durch den Inhalt des Fensters. Ausser man vewendet `place()` – was man aber nicht tut. Unter anderem weil sonst die Fenstergrösse nicht automatisch ermittelt werden kann.

Daraus das Variablen nicht auf Modulebene gehören, resultiert das Funktionen und Methoden alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. An der Stelle braucht man in GUIs mindestens ``lambda``-Ausdrücke oder `functools.partial()` oder objektorientierte Programmierung (OOP). OOP braucht man für jede nicht-triviale GUI.

`x` ist ein schlechter Name für eine Zeichenkette in einer Schleife und den dan nur zu verwenden um die Zeichenkette an einen anderen furchtbar schlechten Namen (`zw`) zu binden ist ziemlich sinnfrei.

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial
import tkinter as tk


def create_labels(image, frame, labels, texts):
    print("Labels erstellen")
    for text in texts:
        label = tk.Label(frame, text=text, image=image, compound=tk.CENTER)
        label.pack()
        labels.append(label)


def destroy_labels(labels):
    print("Labels löschen")
    for label in labels:
        label.destroy()
    labels.clear()


def main():
    fenster = tk.Tk()
    image = tk.PhotoImage(file="StartBild.png")
    label_texts = [
        "wert 1",
        "wert 2",
        "wert 3",
        "wert 100",
        "wert 101",
        "wert Hallo",
        "usw",
    ]
    frame = tk.Frame(fenster, bg="skyblue")
    frame.pack(side=tk.LEFT)

    label = tk.Label(frame, text="Hallo", image=image)
    label.pack()
    labels = [label]

    button_frame = tk.Frame(fenster)
    button_frame.pack(side=tk.LEFT, anchor=tk.S)

    tk.Button(
        button_frame,
        text="Create",
        command=partial(create_labels, image, frame, labels, label_texts),
    ).pack()
    tk.Button(
        button_frame, text="Clear", command=partial(destroy_labels, labels)
    ).pack()

    fenster.mainloop()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
elofranz
User
Beiträge: 6
Registriert: Sonntag 8. September 2019, 12:39

Danke, das hat mir weitergeholfen.

Ich habe noch eine Frage zu den erzeugten Labels.
Kann man die werte einzelner Labels noch ändern?
Und wie müsste man die Objekte ansprechen? :?:
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@elofranz: Klar, die Werte kann man ändern. Die stecken ja in der `labels`-Liste in der `main()`-Funktion und kann von dort falls benötigt auch an andere Funktionen übergeben werden. Wie das bei `destroy_labels()` passiert. Statt die `destroy()`-Methode aufzurufen könnte man natürlich auch den Text oder andere Optionen von den `Label`-Objekten ändern.

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial
import tkinter as tk


def create_labels(image, frame, labels, texts):
    print("Labels erstellen")
    for text in texts:
        label = tk.Label(frame, text=text, image=image, compound=tk.CENTER)
        label.pack()
        labels.append(label)


def extend_labels(labels):
    for label in labels:
        label["text"] = label["text"] + "!"


def destroy_labels(labels):
    print("Labels löschen")
    for label in labels:
        label.destroy()
    labels.clear()


def main():
    fenster = tk.Tk()
    image = tk.PhotoImage(file="StartBild.png")
    label_texts = [
        "wert 1",
        "wert 2",
        "wert 3",
        "wert 100",
        "wert 101",
        "wert Hallo",
        "usw",
    ]
    frame = tk.Frame(fenster, bg="skyblue")
    frame.pack(side=tk.LEFT)

    label = tk.Label(frame, text="Hallo", image=image)
    label.pack()
    labels = [label]

    button_frame = tk.Frame(fenster)
    button_frame.pack(side=tk.LEFT, anchor=tk.S)

    tk.Button(
        button_frame,
        text="Create",
        command=partial(create_labels, image, frame, labels, label_texts),
    ).pack()
    tk.Button(
        button_frame, text="Extend", command=partial(extend_labels, labels)
    ).pack()
    tk.Button(
        button_frame, text="Clear", command=partial(destroy_labels, labels)
    ).pack()

    fenster.mainloop()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
elofranz
User
Beiträge: 6
Registriert: Sonntag 8. September 2019, 12:39

Danke für die schnellen Antworten !!

Also um neue werte zuzuweisen Labels löschen Liste überarbeiten und Labels neu anlegen.
Ach der Böse import*.
Eine Anfängerfrage, woher weiss man was man aus dem Modul braucht und was nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@elofranz: Die Frage verstehe ich nicht so ganz — Du weisst doch, egal ob Anfänger oder nicht, was Du brauchst wenn Du es brauchst. Ist ja nicht so das man alles beim ``import`` hinschreiben muss bevor man anfangen darf den Rest zu programmieren und das man nichts mehr an den ``import``-Anweisungen ändern darf wenn man mal angefangen hat den Teil nach den Importen zu schreiben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Nummer_42O
User
Beiträge: 4
Registriert: Samstag 21. September 2019, 14:37

__blackjack__ hat geschrieben: Donnerstag 19. September 2019, 18:56 @elofranz: Die Frage verstehe ich nicht so ganz — Du weisst doch, egal ob Anfänger oder nicht, was Du brauchst wenn Du es brauchst. Ist ja nicht so das man alles beim ``import`` hinschreiben muss bevor man anfangen darf den Rest zu programmieren und das man nichts mehr an den ``import``-Anweisungen ändern darf wenn man mal angefangen hat den Teil nach den Importen zu schreiben.
Und was genau ist das Problem an

Code: Alles auswählen

from <Lib> import *
? Schmiert dann bei euch Python ab, oder wie?
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hat BlackJack oben beschrieben.
Antworten