Aufraeumen nach der Pruefkette: doppelter Code an eine Stelle
Die Runde vom 07.09.2026 hat neben den zwei echten Fehlern eine Reihe Wiederholungen und stumme Stellen gemeldet. Abgearbeitet in Etappen, jede fuer sich pruefbar - nach jeder liefen beide Pruefketten gruen. * Menueleiste: der Wechsel von einem Titel zum anderen brauchte ZWEI Klicks. Der grab_set der offenen Liste schluckt den ersten, die <Button-1>-Bindung des Titels feuert gar nicht. Das native tk.Menu, das diese Leiste ersetzt hat, wechselte schon beim blossen Ueberfahren - zwei Klicks waren also eine Verschlechterung gegenueber dem, was vorher da war. Die Klappliste wird ausserdem jetzt wie jede andere Position ueber theme.auf_bildschirm() begrenzt; am unteren Fensterrand lief sie vorher hinaus. Neu: pruef_menueleiste.py misst beides ueber echte Klickereignisse. * %LOCALAPPDATA%/ravokk wurde an drei Stellen einzeln ausgerechnet. Jetzt fragen alle gemeinsam.standard_ordner(). Laufen die je auseinander, fuehren die beiden Programme zwei Nummernbuecher - und das faellt erst auf, wenn eine Rechnungsnummer zum zweiten Mal vergeben ist (§ 14 Abs. 4 Nr. 4 UStG). * pruefe() stand 35-mal im Quelltext, in sechs Fassungen -> pruefhelfer.py. Die zwei Ausreisser sind mitgezogen: pruef_bilder.py schrieb ein eigenes Format, pruef_storno_verrechnet.py zaehlte in einer Zahl und konnte am Ende nicht sagen, WAS fehlschlug. * dlg_darstellung() und die drei Handgriffe des Umschaltens standen in beiden app.py fast wortgleich -> ThemeMixin in theme.py. Beide trugen inzwischen denselben langen Kommentar zu demselben Fehler; das war der Beweis, dass es eine Sache ist. Programmspezifisch bleiben der Hinweistext im Dialog (R31) und die Frage, ob die Einstellungen ausdruecklich gespeichert werden muessen. * pruef_gemeinsam_automatisch.py sagte im Text "genau ein Buch" und prueft ">= 1" mit einem any() - eine Pruefung, die nicht rot werden kann. Jetzt == 1, und der Dateiname wird mitgeprueft. Dazu neu: die neun Dateien, die im Kopf zusagen, sie laegen in beiden Programmen gleich, werden byteweise verglichen. Bei hinweis.py stimmte die Zusage seit dem 06.09. nicht mehr - gleicher Inhalt, CRLF gegen LF. * .gitattributes, damit die Zeilenenden nicht von der Maschine abhaengen. Ohne das meldet genau diese Pruefung nach einem frischen Checkout einen Unterschied, den es im Repository gar nicht gibt. * Sechs stumme "except OSError" sagen jetzt, warum sie schweigen duerfen, und einer meldet statt zu schweigen: schlaegt die Uebernahme einer alten config.json fehl, faengt das Programm ohne Firmendaten, Katalog und Zaehler neu an - und der Erfolgsfall schrieb eine Zeile, der Fehlerfall nicht. Die 24 stummen tk.TclError bleiben: dort wird ein Widget angefasst, das gerade zerstoert wurde, und eine Meldung waere Rauschen. * Tote Parameter und Variablen aus dem Umbau des Vortags: symbol_setzen(dunkel=), kachel(grund=, radius=), mit_trennlinie, sechs x/y-Berechnungen, die von theme.mittig() sofort ueberschrieben wurden, vier lokale "import ctypes". 39 Pruefstaende, 0 rot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
This commit is contained in:
parent
5babfc3e2e
commit
2b09d15cd0
36 changed files with 563 additions and 364 deletions
184
pruef_menueleiste.py
Normal file
184
pruef_menueleiste.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Pruefstand: die eigene Menueleiste - Wechsel mit EINEM Klick, und am Rand.
|
||||
|
||||
WARUM ES DIESE LEISTE GIBT
|
||||
--------------------------
|
||||
`tk.Menu` ist unter Windows ein Menue des Betriebssystems. Es nimmt Farben
|
||||
entgegen und zeichnet trotzdem im Systemgrau - ein weisser Balken ueber einem
|
||||
dunklen Fenster. Sein Befund am 07.09.2026: *"nur die einstellunden hilfe
|
||||
leiste die ist noch hell"*. Deshalb ist die Leiste aus gewoehnlichen Labels
|
||||
gebaut.
|
||||
|
||||
WAS HIER GEPRUEFT WIRD
|
||||
|
||||
1. Ein Klick auf einen Titel klappt seine Liste auf, ein zweiter auf denselben
|
||||
Titel klappt sie wieder zu.
|
||||
|
||||
2. Steht eine Liste offen und man klickt auf einen ANDEREN Titel, klappt
|
||||
dessen Liste auf - mit EINEM Klick.
|
||||
|
||||
Das ging bis zum 07.09.2026 nicht. Solange eine Liste offen ist, haelt ihr
|
||||
`grab_set` alle Mausklicks der Anwendung fest; die `<Button-1>`-Bindung des
|
||||
Menuetitels feuert dann gar nicht. Der Klick landete beim Zuklappen und war
|
||||
damit verbraucht - man musste ein zweites Mal klicken, um von "Datei" zu
|
||||
"Hilfe" zu kommen. Das native tk.Menu wechselte schon beim blossen
|
||||
Ueberfahren; zwei Klicks waren also eine Verschlechterung gegenueber dem,
|
||||
was ersetzt wurde.
|
||||
|
||||
Geprueft wird das ueber ein echtes Klickereignis auf der offenen Liste, mit
|
||||
den Bildschirmkoordinaten des anderen Titels - also genau der Weg, den der
|
||||
grab_set erzwingt. Ein direkter Aufruf von aufklappen() wuerde am Problem
|
||||
vorbeipruefen.
|
||||
|
||||
3. Die Liste bleibt auf dem Bildschirm. Steht das Fenster am unteren Rand,
|
||||
lief sie vorher darueber hinaus und war nicht mehr zu treffen: die Position
|
||||
wurde ungeprueft gesetzt, waehrend jede andere neue Position im Programm
|
||||
ueber theme.auf_bildschirm() laeuft.
|
||||
|
||||
Diese Datei liegt in BEIDEN Programmen gleich. Wer sie aendert, kopiert sie
|
||||
hinueber - sonst steht in zwei Programmen zweierlei.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import tkinter as tk
|
||||
|
||||
QUELLE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, QUELLE)
|
||||
|
||||
import menueleiste # noqa: E402
|
||||
import theme # noqa: E402
|
||||
from pruefhelfer import fehler, pruefe # noqa: E402
|
||||
|
||||
|
||||
class Fenster(theme.ThemeMixin, tk.Tk):
|
||||
"""Ein nacktes Fenster mit Farben - mehr braucht die Leiste nicht."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.cfg = {} # theme.modus() will etwas mit .get()
|
||||
self._apply_theme()
|
||||
self.title("Pruefstand Menueleiste")
|
||||
self.geometry("560x320" + theme.pruefplatz())
|
||||
|
||||
|
||||
f = Fenster()
|
||||
gerufen = []
|
||||
f.menue = menueleiste.Menueleiste(f, {
|
||||
"Datei": [("Neu", lambda: gerufen.append("neu")), ("-", None),
|
||||
("Beenden", lambda: gerufen.append("beenden"))],
|
||||
"Bearbeiten": [("Rueckgaengig", lambda: gerufen.append("rueck"))],
|
||||
"Hilfe": [("Info", lambda: gerufen.append("info"))],
|
||||
})
|
||||
|
||||
|
||||
def pumpen(sekunden=0.4):
|
||||
ende = time.time() + sekunden
|
||||
while time.time() < ende:
|
||||
f.update()
|
||||
time.sleep(0.02)
|
||||
|
||||
|
||||
pumpen(1.0)
|
||||
|
||||
titel = {str(w.cget("text")): w for w in f.menue.winfo_children()
|
||||
if isinstance(w, tk.Label)}
|
||||
pruefe(set(titel) == {"Datei", "Bearbeiten", "Hilfe"},
|
||||
f"alle drei Titel stehen in der Leiste ({sorted(titel)})")
|
||||
|
||||
|
||||
def klick_auf_titel(name):
|
||||
"""Auf einen Menuetitel klicken - so, wie es die Maus tut.
|
||||
|
||||
Steht schon eine Liste offen, MUSS das Ereignis an sie gehen: ihr grab_set
|
||||
faengt jeden Klick der Anwendung ab. Genau das ist der Fall, um den es
|
||||
unter Punkt 2 geht.
|
||||
"""
|
||||
w = titel[name]
|
||||
x = w.winfo_rootx() + w.winfo_width() // 2
|
||||
y = w.winfo_rooty() + w.winfo_height() // 2
|
||||
ziel = f.menue._offen or w
|
||||
if ziel is w:
|
||||
# Achtung: Tk nennt die Bildschirmkoordinaten beim Erzeugen -rootx
|
||||
# und -rooty; im Ereignis heissen sie dann x_root und y_root.
|
||||
w.event_generate("<Button-1>", x=w.winfo_width() // 2,
|
||||
y=w.winfo_height() // 2, rootx=x, rooty=y)
|
||||
else:
|
||||
ziel.event_generate("<Button-1>", x=-1, y=-1, rootx=x, rooty=y)
|
||||
pumpen(0.4)
|
||||
|
||||
|
||||
def offener_titel():
|
||||
"""Welcher Titel gerade offen ist - oder None."""
|
||||
anker = f.menue._anker
|
||||
if anker is None or not f.menue._offen:
|
||||
return None
|
||||
return next((t for t, w in titel.items() if w is anker), "?")
|
||||
|
||||
|
||||
# ---- 1. Aufklappen und wieder zuklappen ---------------------------------
|
||||
klick_auf_titel("Datei")
|
||||
pruefe(offener_titel() == "Datei", f"'Datei' klappt auf ({offener_titel()})")
|
||||
|
||||
klick_auf_titel("Datei")
|
||||
pruefe(offener_titel() is None,
|
||||
f"und derselbe Titel klappt wieder zu ({offener_titel()})")
|
||||
|
||||
# ---- 2. Der Wechsel: EIN Klick ------------------------------------------
|
||||
klick_auf_titel("Datei")
|
||||
pruefe(offener_titel() == "Datei", "'Datei' steht offen")
|
||||
|
||||
klick_auf_titel("Hilfe")
|
||||
pruefe(offener_titel() == "Hilfe",
|
||||
f"ein Klick auf 'Hilfe' wechselt hinueber ({offener_titel()})")
|
||||
|
||||
klick_auf_titel("Bearbeiten")
|
||||
pruefe(offener_titel() == "Bearbeiten",
|
||||
f"und weiter auf 'Bearbeiten' ({offener_titel()})")
|
||||
|
||||
# Ein Klick irgendwo neben die Leiste schliesst - und oeffnet nichts.
|
||||
pop = f.menue._offen
|
||||
pop.event_generate("<Button-1>", x=-1, y=-1,
|
||||
rootx=f.winfo_rootx() + 300,
|
||||
rooty=f.winfo_rooty() + 250)
|
||||
pumpen(0.4)
|
||||
pruefe(offener_titel() is None,
|
||||
f"ein Klick daneben schliesst und oeffnet nichts ({offener_titel()})")
|
||||
|
||||
# ---- 3. Ein Eintrag fuehrt seinen Befehl aus ----------------------------
|
||||
klick_auf_titel("Hilfe")
|
||||
eintrag = None
|
||||
for kind in f.menue._offen.winfo_children():
|
||||
for enkel in kind.winfo_children():
|
||||
if isinstance(enkel, tk.Label) and str(enkel.cget("text")) == "Info":
|
||||
eintrag = enkel
|
||||
eintrag.event_generate("<Button-1>", x=2, y=2)
|
||||
pumpen(0.4)
|
||||
pruefe(gerufen == ["info"], f"der Eintrag 'Info' fuehrt seinen Befehl aus ({gerufen})")
|
||||
pruefe(offener_titel() is None, "und die Liste ist danach zu")
|
||||
|
||||
# ---- 4. Am unteren Bildschirmrand bleibt die Liste sichtbar -------------
|
||||
# Das Fenster so weit nach unten schieben, dass unter der Leiste kein Platz
|
||||
# mehr fuer die Klappliste ist.
|
||||
links, oben = theme.auf_bildschirm(f.winfo_rootx(), f.winfo_rooty())
|
||||
unten = oben + f.winfo_screenheight()
|
||||
f.geometry(f"560x320+{links}+{unten - 120}")
|
||||
pumpen(0.6)
|
||||
|
||||
klick_auf_titel("Datei")
|
||||
pop = f.menue._offen
|
||||
pruefe(pop is not None, "die Liste geht auch am unteren Rand auf")
|
||||
if pop is not None:
|
||||
x, y = pop.winfo_rootx(), pop.winfo_rooty()
|
||||
hoehe, breite = pop.winfo_height(), pop.winfo_width()
|
||||
grenze_x, grenze_y = theme.auf_bildschirm(x, y, breite, hoehe)
|
||||
pruefe((x, y) == (grenze_x, grenze_y),
|
||||
f"und steht vollstaendig auf dem Bildschirm "
|
||||
f"(Liste {x},{y} bis {x + breite},{y + hoehe})")
|
||||
|
||||
f.destroy()
|
||||
|
||||
if fehler:
|
||||
print(f"ROT | {len(fehler)} Punkt(e) nicht erfuellt")
|
||||
sys.exit(1)
|
||||
print("GRUEN | Die Menueleiste wechselt mit einem Klick und bleibt im Bild")
|
||||
Loading…
Add table
Add a link
Reference in a new issue