# -*- coding: utf-8 -*- """Pruefstand: geht jeder Dialog ueberhaupt auf? Klingt banal, ist es nicht: beim Aufteilen von app.py in Module sind zwei Dialoge stumm kaputtgegangen (fehlende Importe), und kein anderer Pruefstand hat es gemerkt, weil keiner sie oeffnet. Dieser hier oeffnet ALLE. Geprueft wird ausserdem, dass jeder Dialog auch wieder zugeht - ein modales Fenster, das haengenbleibt, sperrt das Programm. """ import json import os import shutil import sys import tempfile import tkinter as tk QUELLE = r"C:\claude\rechnungstool" sys.path.insert(0, QUELLE) tmp = tempfile.mkdtemp(prefix="dialog_pruef_") ordner = os.path.join(tmp, "rechnungen") os.makedirs(ordner) import config # noqa: E402 shutil.copy(os.path.join(QUELLE, "config.json"), os.path.join(tmp, "config.json")) config.CONFIG_PATH = os.path.join(tmp, "config.json") roh = json.load(open(config.CONFIG_PATH, encoding="utf-8")) roh["output_dir"] = ordner # Der Erststart-Zustand gehoert dem Pruefstand, nicht der config des # Entwicklerrechners: dlg_nummer zeigt sonst nur eine Meldung statt des # Fensters, das hier geprueft werden soll. roh["nummer_eingerichtet"] = False json.dump(roh, open(config.CONFIG_PATH, "w", encoding="utf-8"), ensure_ascii=False) # Kein Erststart-Dialog im Pruefstand - er wuerde den Lauf haengen lassen. os.environ["CAMPINGHOF_TEST"] = "1" import app as A # noqa: E402 from tkinter import messagebox # noqa: E402 messagebox.showinfo = lambda *a, **k: None messagebox.showwarning = lambda *a, **k: None messagebox.showerror = lambda *a, **k: None # Der Erprobungshinweis wird von der pauschalen Antwort unten mit erfasst. # Geprueft wird er eigens in pruef_hinweis.py. messagebox.askyesno = lambda *a, **k: True # Sicherheitsfrage bejahen # --- Reissleine --------------------------------------------------------- # Ein Pruefstand darf NIE haengen. Bleibt er in einem Dialog stehen (etwa weil # eine Aenderung eine neue Frage eingefuehrt hat), bricht er nach zwei Minuten # von selbst ab, statt ein Fenster offen zu lassen, auf das niemand klickt. import threading as _th def _reissleine(): import os as _os print("ABBRUCH | Pruefstand haengt (Zeitgrenze) - vermutlich ein Dialog, " "der nicht beantwortet wird") _os._exit(2) _wecker = _th.Timer(120, _reissleine) _wecker.daemon = True _wecker.start() fehler = 0 f = A.RechnungsApp() f.update() # Damit "Storno"/"Berichtigen" nicht an "nichts zu korrigieren" scheitern: # eine echte Rechnung in den Wegwerf-Ordner legen. f.k_vorname.set("Anna") f.k_name.set("Beispiel") f.k_strasse.set("Musterweg 1") f.k_plzort.set("12345 Musterstadt") f.anzahl_vars[sorted(f.anzahl_vars)[0]].set("2") f.update() f.pdf_erstellen() DIALOGE = ["dlg_steuern", "dlg_katalog", "dlg_firma", "dlg_nummer", "dlg_storno", "dlg_berichtigen"] for name in DIALOGE: offen_vorher = [w for w in f.winfo_children() if isinstance(w, tk.Toplevel)] try: getattr(f, name)() f.update() except Exception as e: # noqa: BLE001 - genau das wollen wir sehen print(f"ABSTURZ | {name}: {type(e).__name__}: {e}") fehler += 1 continue neu = [w for w in f.winfo_children() if isinstance(w, tk.Toplevel) and w not in offen_vorher] if not neu: print(f"FEHLER | {name}: kein Fenster aufgegangen") fehler += 1 continue titel = neu[-1].title() for w in neu: w.grab_release() w.destroy() f.update() print(f" OK | {name} -> „{titel}“") # Der Speicherort-Dialog oeffnet einen Systemdialog - nur die Vorstufe pruefen try: f._frage_speicherort_falls_noetig() f.update() print(" OK | _frage_speicherort_falls_noetig") except Exception as e: # noqa: BLE001 print(f"ABSTURZ | _frage_speicherort_falls_noetig: {type(e).__name__}: {e}") fehler += 1 f.destroy() # Wegwerf-Ordner nur bei Fehlern behalten - sonst laeuft das Temp-Verzeichnis # voll. Ein Pruefstand, der 400 Ordner hinterlaesst, ist selbst ein Problem. if not fehler: # fehler ist hier ein Zaehler import shutil as _sh _sh.rmtree(tmp, ignore_errors=True) print("---") print("alles gruen" if not fehler else f"{fehler} Fehler") sys.exit(1 if fehler else 0)