tkinter Eingabe von Parametern für code
-
- User
- Beiträge: 11
- Registriert: Sonntag 24. April 2022, 20:01
Hallo zusammen,
ich habe ein Dataframe mit Messwerten und den dazugehörigen Zeitstempeln. Beispielhaft sind erste 6 Zeilen des df nachfolgend dargestellt:
Datum_2021 Messwerte_2021
2021-01-01 00:15:00 224.0
2021-01-01 00:30:00 227.2
2021-01-01 00:45:00 222.4
2021-01-01 01:00:00 228.8
2021-01-01 01:15:00 222.4
Für die Filterung des df verwende ich nachfolgenden Befehl:
df_neu = df.loc[lambda x: (x.Datum_2021 > "2021-09-01") & (x.Datum_2021 < "2021-12-01")]
Jetzt möchte ich gerne eine GUI über tkinter aufbauen, welche es mir ermöglicht die Parameter "2021-09-01" und "2021-12-01" einzugeben, sodass diese in meinem Befehl verwendet werden kann.
Könnt ihr mir bitte hierzu eine Lösung nennen?
Viele Grüße
ich habe ein Dataframe mit Messwerten und den dazugehörigen Zeitstempeln. Beispielhaft sind erste 6 Zeilen des df nachfolgend dargestellt:
Datum_2021 Messwerte_2021
2021-01-01 00:15:00 224.0
2021-01-01 00:30:00 227.2
2021-01-01 00:45:00 222.4
2021-01-01 01:00:00 228.8
2021-01-01 01:15:00 222.4
Für die Filterung des df verwende ich nachfolgenden Befehl:
df_neu = df.loc[lambda x: (x.Datum_2021 > "2021-09-01") & (x.Datum_2021 < "2021-12-01")]
Jetzt möchte ich gerne eine GUI über tkinter aufbauen, welche es mir ermöglicht die Parameter "2021-09-01" und "2021-12-01" einzugeben, sodass diese in meinem Befehl verwendet werden kann.
Könnt ihr mir bitte hierzu eine Lösung nennen?
Viele Grüße
-
- User
- Beiträge: 11
- Registriert: Sonntag 24. April 2022, 20:01
Ja das könnte durchaus sein, sorry ich habe bisher noch nicht so wirklich mit tkinter gearbeitet und bin da noch ein Neuling.
Ich möchte quasi, dass durch die Eingabe in einer GUI und nach Betätigung eines Buttons die Parameter in den entsprechenden Befehl übergeben wird, sodass mein df durch die eingegebenen Parameter gefiltert wird.
Ich möchte quasi, dass durch die Eingabe in einer GUI und nach Betätigung eines Buttons die Parameter in den entsprechenden Befehl übergeben wird, sodass mein df durch die eingegebenen Parameter gefiltert wird.
Okay, dann brauchst du noch 'tkinter.Button'.
Wenn du spezielle Probleme mit deinem Code hast, kann man dir gezielter helfen.
So ist es schwierig, es sei denn du willst hier einen fertigen Code.
Grüße
Dennis
Wenn du spezielle Probleme mit deinem Code hast, kann man dir gezielter helfen.
So ist es schwierig, es sei denn du willst hier einen fertigen Code.
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
-
- User
- Beiträge: 11
- Registriert: Sonntag 24. April 2022, 20:01
Für die Eingabe von zwei beliebigen Werten innerhalb einer GUI habe ich folgenden code geschrieben:
from tkinter import *
def Start():
eingabewert1 = eingabe1.get()
eingabewert2 = eingabe2.get()
print(eingabewert1)
print(eingabewert2)
fenster = Tk()
fenster.title("Eingabe")
eingabe1 = Entry(fenster)
eingabe2 = Entry(fenster)
ok_button = Button(fenster, text="ok",command=Start)
eingabe1.pack()
eingabe2.pack()
ok_button.pack()
fenster.mainloop()
Durch betätigung des Buttons "ok" erhalte ich die eingegebenen Werte als Output. Ich möchte aber eine Anfangs- und eine Endzeit (Format 00:00) eingeben und diese Werte für meinen folgenden Befehl nutzen:
df.between_time("07:15", "08:45")
Sodass ich die "07:15" und "08:45" nicht mehr händisch in meinen code ändern muss, sondern über die GUI eingeben kann.
from tkinter import *
def Start():
eingabewert1 = eingabe1.get()
eingabewert2 = eingabe2.get()
print(eingabewert1)
print(eingabewert2)
fenster = Tk()
fenster.title("Eingabe")
eingabe1 = Entry(fenster)
eingabe2 = Entry(fenster)
ok_button = Button(fenster, text="ok",command=Start)
eingabe1.pack()
eingabe2.pack()
ok_button.pack()
fenster.mainloop()
Durch betätigung des Buttons "ok" erhalte ich die eingegebenen Werte als Output. Ich möchte aber eine Anfangs- und eine Endzeit (Format 00:00) eingeben und diese Werte für meinen folgenden Befehl nutzen:
df.between_time("07:15", "08:45")
Sodass ich die "07:15" und "08:45" nicht mehr händisch in meinen code ändern muss, sondern über die GUI eingeben kann.
-
- User
- Beiträge: 41
- Registriert: Montag 12. August 2019, 07:52
Vielleicht habe ich jetzt ein Brett vor dem Kopf, aber ist dein Problem dann nicht schon mit:
gelöst?

Code: Alles auswählen
df.between_time(eingabe1.get(), eingabe2.get())

Hallo,
verwende keine *-Importe, dadurch importierst du alle Namen die in 'tkinter' angelegt wurden in deinen Namensraum. Das kann zu Namenskollisionen und unübersichtlichen Code führen, da man nicht mehr nachvollziehen kann, wo welcher Name her kommt.
Auf Modulebene (der Code ohne Einrückungen) gehört nur Code, der Konstanten, Klassen und Funktionen definiert. Ausnahme ist der Einstiegspunkt in die 'main'-Funktion. Das ist auch übrigends die Funktion, aus der das Programm gesteuert wird.
Namen werden nicht durchnummeriert, es sei denn, die Nummer würde mal tatsächlich einen Mehrwert bieten.
Namen schreibt man in Python klein_mit_unterstrich, Ausnahmen sind Konstanten GANZ_GROSS und Klassen in PascalCase-Schreibweise.
Eine Funktion bekommt alles was sie braucht über Argumente. Dass sind die Namen in der Klammer hinter dem Funktionsnamen. Für deinen Fall musst du dich mit der Funktionsweise von 'partial' vertraut machen, damit du deiner aufrufenden Funktion Argumente übergeben kannst.
Fehlt dir für deinen Wunsch jetzt die Formatierung von zwei Strings?
Gibst du in ein Feld '7:15' und in das zweite '8:45' ein?
verwende keine *-Importe, dadurch importierst du alle Namen die in 'tkinter' angelegt wurden in deinen Namensraum. Das kann zu Namenskollisionen und unübersichtlichen Code führen, da man nicht mehr nachvollziehen kann, wo welcher Name her kommt.
Auf Modulebene (der Code ohne Einrückungen) gehört nur Code, der Konstanten, Klassen und Funktionen definiert. Ausnahme ist der Einstiegspunkt in die 'main'-Funktion. Das ist auch übrigends die Funktion, aus der das Programm gesteuert wird.
Namen werden nicht durchnummeriert, es sei denn, die Nummer würde mal tatsächlich einen Mehrwert bieten.
Namen schreibt man in Python klein_mit_unterstrich, Ausnahmen sind Konstanten GANZ_GROSS und Klassen in PascalCase-Schreibweise.
Eine Funktion bekommt alles was sie braucht über Argumente. Dass sind die Namen in der Klammer hinter dem Funktionsnamen. Für deinen Fall musst du dich mit der Funktionsweise von 'partial' vertraut machen, damit du deiner aufrufenden Funktion Argumente übergeben kannst.
Fehlt dir für deinen Wunsch jetzt die Formatierung von zwei Strings?
Gibst du in ein Feld '7:15' und in das zweite '8:45' ein?
Code: Alles auswählen
import tkinter as tk
from functools import partial
def get_entry(start, end):
print(f"{end.get()},{start.get()}")
def main():
window = tk.Tk()
window.title("Eingabe")
start = tk.Entry(window)
start.pack()
end = tk.Entry(window)
end.pack()
ok_button = tk.Button(window, text="ok", command=partial(get_entry, start, end))
ok_button.pack()
window.mainloop()
if __name__ == "__main__":
main()
"When I got the music, I got a place to go" [Rancid, 1993]
-
- User
- Beiträge: 41
- Registriert: Montag 12. August 2019, 07:52
@Dennis89: Warum partial() nutzen? Hat das besondere Vorteile? Man könnte die Argumente doch einfach per lambda-Funktion einfügen, dann fällt ein import weg und ing_datascience muss sich nicht über partial() den Kopf zerbrechen (falls lambda-Funktionen bekannt sind).
Code: Alles auswählen
ok_button = tk.Button(window, text="ok", command=lambda: get_entry(start, end))
In diesem Fall geht lambda auch, das hat aber den Nachteil, dass der Wert der Variablen nicht zum Zeitpunkt der Definition, sondern zum Zeitpunkt des Aufrufst ausgewertet werden, das ist insbesondere bei for-Schleifen ein beliebter Fehler. Deshalb ist `partial` vorzuziehen, um Fehlerquellen zu vermeiden.
Diese Frage habe ich auch schon gestellt. Siehe hier: viewtopic.php?p=401788#p401788Onomatopoesie hat geschrieben: ↑Mittwoch 4. Mai 2022, 09:12 @Dennis89: Warum partial() nutzen? Hat das besondere Vorteile? Man könnte die Argumente doch einfach per lambda-Funktion einfügen, dann fällt ein import weg und ing_datascience muss sich nicht über partial() den Kopf zerbrechen (falls lambda-Funktionen bekannt sind).Code: Alles auswählen
ok_button = tk.Button(window, text="ok", command=lambda: get_entry(start, end))
-
- User
- Beiträge: 41
- Registriert: Montag 12. August 2019, 07:52
Ich experimentiere gerade mit partial(), da ich ja nun gerade gelernt habe, dass lambda-Funktionen offenbar nicht der Königsweg sind im Zusammenspiel mit command in tkinter. Nun spiele ich gerade herum und stelle fest, dass ein Code, der mit lambda problemlos läuft, mit partial() nicht klappt. Das deutet darauf hin, dass ich das Konzept nicht kapiert habe ...
Ich bekomme nur 1en geliefert - der Generator tut offenbar nichts. Mit lambda hat das Beispiel funktioniert.
Hat jemand ein Werkzeug, um das Brett vor meinem Kopf zu entfernen?

Code: Alles auswählen
import tkinter as tk
from functools import partial
def einfache_funktion(int_var):
print(int_var)
def test_cases():
int_liste = [1, 2, 3, 4, 5, 6]
for i in int_liste:
yield i
root = tk.Tk()
argument = test_cases()
button = tk.Button(root, text="Klick mich", command=partial(einfache_funktion, next(argument)))
button.pack()
root.mainloop()
Hat jemand ein Werkzeug, um das Brett vor meinem Kopf zu entfernen?

- __blackjack__
- User
- Beiträge: 13919
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@Onomatopoesie: Die `einfache_funktion()` kann man einfacher definieren: ``einfache_funktion = print`` hätte es auch getan. Oder einfach gleich `print()` verwenden.
Namen sollten weder Grunddatentypen enthalten wie `int` oder `liste` noch nichtssagende Zusätze wie `var`, denn dass das eine Variable ist, sieht man ja alleine schon daran, dass man dort einen Namen vergeben hat.
Die `test_cases()`-Funktion kann man auch vereinfachen. Die Liste muss man nicht an einen Namen binden:
Statt einer ``for``-Schleife könnte man auch ``yield from`` verwenden:
Und da die Zahlen aufsteigend und zusammenhängend, beziehungsweise in einer einheitlichen Schrittweite sind, könnte man auch `range()` verwenden. Und das dann statt mit ``yield``/``yield from`` auch mit ``return`` und `iter()`:
Namen sollten weder Grunddatentypen enthalten wie `int` oder `liste` noch nichtssagende Zusätze wie `var`, denn dass das eine Variable ist, sieht man ja alleine schon daran, dass man dort einen Namen vergeben hat.
Die `test_cases()`-Funktion kann man auch vereinfachen. Die Liste muss man nicht an einen Namen binden:
Code: Alles auswählen
def iter_test_cases():
for i in [1, 2, 3, 4, 5, 6]:
yield i
Code: Alles auswählen
def iter_test_cases():
yield from [1, 2, 3, 4, 5, 6]
Code: Alles auswählen
def iter_test_cases():
return iter(range(1, 7))
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
— Scott Bellware
@Onomatopoesie: die einfache Funktion ist etwas zu einfach, denn ein Teil der Funktionalität hast Du in das partial auszulagern versucht.
Code: Alles auswählen
import tkinter as tk
from functools import partial
def einfache_funktion(test_cases):
next_testcase = next(test_cases)
print(next_testcase)
def iter_test_cases():
return iter(range(1,7))
def main():
root = tk.Tk()
testcases = iter_test_cases()
tk.Button(root, text="Klick mich", command=partial(einfache_funktion, testcases)).pack()
root.mainloop()
if __name__ == "__main__":
main()
-
- User
- Beiträge: 41
- Registriert: Montag 12. August 2019, 07:52
Ja, ich habe es jetzt (hoffe ich) verstanden. Partial wird letztlich nur einmal ausgeführt und die Argumente "eingefroren", während die lambda-Funktion immer wieder neu ausgeführt (erstellt) wird und neue Argumente übergebene werden können. So in etwa?
@Onomatopoesie: Die Erklärung stimmt so nicht ganz. Vielleicht wird es deutlicher, wenn du `partial` und `lambda` durch einfachere Sprachmittel ausdrückst:
Eine `partial`-Instanz ist also nichts anderes als ein Objekt, das eine Funktion `f` und eine Menge an Parametern `args` und `kwargs` speichert. Wenn sie aufgerufen wird, werden einfach nur die gespeicherten Parameter zusätzlich übergeben. Und deswegen wird das `next(argument)` nur einmal beim Erstellen der `partial`-Instanz ausgewertet und nicht bei jedem Aufruf.
`lambda` ist einfach nur eine Kurzschreibweise, um eine neue Funktion zu erstellen:
ist das gleiche wie
Die Funktion wird nur einmal erstellt, aber bei jedem Aufruf wird `next(args)` ausgeführt, weil das im Körper der Funktion steht.
Und der von Sirius3 erwähne Fall: Was gibt dieses Programm aus? Und warum?
Code: Alles auswählen
class partial:
def __init__(self, f, *args, **kwargs):
self.f = f
self.args = args
self.kwargs = kwargs
def __call__(self, *args, **kwargs):
return self.f(*self.args, *args, **self.kwargs, **kwargs)
`lambda` ist einfach nur eine Kurzschreibweise, um eine neue Funktion zu erstellen:
Code: Alles auswählen
f = lambda: einfache_funktion(next(args))
Code: Alles auswählen
def f():
return einfache_funktion(next(args))
Und der von Sirius3 erwähne Fall: Was gibt dieses Programm aus? Und warum?
Code: Alles auswählen
from functools import partial
def main():
functions = []
for i in range(5):
functions.append(partial(print, i))
for i in range(5):
functions.append(lambda: print(i))
for f in functions:
f()
if __name__ == "__main__":
main()
-
- User
- Beiträge: 41
- Registriert: Montag 12. August 2019, 07:52
Ausgabe ist: 0123444444
Ich interpretiere es so (nach deiner ausführlichen Erklärung):
Es wird von 0 bis 4 iteriert und in der Zählschleife "i" gespeichert. Partial gibt die Funktion "print" mit dem aktuellen Argument "i" zurück und das Objekt wird in der Liste gespeichert, so dass in der Liste die Objekte mit der Funktion print samt dem aktuellen Zähler liegen (print(0), print(1), print(2), print(3), print(4)).
Die zweite Schleife tut (augenscheinlich) das Gleiche. Allerdings wird hier lediglich die Funktion erstellt - das Argument wird nicht ausgewertet, es wird nur die Referenz zu "i" übergeben. Hier werden also die Funktionen (print(i), print(i), print(i), print(i), print(i)) in der Liste gespeichert.
Beim Funktionsaufruf in der dritten Schleife erhalte ich also erwartungsgemäß die 01234, weil die Argumente bereits durch Partial gespeichert wurden und der Funktion print übergeben werden. Die Lambda-Funktionen aber erhalten einfach die Referenz zu "i", weil die Funktionen ja nicht in der Schleife ausgeführt worden sind, sondern erst durch den Aufruf in der dritten Schleife. Die for-Schleife ist aber schon durchgelaufen (das ist ja nicht Teil der lambda-Funktion), sodass i == 4 im Speicher liegt. Folglich werden 5x die 4 ausgegeben.
So etwa?

Ich interpretiere es so (nach deiner ausführlichen Erklärung):
Es wird von 0 bis 4 iteriert und in der Zählschleife "i" gespeichert. Partial gibt die Funktion "print" mit dem aktuellen Argument "i" zurück und das Objekt wird in der Liste gespeichert, so dass in der Liste die Objekte mit der Funktion print samt dem aktuellen Zähler liegen (print(0), print(1), print(2), print(3), print(4)).
Die zweite Schleife tut (augenscheinlich) das Gleiche. Allerdings wird hier lediglich die Funktion erstellt - das Argument wird nicht ausgewertet, es wird nur die Referenz zu "i" übergeben. Hier werden also die Funktionen (print(i), print(i), print(i), print(i), print(i)) in der Liste gespeichert.
Beim Funktionsaufruf in der dritten Schleife erhalte ich also erwartungsgemäß die 01234, weil die Argumente bereits durch Partial gespeichert wurden und der Funktion print übergeben werden. Die Lambda-Funktionen aber erhalten einfach die Referenz zu "i", weil die Funktionen ja nicht in der Schleife ausgeführt worden sind, sondern erst durch den Aufruf in der dritten Schleife. Die for-Schleife ist aber schon durchgelaufen (das ist ja nicht Teil der lambda-Funktion), sodass i == 4 im Speicher liegt. Folglich werden 5x die 4 ausgegeben.
So etwa?

@Onomatopoesie: Ja. Mit der Ausnahme, dass es im Fall von `lambda` keine Referenz ist, die gespeichert wird, sondern der Name `i`.
Je nach dem, was man mit „Referenz“ meint, gibt es entweder in Python keine Referenzen, oder alles ist eine Referenz. Deswegen ist es IMHO kontraproduktiv, in Python von Referenzen zu sprechen.
Je nach dem, was man mit „Referenz“ meint, gibt es entweder in Python keine Referenzen, oder alles ist eine Referenz. Deswegen ist es IMHO kontraproduktiv, in Python von Referenzen zu sprechen.
Alles natürlich richtig. Aber man könnte das auch lösen, indem man der Lambda-Funktion den Wert explizit übergibt:
Code: Alles auswählen
#!/usr/bin/env python
from functools import partial
def main():
functions = []
for i in range(5):
functions.append(partial(print, i))
for i in range(5):
functions.append(lambda i=i: print(i))
for f in functions:
f()
if __name__ == "__main__":
main()