Seite 1 von 1
"command" (Button) wartet nicht auf klick
Verfasst: Samstag 24. Oktober 2015, 12:58
von powaaah
Ich versuche gerade Python zu lernen und schreibe parallel dazu ein kleines Programm. Im ersten Schritt möchte ich einen Auswahlknopf für jede angelegte Person erstellen. Klickt man auf den Button soll der Nutzer ausgewählt sein und dann ein paar Daten angezeigt bekommen. Das Programm funktioniert soweit, dass die Buttons dynamisch erzeugt werden, je nachdem wer sich angelegt hat. Im echten Programm lasse ich eine Datei auslesen in der die Nutzer stehen. Hier zum anschauen habe ich einfach zwei Nutzer in einer Liste festgelegt.
Mein Problem ist nun, dass das Programm nicht auf einen Klick, auf einen Button, wartet und dann die Funktion (nutzer_aufrufen) ausführt, sondern einfach beide Möglichkeiten direkt ausführt.
Daran angefügt, wie schaffe ich es, dass das alte Fenster geschlossen wird und ein neues Fenster sich öffnet? Zum Beispiel: Drücke ich auf neuer Nutzer erscheint das Eingabefeld bisher unter den drei Buttons. Es sollte jedoch ein komplett neues Fenster entstehen mit Eingabefeld usw.. Es müsste also irgendein Befehl in die Funktion neuer_nutzer eingefügt werden, der das vorherige Fenster schließt.
Code: Alles auswählen
from tkinter import *
daten_nutzer1 = open("nutzer1.txt", "w")
schreiben1 = daten_nutzer1.write("1 2 3")
daten_nutzer1.close()
daten_nutzer2 = open("nutzer2.txt", "w")
schreiben2 = daten_nutzer2.write("4 5 6")
daten_nutzer2.close()
class Program:
def __init__(self):
benutzer = ["Nutzer1", "Nutzer2"]
for i in benutzer:
nameButton=Button(text=i, width=20, command=self.nutzer_aufrufen(i)).pack()
def nutzer_aufrufen(self, Name):
dateiname = Name+".txt"
ausgewaehlter_benutzer = open(dateiname, "r")
for daten in ausgewaehlter_benutzer:
daten = list(daten.split(" "))
print(daten)
def neuer_nutzer():
nameEntry = Entry().pack()
pass
neuerNutzer_Button = Button(text="Neuer Nutzer?", width=20, command=neuer_nutzer).pack()
program = Program()
mainloop()
Re: "command" (Button) wartet nicht auf klick
Verfasst: Samstag 24. Oktober 2015, 13:11
von Alfons Mittelmeyer
Ist klar, dass das direkt gleich ausgeführt wird. Denn hier rufst Du das ja gleich auf:
Richtig wäre:
Noch richtiger:
Code: Alles auswählen
command=lambda index = i, function=self.nutzer_aufrufen: function(index)
Man kann auch partial nehmen statt lambda.
Re: "command" (Button) wartet nicht auf klick
Verfasst: Samstag 24. Oktober 2015, 13:29
von Alfons Mittelmeyer
Und für Fenster benutze ein Toplevel. Das kannst Du dann mit destroy schließen.
Re: "command" (Button) wartet nicht auf klick
Verfasst: Sonntag 25. Oktober 2015, 10:17
von powaaah
Vielen Dank es funktioniert.
Was hat command aber für einen Sinn, wenn es eben nicht wartet bis der Button geklickt wird? Im Endeffekt entsteht durch die lambda ... Funktion ja nichts anderes als der gleiche Befehl, den ich reingeschrieben hatte. Warum wird also diese lambda Funktion nicht sofort ausgeführt der direkte Verweis auf die auszuführende Funktion jedoch schon?
Mit dem Toplevel bin ich mir nicht sicher ob es das ist was ich meine. Mir scheint, das Toplevel immer ein komplett neues Fenster anlegt.
Nehmen wir an ich habe ein Formular mit vielen Eingabefeldern und benötige für alle Eingabefelder 2 "Seiten". Das heißt nach der ersten Seite klicke ich auf "weiter" und es erscheint die nächste Seite im gleichen Fenster mit dem neuen Inhalt. Das wird sicher ein ziemlich einfacher Befehl sein. Bei meinen Recherchen bin ich jedoch noch nicht darauf gestoßen. Oder nutze ich in dem Fall die gleiche Funktion nur mit anderen Parametern?
Re: "command" (Button) wartet nicht auf klick
Verfasst: Sonntag 25. Oktober 2015, 10:56
von Sirius3
@powaaah: Du mußt zwischen einer Funktion self.nutzer_aufrufen und dem Aufrufen der Funktion self.nutzer_aufrufen(i) unterscheiden. In Deinem Fall kommt noch hinzu, dass Du Dir den aktuellen Zustand der Variable i beim Aufruf von Button merken mußt. Da ist es mMn verständlicher partial zu benutzen:
Code: Alles auswählen
Button(text=i, width=20, command=functools.partial(self.nutzer_aufrufen, i))
Re: "command" (Button) wartet nicht auf klick
Verfasst: Sonntag 25. Oktober 2015, 11:58
von Sirius3
@powaaah: Wenn Du mehrere Seiten anzeigen willst, nimmst Du am besten mehrere Frames im Hauptfenster.
Zum Programm selbst: *-importe solltest Du nicht verwenden, man lädt bei Tk damit mehrere hundert Namen in den eigenen Namensraum, ohne Kontrolle darüber zu haben. Ich nehme immer "import tkinter as tk" und kann dann über "tk.xxx" auf alle Attribute des Moduls zugreifen.
Auf oberster Ebene sollten außer Funktions-/Klassendefinitionen und Konstanten nichts stehen. Meist definiert man sich eine main-Funktion die über das Idiom:
aufgerufen wird. Wenn Du Dateien mit dem Statement "with ..." öffnest, vergisst Du sie auch nicht wieder zu schließen, wie in "nutzer_aufrufen". "i" ist ein schlechter Variablennamen. Besser wäre "benutzer", aber der kolidiert mit Deiner Liste "benutzer" weil im Deutschen Singular und Plural gleich geschrieben werden. Im Englischen wäre das einfacher: user bzw. users.
Was ist der Rückgabewert von "pack" und was für einen Sinn hat es, diesen an eine Variable zu binden?
"ausgewaehlter_benutzer" ist ein schlechter Name für ein File-Objekt. Klassen-Attribute sind nur in speziellen Situationen sinnvoll. "neuerNutzer_Button" gehört da definitiv nicht dazu, zumal er nur den Wert None hat.
Re: "command" (Button) wartet nicht auf klick
Verfasst: Sonntag 25. Oktober 2015, 12:10
von __deets__
powaaah hat geschrieben:Was hat command aber für einen Sinn, wenn es eben nicht wartet bis der Button geklickt wird? Im Endeffekt entsteht durch die lambda ... Funktion ja nichts anderes als der gleiche Befehl, den ich reingeschrieben hatte. Warum wird also diese lambda Funktion nicht sofort ausgeführt der direkte Verweis auf die auszuführende Funktion jedoch schon?
command kann nicht warten auf irgendetwas, es ist einfach nur ein ganz normaler Parameter. Wichtig ist, was dieser Parameter ist: ein aufrufbares Objekt (callable) ist. Es gibt verschieden Moeglichkeiten, das zu erreichen - eben ein lambda, welches ein solches callable erzeugt, oder ein partial-Objekt, das ebenfalls so etwas darstellt.
Re: "command" (Button) wartet nicht auf klick
Verfasst: Sonntag 25. Oktober 2015, 18:24
von Alfons Mittelmeyer
powaaah hat geschrieben:Was hat command aber für einen Sinn, wenn es eben nicht wartet bis der Button geklickt wird? Im Endeffekt entsteht durch die lambda ... Funktion ja nichts anderes als der gleiche Befehl, den ich reingeschrieben hatte. Warum wird also diese lambda Funktion nicht sofort ausgeführt der direkte Verweis auf die auszuführende Funktion jedoch schon?
Um das mal etwas zu verdeutlichen. Wenn Du eine Funktion hast, die etwa heißt: myfunction
Dann schreibst Du bei command: command = myfunction
Aber bitte nicht: command = myfunction()
Wenn Du geschrieben hast: command = myfunction
dann ruft der Klick auf den Button auf: myfunction()
Das funktioniert soweit gut, solange man keine Parameter übergeben muss. Wenn man Parameter übergeben will, dann braucht man eine Funktion, welche die aufzurufende Funktion nicht ausführt, sondern lediglich eine Funktion erzeugt, welche die aufzurufende Funktion aufruft und eine Referenz auf diese Funktion zurückliefert. Und genau das macht lambda:
a = lambda: print('Hello')
Dadurch wird print("Hello") noch nicht ausgeführt. Erst wenn man a() aufruft. Und statt lambda kann man auch partial nehmen, oder sich selbst etwas stricken.
Das wäre etwa partial selber gestrickt:
Code: Alles auswählen
class MyPartial:
def __init__(self,function,args):
self.function = function
self.args = args
def execute(self):
self.function(*(self.args))
def mypartial(function,*args):
return MyPartial(function,args).execute
def test_function(a,b):
print(a)
print(b)
myref = mypartial(test_function,"hello","nice day")
print('Does it work?')
myref()
Re: "command" (Button) wartet nicht auf klick
Verfasst: Sonntag 25. Oktober 2015, 21:50
von Alfons Mittelmeyer
powaaah hat geschrieben:Mit dem Toplevel bin ich mir nicht sicher ob es das ist was ich meine. Mir scheint, das Toplevel immer ein komplett neues Fenster anlegt.
Nehmen wir an ich habe ein Formular mit vielen Eingabefeldern und benötige für alle Eingabefelder 2 "Seiten". Das heißt nach der ersten Seite klicke ich auf "weiter" und es erscheint die nächste Seite im gleichen Fenster mit dem neuen Inhalt. Das wird sicher ein ziemlich einfacher Befehl sein. Bei meinen Recherchen bin ich jedoch noch nicht darauf gestoßen. Oder nutze ich in dem Fall die gleiche Funktion nur mit anderen Parametern?
Sorry, dass ich das mit den 'neuen Fenster' mißverstanden hatte. Ich hatte gedacht, wenn Du 'neues Fenster' schreibst, dass Du auch neues Fenster meinst.
Aber offensichtlich geht es Dir darum, zwei Seiten im selben Fenster anzuzeigen, und nicht in einem 'neuen Fenster'. Da kannst Du dann zwei Frames nehmen oder zwei Labelframes. Wenn Du ein GridLayout nimmst, kannst Du ja beide Frames in gleicher Zeile und Spalte positionieren. Vor mainloop sieht man sie ja sowieso nicht. Und beim zweiten Frame machst Du ein grid_remove().
Und umschalten tust Du dann mit:
bzw.
Oder wenn sie gleiche Größe haben, kannst Du das Layout auch so lassen und schiebst mal den einen oder anderen Frame in den Vordergrund, etwa mit: