Hallo Community,
erstmal hallo an alle und gleichzeitig sorry für meine billo frage.
habe vor kurzem angefangen python als meine erste programmiersprache zu lernen und bin auch schon einige tutorials durchgegangen und habe auch explizit nach meinem problemchen gesucht aber die ganzen lösungsansätze wollen nicht so richtig funktionieren.
Zum Thema:
Ziel: einfaches fahrtenbuch mit tkinter
habe tkinter importiert und habe in der __init__ das design festgelegt.
das funzt alles und sieht "wunderschön" aus.
ein kleiner ausschnitt aus meinem code:
import pandas as pd
import tkinter as tk
import tkinter.messagebox
class framework(tk.Frame):
carlist = ["AutoA",
"AutoB",
"Diverse"
]
stats = pd.read_csv('input.csv')
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.choosecar = tk.OptionMenu(self, tk.StringVar(), *self.carlist).grid(row=0, column=0, pady=1, padx=1)
self.show_data = tk.Entry(self, state=tk.DISABLED).grid(row=1, column=0, pady=1, padx=1)
weiter unten aber weiterhin in der klasse framework habe ich denn eine funktion erstellt:
def show_data(self):
stats = pd.read_csv('input.csv')
print(stats)
wie gesagt, ist mit sicherheit die totale billofrage, aber wie kann ich meinem tk.Entry sagen das er auf die funktion def show_data(self): zugreifen soll damit er den inhalt der input.csv im Entry feld anzeigt?
Grüße
Michael
Aufrufen einer funktion über tk.Entry
Dein Quelltext ist in der aktuellen Version nicht verständlich, da die Einrückung nicht passt. Dafür gibt es im Editor in diesem Forum den Button </>, um Quelltexte einzufügen.
Mir fällt auf, dass du Klassen auf seltsame Weise instantiierst. Also etwa
statt
Gibt es dafür einen bestimmten Grund?
Deine Frage verstehe ich nicht ganz. "Entry" ist ein Eingabefeld. Wie - und vor allem in welchen Situationen - soll das Eingabefeld auf die Funktion "zugreifen"?
Hier habe ich einige einsteigerfreundliche Funktionalitäten um tkinter einmal zusammengefasst. Vielleicht hilft dir das schon.
Mir fällt auf, dass du Klassen auf seltsame Weise instantiierst. Also etwa
Code: Alles auswählen
tk.Frame.__init__(self, master)
Code: Alles auswählen
tk.Frame(master)
Deine Frage verstehe ich nicht ganz. "Entry" ist ein Eingabefeld. Wie - und vor allem in welchen Situationen - soll das Eingabefeld auf die Funktion "zugreifen"?
Hier habe ich einige einsteigerfreundliche Funktionalitäten um tkinter einmal zusammengefasst. Vielleicht hilft dir das schon.
- __blackjack__
- User
- Beiträge: 14052
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@pintman: Da wird kein `Frame` instanziert sondern initialisiert. Den `Frame` gibt es ja schon — das ist `self`, weil die Klasse `framework` von `Frame` erbt. Und in der `framework.__init__()` wird so die `__init__()` der Basisklasse aufgerufen.
Du benutzt da `super()` für — natürlich falsch, wie jeder, weil kaum jemand `super()` und die Folgen davon versteht. Die `BlankWindow`-Klasse müsste nämlich auch `super()` in der `__init__()` verwenden, und beide `__init__()`\s müssten noch beliebige Positions- und Schlüsselwortargumente entgegennehmen und weiterreichen. Siehe auch Python's Super is nifty, but you can't use it.
Und bei von `tkinter` abgeleiteten Klassen wäre `super()` falsch weil `tkinter` selbst kein `super()` verwendet.
Was mir an den Beispielen in dem Jupyter-Notebook nicht so gut gefällt sind übrigens Namen wie `btn` und `lbl`. Ist ja nicht so das man für Vokale extra zahlen müsste, oder Extrapunkte bekäme wenn man Namen auf drei Zeichen beschränkt.
@trapper-keeper: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Die Klasse müsste also `Framework` geschrieben werden. Wobei das ein sehr generischer Name ist und nicht beschreibt was in diesem `Frame` an Information enthalten sein wird.
`carlist` auf Klassenebene ist eine Konstante, sollte also komplett gross geschrieben werden. Allerdings ohne das `list`, denn Grunddatentypen gehören nicht in Namen. Man ändert solche Typen öfter mal und dann müsste man im ganzen Programm die betroffenen Namen ändern, oder hat falsche, irreführende Namen im Quelltext stehen.
`stats` gehört nicht auf Klassenebene denn das ist keine Konstante und das reine definieren von Klassen sollte keine Seiteneffekte wie das einlesen von Datendateien haben. Das gilt generell für Code der auf Klassenebene steht. Man muss Module impirtieren können, ohne das so etwas passiert (und fehlschlagen kann).
Widgets layouten sich nicht selbst. Der `self.grid()`-Aufruf gehört nicht in die `__init__()`. Wo ein Widget in der GUI angeordnet wird, entscheidet der Code der das Widget erstellt hat, nicht das Widget selbst. Das sollte nichts darüber wissen wo und wie es layouted wird.
Du hast eine Methode `show_car()` und bindest in der `__init__()` ein `Entry` an das gleiche Attribut. Damit hat das Objekt dann keine Methode mit dem Namen mehr, denn ein Attribut/Name kann immer nur an *ein* Objekt zur gleichen Zeit gebunden sein. Wenn dem nicht so wäre, wie sollte Python dann bei einem `instance.show_car` entscheiden ob Du die Methode oder das `Entry` meinst‽
`show_car` ist auch kein guter Name für ein `Entry` genau wie `choosecar` (oder `choose_car`) kein guter Name für ein `OptionMenu` ist, denn die beiden Namen beschreiben Tätigkeiten und keine ”Dinge”. Tätigkeiten sind aber für Funktionen und Methoden, denn die tun etwas.
In beiden Fällen wird `None` an die Namen gebunden was sicher nicht gewollt war. `grid()` gibt, wie alle Layout-Methoden ”nichts” zurück.
Das `OptionMenu` braucht auch nicht unbendingt einen Namen/ein Attribut, sofern man nicht später noch mal auf dieses Objekt zugreifen muss.
Dafür sollte man das `StringVar`-Objekt an ein Attribut binden wenn man später auch mal herausfinden will was ausgewählt wurde.
Falls der Name der Datendatei mehrfach im Quelltext vorkommt, sollte man dafür eine Konstante definieren, damit man den leicht ändern kann.
Zwischenstand (ungetestet):
Die Frage macht nicht so wirklich Sinn denn `Entry`-Objekte sind passiv, die greifen auf nichts zu, insbesondere nicht auf Methoden. Wie und wann sollte ein `Entry`-Objekt denn eine Methode aufrufen? Man kann von Methoden aus auf `Entry`\s zugreifen, zum Beispiel um den Text abzufragen, zu löschen, oder zu ergänzen.
Sollte das beispielsweise zur Anzeige von einem Datum zum ausgewählten Auto dienen, dann könnte man auf eine Änderung der Auswahl im `OptionMenu` reagieren und den `Entry`-Inhalt aktualisieren. Dazu müsste man `add_trace()` auf `StringVar` verwenden um einen Rückruf zu bekommen wenn sich der Inhalt des `StringVar`-Objekts verändert hat.
Die Werte für das `OptionMenu` sollten vielleich besser aus der Datei kommen und nicht hart im Programm kodiert werden.
Du benutzt da `super()` für — natürlich falsch, wie jeder, weil kaum jemand `super()` und die Folgen davon versteht. Die `BlankWindow`-Klasse müsste nämlich auch `super()` in der `__init__()` verwenden, und beide `__init__()`\s müssten noch beliebige Positions- und Schlüsselwortargumente entgegennehmen und weiterreichen. Siehe auch Python's Super is nifty, but you can't use it.
Und bei von `tkinter` abgeleiteten Klassen wäre `super()` falsch weil `tkinter` selbst kein `super()` verwendet.
Was mir an den Beispielen in dem Jupyter-Notebook nicht so gut gefällt sind übrigens Namen wie `btn` und `lbl`. Ist ja nicht so das man für Vokale extra zahlen müsste, oder Extrapunkte bekäme wenn man Namen auf drei Zeichen beschränkt.
@trapper-keeper: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Die Klasse müsste also `Framework` geschrieben werden. Wobei das ein sehr generischer Name ist und nicht beschreibt was in diesem `Frame` an Information enthalten sein wird.
`carlist` auf Klassenebene ist eine Konstante, sollte also komplett gross geschrieben werden. Allerdings ohne das `list`, denn Grunddatentypen gehören nicht in Namen. Man ändert solche Typen öfter mal und dann müsste man im ganzen Programm die betroffenen Namen ändern, oder hat falsche, irreführende Namen im Quelltext stehen.
`stats` gehört nicht auf Klassenebene denn das ist keine Konstante und das reine definieren von Klassen sollte keine Seiteneffekte wie das einlesen von Datendateien haben. Das gilt generell für Code der auf Klassenebene steht. Man muss Module impirtieren können, ohne das so etwas passiert (und fehlschlagen kann).
Widgets layouten sich nicht selbst. Der `self.grid()`-Aufruf gehört nicht in die `__init__()`. Wo ein Widget in der GUI angeordnet wird, entscheidet der Code der das Widget erstellt hat, nicht das Widget selbst. Das sollte nichts darüber wissen wo und wie es layouted wird.
Du hast eine Methode `show_car()` und bindest in der `__init__()` ein `Entry` an das gleiche Attribut. Damit hat das Objekt dann keine Methode mit dem Namen mehr, denn ein Attribut/Name kann immer nur an *ein* Objekt zur gleichen Zeit gebunden sein. Wenn dem nicht so wäre, wie sollte Python dann bei einem `instance.show_car` entscheiden ob Du die Methode oder das `Entry` meinst‽
`show_car` ist auch kein guter Name für ein `Entry` genau wie `choosecar` (oder `choose_car`) kein guter Name für ein `OptionMenu` ist, denn die beiden Namen beschreiben Tätigkeiten und keine ”Dinge”. Tätigkeiten sind aber für Funktionen und Methoden, denn die tun etwas.
In beiden Fällen wird `None` an die Namen gebunden was sicher nicht gewollt war. `grid()` gibt, wie alle Layout-Methoden ”nichts” zurück.
Das `OptionMenu` braucht auch nicht unbendingt einen Namen/ein Attribut, sofern man nicht später noch mal auf dieses Objekt zugreifen muss.
Dafür sollte man das `StringVar`-Objekt an ein Attribut binden wenn man später auch mal herausfinden will was ausgewählt wurde.
Falls der Name der Datendatei mehrfach im Quelltext vorkommt, sollte man dafür eine Konstante definieren, damit man den leicht ändern kann.
Zwischenstand (ungetestet):
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
import pandas as pd
CARS_DATA_FILENAME = "input.csv"
class Framework(tk.Frame):
CAR_NAMES = ["AutoA", "AutoB", "Diverse"]
def __init__(self, cars_data_filename, master=None):
tk.Frame.__init__(self, master)
self.cars_data_filename = cars_data_filename
self.car_var = tk.StringVar()
tk.OptionMenu(self, self.car_var, *self.CAR_NAMES).grid(
row=0, column=0, pady=1, padx=1
)
#
# TODO Better name for this attribute.
#
self.entry = tk.Entry(self, state=tk.DISABLED)
self.entry.grid(row=1, column=0, pady=1, padx=1)
def show_data(self):
print(pd.read_csv(self.cars_data_filename))
Sollte das beispielsweise zur Anzeige von einem Datum zum ausgewählten Auto dienen, dann könnte man auf eine Änderung der Auswahl im `OptionMenu` reagieren und den `Entry`-Inhalt aktualisieren. Dazu müsste man `add_trace()` auf `StringVar` verwenden um einen Rückruf zu bekommen wenn sich der Inhalt des `StringVar`-Objekts verändert hat.
Die Werte für das `OptionMenu` sollten vielleich besser aus der Datei kommen und nicht hart im Programm kodiert werden.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Danke für den Hinweis. Ich habe einen Issue daraus gemacht. Wenn jemand Erfahrungen mit github und jupyter notebooks sammeln möchte, wäre dies eine gute Gelegenheit für einen einfachen ersten Einstieg.__blackjack__ hat geschrieben: Mittwoch 22. Juli 2020, 06:12 @pintman:
Was mir an den Beispielen in dem Jupyter-Notebook nicht so gut gefällt sind übrigens Namen wie `btn` und `lbl`. Ist ja nicht so das man für Vokale extra zahlen müsste, oder Extrapunkte bekäme wenn man Namen auf drei Zeichen beschränkt.![]()

https://github.com/tbs1-bo/software-101/issues/12
Die Sache mit dem super werde ich selbst noch einmal anschauen.