Seine Ansage: "schau dir unsern fivem server an und unsere webseiten, dann
weisst du wie mein design ist" - also die Palette aus _ui_www und der
Adminpage: Tuerkis #06C6A4 auf gestuften dunklen Flaechen (#0d0e10 Grund,
#15171b Kachel), Raender #25282e, Text #e8e8ea / #8a8e96, Rot #e85a5a.
Das PROGRAMM traegt diese Handschrift, die RECHNUNG die Farbe der jeweiligen
Firma - das Blatt gehoert dem Kunden, nicht uns.
- Roboto wird mitgeliefert und nur fuer den Prozess geladen
(AddFontResourceEx mit FR_PRIVATE): keine Installation, keine Adminrechte,
nach dem Schliessen ist der Rechner wie vorher. Fehlt sie, bleibt es bei
Segoe UI. Die Schriftdateien gehen mit in die exe (spec).
- Abschnitte sind Kacheln mit Ueberschrift statt grauer Rahmen.
- Knoepfe im Glas-Stil wie in seinen Oberflaechen: getoente Flaeche, Rand in
Akzent-Transparenz. Tk kann kein backdrop-filter - die Toenung wird
ausgerechnet (theme.mischen), das Ergebnis ist auf einfarbigem Grund
identisch. "PDF erstellen" fuehrt, "Storno" ist rot.
- Zebrastreifen in der Leistungsliste: bei 17 Zeilen verrutscht man sonst.
- Kein expand am offenen Reiter mehr - dadurch sprang beim Umschalten die
ganze Leiste ("das taben macht den aktiven kleiner").
- Das Fenster fuer den gemeinsamen Ordner stand als einziges noch in Weiss da:
es kam nie durch _style_dialog.
Dazu ein Fund, der nichts mit dem Design zu tun hat: die Pruefstaende erbten
"nummer_eingerichtet" aus der config DIESES Rechners. Sobald hier jemand die
Nummer einmal einrichtet, liefen sie in einen anderen Zweig und wurden rot -
ohne dass sich am Programm etwas geaendert haette. Sie legen den Zustand jetzt
selbst fest. Alle 14 gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
122 lines
4 KiB
Python
122 lines
4 KiB
Python
# -*- 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
|
|
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)
|