# -*- coding: utf-8 -*- """Pruefstand: hell, dunkel, automatisch - und nichts geht dabei verloren. Sein Wortlaut vom 07.09.2026: "waere nun noch gut das mann in den einstellungen einstellen kann hell oder dunkel oder automatik das fehlt sollte eigendlich drin sein" "und beim steuertool fehlt das theam in genze oder wird nicht durch die automatik gesetzt" Er hatte recht: dieses Programm hatte ueberhaupt kein Theme - es lief im Tk-Standard, mit dreimal fest eingetragenem foreground="black". GEPRUEFT WIRD DREIERLEI 1. Die Einstellung wirkt. "hell" ist hell, "dunkel" ist dunkel, "auto" folgt Windows. 2. Beim Umschalten bleibt der Stand stehen - Jahr und Suchfeld. 3. NACH dem Wechsel traegt kein Widget mehr eine Farbe der anderen Palette. Das ist die eigentliche Falle beim Umfaerben: ein einzelnes vergessenes Label leuchtet hell im dunklen Fenster, und niemand findet die Stelle wieder. Deshalb wird der ganze Widget-Baum abgegangen und mit den Farben der jeweils ANDEREN Palette verglichen. """ import os import shutil import sys import tempfile import time import tkinter as tk QUELLE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, QUELLE) fehler = [] def pruefe(bedingung, text): zeile = (" OK | " if bedingung else "FEHLER | ") + text kod = sys.stdout.encoding or "utf-8" print(zeile.encode(kod, "replace").decode(kod)) if not bedingung: fehler.append(text) os.chdir(QUELLE) os.environ["CAMPINGHOF_TEST"] = "1" from tkinter import messagebox # noqa: E402 messagebox.askyesno = lambda *a, **k: True for _n in ("showinfo", "showwarning", "showerror"): setattr(messagebox, _n, lambda *a, **k: None) # Auf einer KOPIE des Programms arbeiten: der Datenordner liegt aus der Quelle # heraus neben app.py (ablage.py), also legt eine Kopie im Temp-Ordner auch ein # eigenes Journal an. Das echte Journal des Betriebs wird nicht angefasst - # hier wird eine Einstellung geschrieben, und die gehoert nicht in seine Daten. tmp = tempfile.mkdtemp(prefix="darstellung_") prog = os.path.join(tmp, "programm") os.makedirs(prog) for name in os.listdir(QUELLE): if name.endswith(".py"): shutil.copy(os.path.join(QUELLE, name), os.path.join(prog, name)) for name in ("assets",): if os.path.isdir(os.path.join(QUELLE, name)): shutil.copytree(os.path.join(QUELLE, name), os.path.join(prog, name)) sys.path.insert(0, prog) os.chdir(prog) import app as A # noqa: E402 import theme # noqa: E402 f = A.App() def pumpen(sekunden=1.0): ende = time.time() + sekunden while time.time() < ende: f.update() time.sleep(0.02) pumpen(2.0) # ---- Ohne Einstellung: automatisch -------------------------------------- pruefe(theme.modus(f.cfg) == "auto", f"ohne Eintrag gilt 'auto' ({theme.modus(f.cfg)!r})") pruefe(f._dark_mode == theme.windows_dark_mode(), "und folgt damit dem Windows-Theme") # ---- Stand, der den Wechsel ueberleben muss ---------------------------- f.jahr.set(2025) f.suche.set("Muster") pumpen(0.5) def farben(dunkel): """Die Palette der einen oder anderen Darstellung, als Menge.""" merker = f._dark_mode f.cfg["darstellung"] = "dunkel" if dunkel else "hell" f._apply_theme() satz = {getattr(f, n).lower() for n in dir(f) if n.startswith("col_") and isinstance(getattr(f, n), str)} f._dark_mode = merker return satz HELL = farben(False) DUNKEL = farben(True) # Was in BEIDEN Paletten vorkommt, taugt nicht als Beweis. NUR_HELL = HELL - DUNKEL NUR_DUNKEL = DUNKEL - HELL def fremdfarben(verboten): """Alle Widgets, die eine Farbe der falschen Palette tragen.""" treffer = [] def durchgehen(w): for kind in w.winfo_children(): for eigenschaft in ("bg", "background", "fg", "foreground"): try: wert = str(kind.cget(eigenschaft)).lower() except tk.TclError: continue if wert in verboten: treffer.append((str(kind), eigenschaft, wert)) durchgehen(kind) durchgehen(f) return treffer # ---- Die drei Modi ------------------------------------------------------ for wunsch, soll_dunkel in (("hell", False), ("dunkel", True)): f.cfg["darstellung"] = wunsch f._oberflaeche_neu() pumpen(0.8) pruefe(f._dark_mode == soll_dunkel, f"'{wunsch}' zeichnet {'dunkel' if soll_dunkel else 'hell'} " f"(Hintergrund {f.col_bg})") # Nichts darf verlorengehen. pruefe(f.jahr.get() == 2025, f"nach dem Wechsel steht das gewaehlte Jahr noch da ({f.jahr.get()})") pruefe(f.suche.get() == "Muster", f"und der Suchbegriff ({f.suche.get()!r})") # Kein Rest der anderen Palette. reste = fremdfarben(NUR_DUNKEL if not soll_dunkel else NUR_HELL) pruefe(not reste, f"in '{wunsch}' ist keine Farbe der anderen Darstellung " f"uebrig ({reste[:3]})") # ---- Dialoge gehen mittig ueber dem Programmfenster auf ------------------ # Seine Ansage: "einfach so programmieren das die popup auf dem fenster mittig # vom main fenster auf gehen". Das ist zugleich die Antwort auf die # Monitorfrage - steht das Programm auf dem zweiten Bildschirm, geht der # Dialog dort auf. Vorher klemmte ein max(0, x) jede negative Koordinate auf # null und riss die Fenster auf den Hauptmonitor zurueck. d = f.dlg_darstellung() pumpen(1.2) d.update_idletasks() mitte_dialog = d.winfo_rootx() + d.winfo_width() // 2 mitte_fenster = f.winfo_rootx() + f.winfo_width() // 2 pruefe(abs(mitte_dialog - mitte_fenster) <= 30, f"der Dialog steht mittig ueber dem Fenster " f"({mitte_dialog} vs. {mitte_fenster})") pruefe(d.winfo_rootx() >= f.winfo_rootx() - 40 and d.winfo_rootx() + d.winfo_width() <= f.winfo_rootx() + f.winfo_width() + 40, "und liegt waagerecht innerhalb des Fensters") pruefe(d.winfo_rooty() >= f.winfo_rooty() - 40, "und nicht oberhalb davon") d.destroy() pumpen(0.3) # ---- Zurueck auf automatisch -------------------------------------------- f.cfg["darstellung"] = "auto" f._oberflaeche_neu() pumpen(0.8) pruefe(f._dark_mode == theme.windows_dark_mode(), "'auto' folgt wieder dem Windows-Theme") # ---- Und die Einstellung ueberlebt den Neustart ------------------------- f.cfg["darstellung"] = "hell" f.destroy() f = A.App() pumpen(2.0) pruefe(f._dark_mode is False, "die Einstellung steht beim naechsten Start wieder da") try: f.destroy() except Exception: # noqa: BLE001 pass shutil.rmtree(tmp, ignore_errors=True) if fehler: print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt") sys.exit(1) print("GRUEN | Hell, dunkel und automatisch wirken - ohne Datenverlust")