@pyCoder: Da ist in Zeile 31 ein ganz offensichtlicher Syntaxfehler, weil da etwas steht, was da nicht rein gehört.
Eingerückt wird in Python per Konvention mit vier Leerzeichen pro Ebene.
`messagebox` wird importiert aber nirgends verwendet.
Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 140 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).
Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen.
Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste.
`stdFont`, `chkBoxPadX` , `chkBoxPadY`, `getUsername`, `msgBox` und `chkBox_2` werden definiert, aber nirgends verwendet.
`getUsername` (beziehunsgweise `get_username`) wäre ein guter Name für eine Funktion oder Methode, aber nicht für einen passiven Wert.
Kommentare werden mit *einem* # eingeleitet, nicht vier. Und man sollte da auch nicht nur Grossbuchstaben im Text verwenden. Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.
Die Fenstergrösse ergibt sich automatisch aus dem Inhalt, und die Position sollte man nicht selbst vorgeben, sondern der Fensterverwaltung überlassen.
`place()` verwendet man nicht. Da ist nicht garantiert ob die Pixel-Angaben zu der Bildschirmgrösse, -auflösung, und den -einstellungen passt. Im besten Fall sieht's komisch aus, im schlechtesten Fall wird die Oberfläche unbedienbar wenn sich Elemente überlappen oder aus dem sichtbaren Fenster herausragen.
Die Klasse `CheckBoxes` ist unsinnig. Methoden bekommen als erstes Argument immer `self`. Das wird in der `__init__()` welche die einzige Methode ist, aber überhaupt gar nicht verwendet. Wenn eine Methode `self` nicht verwendet, dann ist das aber keine sinnvolle Methode, denn dann kann man das einfach als Funktion ausserhalb der Klasse definieren. Damit hätte die Klasse nur noch ein Attribut das verändert wird, also eine globale Variable. Da man keine globalen Variablen haben will, ist die Klasse also leer und damit unnötig. Das hätte auch auffallen können weil mit den Exemplaren auch überhaupt gar nichts gemacht wird.
Warum heisst ein Wert der für eine Zeile in einem Grid steht `chkBoxShow`?
`var` wird in der `__init__()` definiert, aber nicht verwendet.
"Standard selected files" wird für jede Checkbox erneut als Label erzeugt und immer übereinander in die gleiche Grid-Zelle gesteckt.
Ein Widget erstellen und danach dann mehrfach `config()` aufrufen ist umständlich, weil man das auch alles beim Erstellen hätte angeben können.
An der Stelle würde ich auch die Finger von den ganzen Farben lassen. Du bügelst da alle möglichen Farben für Aktiv und Ausgewählt mit den gleichen Werten platt. Die haben ja einen Sinn und machen GUI leichter bedienbar. Und auch von der Schriftart und Grösse würde ich die Finger lassen. Das sind Sachen die der Benutzer entscheided, und zwar manchmal auch weil er das *muss* um überhaupt mit dem Rechner arbeiten zu können. Beispielsweise im Alter/bei Sehberhinderungen. Schriftart, Grösse, und Kontraste sollte man nicht ändern weil man das ”schöner” findet.
Bei den Checkboxen sind `onvalue` und `offvalue` Zeichenketten und die `variable` eine `IntVar` — das geht natürlich nicht. Ausserdem verwenden alle Checkboxen das *selbe* `IntVar`-Objekt.
Es macht eher keinen Sinn jedes mal die Kopierfunktion aufzurufen wenn eine Checkbox an- oder abgehakt wurde.
Worte sollten in Namen die richtige Reihenfolge haben. Eine Button-Kopie ist was anderes als ein Kopier-Button.
Man muss auch nicht jedes Zwischenergebnis an einen Namen binden.
Man bastelt keine Pfade mit Zeichenkettenoperationen und hart kodierten Pfadtrennern zusammen, sondern verwendet dafür `pathlib`. Die Funktionen aus `os.path` nur wenn es tatsächlich keinen entsprechenden Weg mit `pathlib` gibt.
Defaultargumente sind dazu da, das man die gerade *nicht* beim Aufruf angeben muss.
Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
import shutil
import tkinter as tk
from pathlib import Path
def copy_all():
base_path = Path.home() / "tmp"
shutil.copytree(
base_path / "copy_2", base_path / "test", dirs_exist_ok=True
)
def create_checkbox(master, row, label):
#
# TODO Damit man damit später sinnvoll etwas machen kann, sollte das
# vielleicht der Rückgabewert dieser Funktion sein.
#
variable = tk.StringVar(master)
checkbox = tk.Checkbutton(
master, text=label, variable=variable, onvalue=label, offvalue=""
)
checkbox.grid(row=row, column=0, sticky=tk.W, padx=50, pady=5)
def main():
root = tk.Tk()
root.title("Codat - Save files and copy back")
root.resizable(False, False)
tk.Label(
root,
text="Standard selected files",
anchor=tk.NW,
font="TkHeadingFont",
).grid(row=0, column=0, padx=20, pady=20, sticky=tk.NW)
create_checkbox(root, 1, "Desktop")
create_checkbox(root, 2, "Documents")
create_checkbox(root, 3, "Favorite")
tk.Button(root, text="Copy", command=copy_all).grid(row=4, column=0)
tk.Button(root, text="Exit", command=root.quit).grid(row=5, column=0)
root.mainloop()
if __name__ == "__main__":
main()
Falls die Datenmengen grösser sind, sollte man bedenken, dass die GUI einfriert solange die Kopierfunktion läuft, und das manche Betriebssysteme nach einer gewissen Wartezeit den Benutzer fragen ob die Anwendung beendet werden soll, weil sie nicht mehr reagiert.
Und man sollte sich überlegen ob Fehler entsprechend sinnvoll erkannt werden und behandelt werden, weil das bei Sicherungskopien ja doch wichtig ist, das der Benutzer mitbekommt, wenn etwas nicht funktioniert hat, und er keine Kopie hat.