fix(display): Fusszeile des Bildsatzes passt aufs Panel, auch die Fehlt-Warnung

Am Geraet kam die Zeile gekuerzt an ('... (ohne WLA...'), und mit dem Zusatz
' - SATZ FEHLT' war die Warnung in beiden Richtungen ausserhalb der Panelbreite -
also genau die Zeile, die sagt, dass die Station nichts ausliefern kann.

Unbemerkt blieb das, weil _load_font() auf dem Pi DejaVu nimmt und auf dem Mac bis
Arial durchfaellt: dieselbe Zeile misst dort 246,0 von 246 px, auf dem Pi 283,5 px.
Der neue Test misst deshalb mit 25 % Reserve gegen eine breitere Schrift.

Die Fehlt-Warnung ersetzt die Zeile jetzt, statt sie zu verlaengern.
This commit is contained in:
Michael Sauer
2026-09-15 10:37:30 +02:00
parent c482194503
commit f4dd1386ef
2 changed files with 63 additions and 4 deletions
+17 -4
View File
@@ -194,11 +194,24 @@ ARMED_DETAIL_PREFIX = "Freigabe: "
# "Lampe: sofort ins WLAN" sagt ihm genau das, was er wissen muss, bevor er einschaltet. # "Lampe: sofort ins WLAN" sagt ihm genau das, was er wissen muss, bevor er einschaltet.
# Der gefaehrliche Zustand steht in Grossbuchstaben - er ist der, den man uebersehen soll # Der gefaehrliche Zustand steht in Grossbuchstaben - er ist der, den man uebersehen soll
# und darf. # und darf.
# Die Zeile muss UNGEKUERZT passen, und zwar auch mit einer breiteren Schrift als der
# hier gerade installierten: am Geraet (DejaVu) kam die alte, laengere Fassung als
# "Lampe -> Onboarding-AP (ohne WLA..." an - der Zusatz in der Klammer, also gerade die
# Aussage, fiel weg. Auf dem Mac des Entwicklers (Arial, schmaler) passte dieselbe Zeile
# mit 246,0 von 246 px haarscharf; deshalb hat es niemand bemerkt. Breitenbudget siehe
# test_imageset_footer_fits_the_panel_with_room_for_a_wider_font.
IMAGESET_LABELS = { IMAGESET_LABELS = {
"ohne-wlan": "Lampe -> Onboarding-AP (ohne WLAN-Daten)", "ohne-wlan": "ohne WLAN-Daten (Onboarding-AP)",
"mit-wlan": "Lampe -> SOFORT INS EIGENE WLAN", "mit-wlan": "MIT WLAN-Daten (eigenes Netz)",
}
# Fehlt das Verzeichnis des gewaehlten Satzes, ERSETZT die Warnung die Zeile, statt sie
# zu verlaengern. Als angehaengtes Suffix (" - SATZ FEHLT", bis 2026-09-15) war sie in
# BEIDEN Richtungen und in beiden Schriften ausserhalb der Panelbreite - die einzige
# Zeile, die sagt, dass die Station gar nichts ausliefern kann, war damit nie zu sehen.
IMAGESET_MISSING_LABELS = {
"ohne-wlan": "SATZ FEHLT: ohne WLAN-Daten",
"mit-wlan": "SATZ FEHLT: MIT WLAN-Daten",
} }
IMAGESET_MISSING_SUFFIX = " - SATZ FEHLT"
# Der Abschlussbildschirm: was auf dem Panel stehen bleibt, wenn der Pi heruntergefaehrt # Der Abschlussbildschirm: was auf dem Panel stehen bleibt, wenn der Pi heruntergefaehrt
# oder neu gestartet wird. E-Paper ist bistabil - das zuletzt gezeichnete Bild bleibt fuer # oder neu gestartet wird. E-Paper ist bistabil - das zuletzt gezeichnete Bild bleibt fuer
@@ -420,7 +433,7 @@ def _imageset_blocks(state: dict) -> list[tuple[str, str]]:
if not label: if not label:
return [] return []
if state.get("imageset_ok") is False: if state.get("imageset_ok") is False:
label += IMAGESET_MISSING_SUFFIX label = IMAGESET_MISSING_LABELS[name]
return [(label, ROLE_FOOTER)] return [(label, ROLE_FOOTER)]
+46
View File
@@ -2064,3 +2064,49 @@ def test_missing_pillow_logs_exactly_one_line_and_exits_zero(argv, monkeypatch,
assert len(caplog.records) == 1, \ assert len(caplog.records) == 1, \
f"erwartet genau eine Protokollzeile, bekommen: {[r.getMessage() for r in caplog.records]}" f"erwartet genau eine Protokollzeile, bekommen: {[r.getMessage() for r in caplog.records]}"
assert "PIL" in caplog.records[0].getMessage() assert "PIL" in caplog.records[0].getMessage()
# --- Fusszeile des Bildsatz-Umschalters (Geraetetermin 2026-09-15) ------------------
#
# Am Geraet kam die Zeile gekuerzt an: "Lampe -> Onboarding-AP (ohne WLA...". Ausgerechnet
# die Klammer, die den Zustand benennt, fiel weg - und mit dem Zusatz " - SATZ FEHLT" war
# die WARNUNG, dass der gewaehlte Satz gar nicht auf dem Pi liegt, in beiden Richtungen
# unsichtbar (367,5 px bzw. 306 px bei 246 px Platz).
#
# Warum die Testsuite das nie bemerkt hat: _load_font() nimmt den ersten Pfad, der sich
# oeffnen laesst. Auf dem Pi ist das DejaVu Sans Bold, auf dem Mac des Entwicklers faellt
# die Kette bis "Arial Bold.ttf" durch - und Arial baut schmaler. Dieselbe Zeile misst
# dort 246,0 von 246 px (haarscharf drin), auf dem Pi 283,5 px. Das Layout war damit auf
# die Schrift der ENTWICKLUNGSMASCHINE ausgelegt, nicht auf die des Zielgeraets.
#
# Dieser Test misst deshalb mit einer ausdruecklichen Marge: die Zusage lautet nicht "es
# passt mit der hier zufaellig installierten Schrift", sondern "es passt auch mit einer
# spuerbar breiteren". 25 % decken den gemessenen Abstand Arial -> DejaVu (+15 %) mit
# Reserve ab.
FOOTER_WIDTH_SAFETY = 1.25
def _footer_width_px(text: str) -> float:
from PIL import Image, ImageDraw, ImageFont
draw = ImageDraw.Draw(Image.new("1", (render.PANEL_WIDTH_PX,
render.PANEL_HEIGHT_PX), 255))
font = render._load_font(ImageFont, render.FOOTER_FONT_PX, bold=True)
return draw.textlength(text, font=font)
@pytest.mark.parametrize("name", sorted(render.IMAGESET_LABELS))
@pytest.mark.parametrize("missing", [False, True])
def test_imageset_footer_fits_the_panel_with_room_for_a_wider_font(name, missing):
"""Jede Fusszeile des Umschalters - beide Saetze, mit und ohne Fehlt-Warnung - muss
ungekuerzt auf das Panel passen, auch wenn die Schrift des Zielgeraets breiter baut
als die hier installierte. Der Fehlt-Fall ist der wichtigere der vier: er sagt, dass
der GEWAEHLTE Bildsatz nicht auf dem Pi liegt, und genau der war unsichtbar."""
text, = (t for t, role in render._imageset_blocks(
{"imageset": name, "imageset_ok": not missing}) if role == render.ROLE_FOOTER)
max_width_px = render.PANEL_WIDTH_PX - 2 * render.TEXT_MARGIN_PX
width_px = _footer_width_px(text)
assert width_px * FOOTER_WIDTH_SAFETY <= max_width_px, (
"Fusszeile {!r} misst {:.1f} px; mit {:.0f} %% Reserve fuer eine breitere "
"Schrift bleiben nur {:.1f} px Platz".format(
text, width_px, (FOOTER_WIDTH_SAFETY - 1) * 100, max_width_px))