Logo in der Statuszeile, Bau an einer Stelle, Startprobe fuer die EXE

Logo
  Das Programmlogo steht jetzt klein neben "powered by ravokk" - dort sitzt
  die Kennung des Werkzeugs, nicht die des Kunden. Eine eigene Kopfzeile hat
  dieses Programm nicht; oben sind die Reiter.

bauen.py
  Der Weg von der Quelle zur Setup-Datei war eine Folge von Befehlen, die
  jemand von Hand eintippen musste. Wer einen Schritt vergisst, liefert eine
  Setup-Datei mit einer alten EXE aus - und sieht es ihr nicht an. Genau das
  ist heute passiert. Jetzt ist es ein Befehl, die Nummer kommt aus
  version.py, und am Ende stehen die SHA256-Summen fuer den Release-Text.

pruef_start_exe.py
  Prueft, was kein anderer Pruefstand prueft: ob die GEBAUTE EXE ein
  sichtbares Fenster zeigt. Alle uebrigen fahren das Programm aus der Quelle,
  dort trat der Fehler nie auf. "Der Prozess laeuft" beweist dabei nichts -
  er lief ja, waehrend nichts zu sehen war.

Aufgeraeumt
  "Rechnung Campinghof.exe" ist weg; gebaute Dateien gehoeren ins Release,
  nicht ins Repo. Die .spec-Dateien dagegen schon: ohne sie kann niemand die
  EXE nachbauen, die der Updater ausliefert.

Fassung 1.7.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
This commit is contained in:
TheMockTv 2026-09-06 20:34:38 +02:00
parent fec0e8449c
commit 73042b771c
20 changed files with 1445 additions and 14 deletions

64
app.py
View file

@ -32,6 +32,7 @@ import config
import berechnung
import gemeinsam
import hinweis
import wasserzeichen
import ordnerwahl
import pdf_renderer
from helfer import ANREDEN, kunde_maengel, parse_date, parse_num, saeubern, safe_filename, ziffern
@ -63,6 +64,7 @@ class RechnungsApp(ThemeMixin, MeldungMixin, KorrekturMixin, EinstellungenMixin,
super().__init__()
self.cfg = config.load_config()
self.title(config.PROGRAMM)
self._fenstersymbol()
# Fensterhoehe an den Bildschirm anpassen, damit nichts unter den Rand rutscht
hoehe = max(560, min(840, self.winfo_screenheight() - 90))
self.geometry(f"1060x{hoehe}")
@ -118,11 +120,12 @@ class RechnungsApp(ThemeMixin, MeldungMixin, KorrekturMixin, EinstellungenMixin,
# es morgen ohne berichtigte Rechnung im Ordner und keiner weiss warum.
self.protocol("WM_DELETE_WINDOW", self._beenden)
# after_idle statt after(0): laeuft, sobald Tk nichts mehr zu zeichnen hat -
# also nachdem das Hauptfenster steht. Bei after(0) haengt der modale Dialog
# an einem noch ungezeichneten Fenster und wirkt wie eingefroren; eine feste
# Wartezeit dagegen erreicht ein Pruefstand nie, der keine Schleife dreht.
self.after_idle(self._hinweis_pruefen)
# Der Hinweis darf erst aufgehen, wenn das Hauptfenster WIRKLICH
# gezeichnet ist. after_idle reicht dafuer nicht: in der gepackten
# EXE ist das Fenster dann noch nicht sichtbar, der modale Dialog
# sperrt es - und beide bleiben unsichtbar. Das sah aus wie ein
# haengendes Programm (06.09.2026, am Installer aufgefallen).
self.after(50, self._hinweis_wenn_sichtbar)
# ---------------------------------------------------------------- Menue
@ -243,6 +246,11 @@ class RechnungsApp(ThemeMixin, MeldungMixin, KorrekturMixin, EinstellungenMixin,
self.flaeche = flaeche.Arbeitsflaeche(self, farben=self)
self.flaeche.pack(side="top", fill="both", expand=True, padx=10, pady=(4, 0))
# Das Wasserzeichen liegt HINTER der Arbeitsflaeche, unten rechts. Es
# wird mit place() gesetzt, damit es die Anordnung der Kaesten nicht
# anfasst - die darf der Benutzer ja selbst verschieben. lower() legt
# es unter alles andere.
self._wasserzeichen_setzen()
def _anordnen_umschalten(self):
self.flaeche.anordnen_starten()
@ -611,6 +619,12 @@ class RechnungsApp(ThemeMixin, MeldungMixin, KorrekturMixin, EinstellungenMixin,
def _build_summen(self):
# Statuszeile ganz unten
status = ttk.Frame(self, padding=(12, 2))
# Das Programmlogo neben der Marke - klein, immer sichtbar. Es steht
# rechts bei "powered by ravokk", weil dort die Kennung des Werkzeugs
# sitzt und nicht die des Kunden.
self._kopf_bild = wasserzeichen.kopf(18, master=self)
if self._kopf_bild is not None:
ttk.Label(status, image=self._kopf_bild).pack(side="right", padx=(0, 6))
status.pack(side="bottom", fill="x")
ttk.Label(status, text=config.MARKE, foreground=self.col_muted,
font=(theme.SCHRIFT_NAME, 8)).pack(side="right")
@ -644,6 +658,40 @@ class RechnungsApp(ThemeMixin, MeldungMixin, KorrekturMixin, EinstellungenMixin,
# --------------------------------------------------- Gemeinsames Buch
# ------------------------------------------------- Erprobungshinweis
def _wasserzeichen_setzen(self, groesse=240):
"""Das Programmlogo blass hinter die Arbeitsflaeche legen."""
bild = wasserzeichen.hole(groesse, 0.06, master=self)
if bild is None:
return
# Die Referenz MUSS bleiben - sonst raeumt Python das Bild weg und Tk
# zeigt eine leere Flaeche (der klassische PhotoImage-Fehler).
self._wz_bild = bild
self._wz = tk.Label(self.flaeche, image=bild, borderwidth=0,
background=self.col_bg)
self._wz.place(relx=1.0, rely=1.0, anchor='se', x=-12, y=-12)
self._wz.lower()
def _fenstersymbol(self):
"""Das Programmsymbol auf das Fenster legen."""
return wasserzeichen.symbol_setzen(self)
print("[symbol] assets/programm.ico nicht gefunden")
return False
def _hinweis_wenn_sichtbar(self, versuche=0):
"""Wartet, bis das Fenster gezeichnet ist, und fragt dann.
winfo_viewable() sagt, ob das Fenster tatsaechlich auf dem Schirm
ist. Solange nicht, wird in kurzen Abstaenden nachgesehen. Nach
rund zwei Sekunden wird trotzdem gefragt - im Pruefstand ist das
Fenster absichtlich nicht sichtbar, und dort soll es trotzdem
laufen.
"""
if not self.winfo_viewable() and versuche < 40:
self.after(50, lambda: self._hinweis_wenn_sichtbar(versuche + 1))
return
self._hinweis_pruefen()
def _hinweis_pruefen(self):
"""Hinweis auf die Erprobungsfassung. Ablehnen beendet das Programm.
@ -1821,7 +1869,11 @@ if __name__ == "__main__":
if not fenster_nach_vorn(laufende_pid_lesen()):
_r = tk.Tk()
_r.withdraw()
self.melden_fehler(
# Hier gibt es kein self - das Fenster wird ja gerade NICHT gebaut.
# Deshalb die schlichte Windows-Meldung statt des eigenen Dialogs.
# (Frueher stand hier self.melden_fehler und ist beim Start eines
# zweiten Fensters mit NameError abgestuerzt.)
messagebox.showerror(
"Rechnungstool läuft bereits",
"""Das Rechnungstool ist schon geöffnet, das Fenster ließ sich
aber nicht nach vorn holen - bitte über die Taskleiste zurückwechseln.