@AngelFilmnMusic: Regeln sind ja dafür da, das man Ausnahmen machen kann, und bei einigen wenigen Modulen, die sehr viele Namen enthalten, ist es üblich eine kryptische Abkürzung zu verwenden. Wobei Tk ja selbst schon diese Abkürzung ist. Das Modul wird üblicherweise mit ``import tkinter as tk`` importiert. Dann werden Zeilen nicht zu lang weil man sonst mehrfach `tkinter` in einigen Zeilen hätte, aber man weiss trotzdem noch das `tk.Image` was anderes ist als `PIL.Image`.
Bei den Kassenautomaten ``kassenautomaten_displaylabel = []`` und da dann die Label rein. Sollten dir grundsätzlich das gleiche anzeigen, würde man die Label selbst eher gar nicht speichern, sondern eine `tkinter.StringVar` erstellen, die beiden Labeln als `textvariable` übergeben, und dann immer nur die `StringVar` verändern.
``GESAMTANZAHL_STELLPLAETZE = 500`` damit es als Konstante erkennbar ist. Die Gross-/Kleinschreibung bei `gesamtAnzahl_stellPlaetze` wäre komisch mit den Grossbuchstaben, und damit meine ich nicht mal Namenskonventionen, sondern Gesamtanzahl und Stellplätze sind doch auch im Deutschen jeweils *ein* Wort‽
Also `open()` muss man ja auch bei ``with`` verwenden, und wenn das beides im Endeffekt das selbe ist, dann wäre ``with`` ja überflüssig. Bei ``with`` ist sichergestellt, beim verlassen des ``with``-Blocks — egal aus welchem Grund — der Ressource aus der ``with``-Anweisung gesagt wird „räum mal auf“. Was bei Dateiobjekten „schliessen“ bedeutet. Klar kann man jetzt bei einem Dreizeiler öffnen/schreiben/schliessen anfangen zu Argumentieren was soll denn da schon schief gehen wenn ich da mal nicht ``with`` verwende. Das ist aber so ähnlich wie, „Ich kenne die Strecke und die ist kurz, dafür muss ich mich ja nicht anschnallen“. Das sollte einfach Routine sein/werden, bei Kontextmanagern auch tatsächlich ``with`` zu verwenden. Wobei das im Gegensatz zum Anschnallen, was ja eigentlich keine zusätzliche Arbeit ist, wenn das Routine geworden ist, ja sogar aus dem Dreizeiler einen Zweizeiler macht.
Mit Kodierung ist das `encoding`-Argument gemeint. Wenn man das nicht explizit angibt, ”rät” Python aufgrund der Systemeinstellungen welche Kodierung für den Text verwendet werden soll. Rechner können keinen Text sondern nur Zahlen, also werden Zeichen als Zahl kodiert und dabei ist leider nicht klar welche Zahl(en) für welches Zeichen verwendet werden in Dateien, denn in Dateien gibt es nur eine Folge von Zahlen zwischen 0 und 255 (inklusive). Es gibt einen Grundvorrat bei dem es zumindest in unserem Kulturkreis und wenn man keine Grossrechner verwendet, nahezu egal ist welche Kodierung man verwendet, weil ASCII eine Untermenge von sehr vielen gängigen Kodierungen ist, aber bei bei Ä, ö, ß, € & Co hören die Gemeinsamkeiten dann auf. Viele der klassischen Kodierungen können auch nur 256 unterschiedliche Zeichen kodieren, also ein Byte pro Zeichen, was für Unicode natürlich nicht ausreicht. Wenn man die Kodierung selbst in der Hand hat, bietet sich UTF-8 an. Das kann alle Unicode-Zeichen kodieren und gleichzeitig ist ASCII eine Untermenge. Ist also immer noch platzsparend für viele ”westliche” Texte.
Das mit dem ``+ ".txt"`` ist ungünstig wenn der Benutzer die Endung selbst eingibt, oder eine bereits vorhandene Datei mit Endung im Dateidialog auswählt. Da sollte man vorher prüfen ob die Endung bereits existiert, bevor man sie dann eventuell verdoppelt. Da kommt dann `pathlib` ins Spiel, denn Dateinamen/Pfade sind keine gewöhnlichen Zeichenketten, sondern welche die bestimmten Regeln unterworfen sind, die zudem noch systemabhängig sein können. *Und* man wird dadurch in `addSave()`-Fall auch die Frage ``with`` oder nicht los, weil `Path`-Objekte eine praktische Methode haben um den kompletten Dateiinhalt auf einmal zu schreiben.
Ausserdem müsste man noch den Fall berücksichtigen, dass der Benutzer gar keine Datei auswählt, sondern den Dialog abbricht.
Wenn man per `print()` das gleiche Ausgibt was man auch in die Datei schreibt, sollte man diese Zeichenkette einmal erstellen und nicht zwei mal. Und das dann auch noch mit leicht unterschiedlichem Code.
Warum sind die Platzhalter bei dem `format()`-Aufruf in einer anderen Reihenfolge als die Argumente? Das kann Sinn machen wenn man die Vorlage zum Ausfüllen getrennt vom eigentlichen Ausfüllen hat und die Vorlage nicht zwingend fest ist, weil es dann sein kann, das man verschiedene Vorlagen für die gleichen Daten hat, in denen aber die Reihenfolge nicht immer gleich sein muss. Wenn man das trennt, würde ich aber auch nicht unbedingt die nummerierten Platzhalter verwenden, sondern Namen. Das ist dann verständlicher.
Argumente sind die Werte die man Funktionen beim Aufruf übergibt. Und das sind neben Konstanten die einzigen Werte, die eine Funktion intern benutzen sollte. Das ist der Sinn von (echten) Funktionen — eine in sich geschlossene Einheit zu bilden, wo man Werte rein steckt und ein Erfgebnis heraus bekommt, und zwar über Argument(e) rein, und einen Rückgabewert raus. Die `addSave()`-”Funktion” benutzt beispielsweise einfach so `AusfahrtBerechnung` und `Gebuehren` ohne das man das an der ``def``-Zeile sehen könnte. Dazu muss man die Funktion komplett lesen. Je mehr Funktionen man hat und je länger die werden, um so unübersichtlicher wird das Programm welcher Wert eigentlich wo definiert, verwendet, und verändert wird. Darum verwendet man keine globalen Variablen, also keine Variablen auf Modulebene, und das Hauptprogramm in einer Funktion, und daraus folgt dann, das man Werte als Argumente an Funktionen übergeben muss.
Und daraus folgt bei GUIs mehr oder weniger zwangsläufig, dass man sich mit objektorientierter Programmierung beschäftigen muss, weil man sich bei nicht-trivialen GUIs Zustand über Aufrufe hinweg merken muss.
Code: Alles auswählen
def save(parkdauer, gebuehren):
filename = filedialog.asksaveasfilename(
initialdir=Path().absolute().root,
filetypes=(("Textdatei", "*.txt"), ("all files", "*.*")),
)
if filename:
file_path = Path(filename)
if not file_path.suffix:
file_path = file_path.with_suffix(".txt")
text = f"Parkdauer: {parkdauer} Std.\nZu zahlen: {gebuehren:5.2f} €\n"
print(text, end="")
file_path.write_text(text, encoding="utf-8")
`btn` wäre eine kryptische Abkürzung und Funktions- und Methodennamen werden üblicherweise nach der Tätigkeit benannt die sie durchführen. Bei Funktionen/Methoden die Ereignisse behandeln ist manchmal auch `on_<ereignisname>` üblich, also beispielsweise `on_save()` oder `on_exit()`.
Mir fällt gerade auf, das nicht nur die `PhotoImage`-Aufrufe überflüssig sind, sondern auch die `Canvas`-Objekte die da als `master` übergeben werden, nirgends verwendet werden.
Spyder ist IMHO keine so glücliche Wahl zur Programmentwicklung, jedenfalls nicht wenn man Programm in der IDE laufen lässt, weil sich das deutlich anders verhält als Programme normal laufen zu lassen. Es sei denn man kann bei Spyder mittlerweile irgendwo einstellen, das jeder Programmlauf mit einem frischen Kernel passieren soll.
Bei Bildobjekten für Tk(inter) gibt es eine Besonderheit, das man auf der Python-Seite unbedingt sicherstellen muss, dass das Objekt dort bestehen bleibt, also im Programm erreichbar bleibt, weil die `tkinter`-Anbindung keine Ahnung hat ob und wie lange das Bild auf der Tk-Seite benutzt wird, und das einfach abräumt, wenn es auf Python-Seite nicht mehr erreichbar ist. Falls Tk das aber trotzdem noch braucht hat es Pech gehabt und zeigt nichts an, weil nichts mehr da ist.
Die Fenstergrösse fest vorzugeben ist ja ein Teil des Problems. Wenn man konsequent Layouts verwendet, dann ist das Fenster automatisch so gross, das da alles rein passt. Nicht kleiner, aber auch nicht grösser. Und das gilt auch für die ganzen Komponenten die man im Fenster anzeigt.