Button Erstellung in Schleife

Fragen zu Tkinter.
Antworten
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Guten Tag zusammen.

Ich habe folgendes Problem. Ich will Daten aus einer Datenbank auslesen und diese dann durch Buttons aufrufen und in ein neues Fenster kommen. Das auslesen etc ist kein Problem, allerdings wird in der Schleife nur der letzte Datensatz als Button ausgegeben. Ich bin noch ein ziemlicher Anfänger was die Programmierung mit Python angeht.

Im folgenden mal ein Teil des Quellcodes:

cursor.execute("SELECT Werkzeugname, Schrankfach, Ausgeliehen FROM Werkzeuge")

daten_Werkzeuge = cursor.fetchall()

i = 0
x = 50
y = 180

while i<len(daten_Werkzeuge):
daten = daten_Werkzeuge
Werkzeugname = []
Werkzeugname = daten[0]
Schrankfach = []
Schrankfach = daten[1]
Ausgeliehen = []
Ausgeliehen = daten[2]

labelAW = Label(FenstergefundenerMitarbeiter,image=ImageAW)
labelAW.place(x=0, y=0, width=800, height=480)

#Vorname ausgeben
labelFenstergefundenerMitarbeiter_Vorname = Label(master=FenstergefundenerMitarbeiter, text ="Vorname:")
labelFenstergefundenerMitarbeiter_Vorname.place(x=300, y = 20, width = 100, height=20)

labelFenstergefundenerMitarbeiter_Vorname1 = Label(master=FenstergefundenerMitarbeiter, text =Vorname)
labelFenstergefundenerMitarbeiter_Vorname1.place(x=300, y = 40, width = 100, height=30)

#Nachname ausgeben
labelFenstergefundenerMitarbeiter_Nachname = Label(master=FenstergefundenerMitarbeiter, text ="Nachname:")
labelFenstergefundenerMitarbeiter_Nachname.place(x=420, y = 20, width = 100, height=20)

labelFenstergefundenerMitarbeiter_Nachname1 = Label(master=FenstergefundenerMitarbeiter, text =Nachname)
labelFenstergefundenerMitarbeiter_Nachname1.place(x=420, y = 40, width = 100, height=30)

gesamt = str(Schrankfach) + " " + Werkzeugname

#Buttonerstellung
if Ausgeliehen == "Nein":
button = Button(master=FenstergefundenerMitarbeiter, text = gesamt , bg="#00FF00" , command= print("20"))
button.place(x = x, y = y, width = 100, height = 50)
else:
button = Button(master=FenstergefundenerMitarbeiter, text = gesamt , bg="#FF0000" , command=print("20"))
button.place(x = x, y = y, width = 100, height = 50)

x += 120
if x > 650 and y == 180:
x = 50
y = 250

ButtonMitarbeiterhinzugefügt_schliessen = Button(master=FenstergefundenerMitarbeiter, text ="Zurück", command=zurückWerkzeugentnahme)
ButtonMitarbeiterhinzugefügt_schliessen.place(x=50, y=330, width = 330, height=100)

i += 1

Die "print("20") Funktion dient erstmal nur zur Überprüfung. Später soll dann sobald ich den Button klicke, die neue Seite aufgerufen werden.
Ich bin für jede Hilfe sehr dankbar.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Theynk: die while-Schleife ist ein Anti-Pattern. Viel besser Lesbar ist es, direkt mit einer for-Schleife über die Einträge in Deiner Liste `daten_Werkzeuge` zu iterieren. Was ist der Sinn, die Variablen `Werkzeugname`, `Schrankfach` und `Ausgeliehen` an eine leere Liste zu binden um den Wert gleich in der nächsten Zeile wieder zu überschreiben?

place sollte nicht verwendet werden, weil das für jede Monitorauflösung und jedes Betriebsystem angepasst werden muß. Nimm pack. Zudem packst Du in Deiner Schleife alle Labels übereinander. Variablennamen sollen zwar aussagekräftig sein, aber 42 Zeichen lang müssen sie auch nicht sein. Da es sich um lokale Variablen handelt ist die Information, dass es sich um ein Label handelt, dass es im Fenster gefundenerMitarbeiter ist auch überflüssig. `vorname` reicht völlig, oder weil es nur zwei Zeilen gültig ist, hier ausnahmsweise auch `label`. `Vorname` und `Nachname` sind nicht definiert.
Statt Strings mit str und + zusammenzustückeln nimmt man die format-Methode. `print("20")` ist ein Funktionsaufruf, der None zurückliefert. None als command an einen Knopf zu binden, ist ziemlich sinnfrei. Wenn sich zwei Zeilen nur in einem Parameter unterscheiden, definiert man diesen als Variable und hat nur einen Aufruf. Mit `x` und `y` wird zwar gerechnet, aber es wird nie verwendet.
Variablennamen werden generell klein_mit_unterstrich geschrieben.

Ich lass mal die konstanten Labels und Buttons weg, die haben in der Schleife sowieso nichts zu suchen, und komme auf das:

Code: Alles auswählen

        cursor.execute("SELECT Werkzeugname, Schrankfach, Ausgeliehen FROM Werkzeuge")
        for idx, (werkzeugname, schrankfach, ausgeliehen) in enumerate(cursor):
            row, column = divmod(idx, 4)
            gesamt = "{} {}".format(schrankfach, werkzeugname)
            color = "#00FF00" if ausgeliehen == "Nein" else "#FF0000"
            button = Button(master=fenster_gefundener_mitarbeiter, text=gesamt, bg=color, command=partial(was_auch_immer, werkzeugname))
            button.grid(row=row, column=column)
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Schon mal danke für deine Hilfe. Ich dachte man muss es vorher definieren, dass es sich um eine Liste handelt. Place habe ich genutzt, da das Programm nur über das offiziele Touch-Display läuft. Das mit den Labels habe ich ausversehen da rein gepackt. Habe ich jetzt mal direkt raus genommen. Ok das mit den Variablennamen merke ich mir. Dachte nur, dass man so genau sieht wofür das ist. Wie gesagt die print funktion war nur, um erstmal zu testen ob die Buttons erstellt werden.

Ich habe deinen Quellcode jetzt einmal kopiert und eingefügt, allerdings werden keine Buttons erstellt.

Was ist denn die Bedeutung von "in enimerate(cursor):" ebenso weiß ich nicht was "divmod" und bei command "partial" bedeutet. Wie gesagt ich bin noch ein Anfänger.
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Also ich habe jetzt mit deinen Verbesserungsvorschlägen es noch einmal mit meiner while-Schleife probiert und auf einmal klappt es. Muss ich dann nochmal überprüfen woran das liegt. Danach muss ich dann jetzt noch programmieren, dass wenn ich zu viele Datensätze auslese auf eine nächste Seite springen kann. Jetzt hätte ich nur dazu noch zwei Fragen. Ich habe gesehen, dass ein Eintrag zu lang ist. Könnte ich irgendwie ein Zeilenumbruch machen, so dass oben die Schrankfachnummer steht und unten der Werkzeugname? Und die zweite ist, dass ich jetzt testen wollte, ob ich die Variablen von den einzelnen Buttons übergeben kann. Hier werden die allerdings nur einmal am Anfang in die Shell geschrieben, beim Druck allerdings nicht.

Hier noch einmal der Code:
def weiterWerkzeugentnahme(Schrankfach, MitarbeiterNr):
print(Schrankfach)
print(MitarbeiterNr)

while i<len(daten_Werkzeuge):
daten = daten_Werkzeuge
Werkzeugname = daten[0]
Schrankfach = daten[1]
Ausgeliehen = daten[2]

gesamt = str(Schrankfach) + " " + Werkzeugname

#Buttonerstellung
if Ausgeliehen == "Nein":
button = Button(master=FenstergefundenerMitarbeiter, text = gesamt , bg="#00FF00" , command= weiterWerkzeugentnahme(Schrankfach, MitarbeiterNr)
button.place(x = x, y = y, width = 100, height = 50)
else:
button = Button(master=FenstergefundenerMitarbeiter, text = gesamt , bg="#FF0000" , command=weiterWerkzeugentnahme(Schrankfach, MitarbeiterNr)
button.place(x = x, y = y, width = 100, height = 50)

x += 120
if x > 650 and y == 180:
x = 50
y = 250

i += 1
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Ich habe es jetzt mit dem "lambda" Befehl hinbekommen, dass die Funktion nicht direkt ausgeführt wird, allerdings sobald ich jetzt ein Button betätige, wird immer nur die Variable von dem letzten Knopf übergeben und nicht die von z.B. dem 2. Knopf. Gibt es dafür eine Lösung, so dass ich wenn ich den 2. Button drücke, davon die Variablen übergeben werden?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sirius3: warum, glaubst Du, habe ich bei meiner Lösung Deines Problems `partial` benutzt? Alle Deine Fragen, was `enumerate`, `divmod` oder `partial` macht, kann man einfach durch einen Blick in die Dokumentation beantworten.
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Das weiß ich nicht xD Mit partial bekomme ich ich ein Syntax Fehler.

button = Button(master=fenster_gefundener_mitarbeiter, text = gesamt , bg=color , command= partial(weiterWerkzeugentnahme(Schrankfach, MitarbeiterNr))
button.place(x = x, y = y, width = 100, height = 50)

Der Fehler kommt auch mit .grid
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@Theynk: Du rufst ja auch `weiterWerkzeugentnahme` wieder auf, statt die Funktion an `partial` zu übergeben.
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Sorry aber das verstehe ich nicht. Ich kenne die Funktion ja gar nicht. Wie muss ich das denn an partial übergeben und kann dies dann aufrufen?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hast du dir mal die partial Dokumentation angeschaut? https://docs.python.org/3/library/funct ... ls.partial

Das Verhalten ist da klar beschrieben: du uebergibst eine Funktion, und Parameter. Die Rueckgabe ist dann wieder eine neue Funktion, die also fuer dein command als Callback/Rueckruffunktion geeignet ist, aber die Argumente die du vorher schon mitgegeben hast werden dann automatisch schon uebergeben. Also

Code: Alles auswählen

def testfunktion(ein_argument):
      print(ein_argument)

f = partial(testfunktion, "das ist alles nicht so schwer")
f() # jetzt ohne Argument aufrufbar!
Und dein Syntaxfehler kommt einfach von vergessenen Klammern, das hat aber mit partial nix zu tun.

Code: Alles auswählen

Button(.., command=lambda : 
geht auch nicht, fehlt auch die schliessende Klammer.
Theynk
User
Beiträge: 22
Registriert: Donnerstag 8. März 2018, 14:20

Super gut es klappt besten dank :) :) :)

Jetzt werde ich mich mal daran wagen, dass wenn die Daten aus fetchall() größer als 12 sind nur die ersten 12 auf der ersten Seite dargestellt wird und dann ein Button entsteht, von dem aus ich auf eine nächste Seite komme, wo dann die anderen dargestellt werden. Ich habe zwar noch keine Idee aber werde es mal versuchen. Nochmal besten dank ich habe da schon lange dran rumprobiert.
Antworten