diff --git a/tools/station/display/render.py b/tools/station/display/render.py index dc7ecd4..6c1bdac 100644 --- a/tools/station/display/render.py +++ b/tools/station/display/render.py @@ -224,7 +224,11 @@ IMAGESET_MISSING_LABELS = { # der Anzeigedienst wieder zeichnet, und an genau diesem Pi hat ein Stromzug in dieser # Wartezeit schon eine SD-Karte gekostet. FINAL_LABELS = { - "poweroff": ("AUSGESCHALTET", "Strom kann getrennt werden", []), + # "Strom kann getrennt werden" war in der Hero-Schrift 262,1 px breit und kam am + # Geraet als "Strom kann getrennt wer..." an - siehe die Fusszeile weiter oben, es + # ist derselbe Fehler. Alle festen Beschriftungen haengen jetzt an + # test_fixed_labels_fit_the_panel_with_room_for_a_wider_font. + "poweroff": ("AUSGESCHALTET", "Strom trennen erlaubt", []), "reboot": ("NEUSTART", "Strom NICHT trennen!", ["Der Pi kommt in rund 2 min wieder.", "Solange bleibt dieses Bild stehen."]), diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py index 4a98913..2fad452 100644 --- a/tools/station/tests/test_render.py +++ b/tools/station/tests/test_render.py @@ -1885,11 +1885,13 @@ def test_the_shutdown_and_the_reboot_screen_differ(): off, reboot = _final_text("poweroff"), _final_text("reboot") assert off != reboot assert "AUSGESCHALTET" in off and "NEUSTART" in reboot - # Ausschalten: erlaubt das Trennen ausdruecklich. - assert "kann getrennt werden" in off + # Ausschalten: erlaubt das Trennen ausdruecklich. Geprueft wird die ZUSAGE, nicht + # der genaue Satz - der Wortlaut musste am 2026-09-15 kuerzer werden, weil die alte + # Fassung ("Strom kann getrennt werden", 262,1 px) auf dem Panel abgeschnitten war. + assert "trennen erlaubt" in off # Neustart: verbietet es ebenso ausdruecklich - und sagt nirgends das Gegenteil. assert "NICHT trennen" in reboot - assert "kann getrennt werden" not in reboot + assert "trennen erlaubt" not in reboot def test_the_reboot_screen_says_how_long_it_stays(): @@ -2094,19 +2096,49 @@ def _footer_width_px(text: str) -> float: 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) +def _fixed_labels(): + """Jede fest im Code stehende Beschriftung mit der Rolle, in der sie gezeichnet wird. + Bewusst VOLLSTAENDIG und nicht auf die Fusszeile beschraenkt: der Geraetetermin hat + denselben Fehler an zwei Stellen gezeigt (Fusszeile UND das Abschlussbild "Strom kann + getrennt werden", 262,1 px bei 246 px Platz). Eine Pruefung, die nur die zuerst + entdeckte Stelle abdeckt, haette die zweite wieder durchgelassen.""" + out = [] + for kind, (title, hero, details) in render.FINAL_LABELS.items(): + out.append(("FINAL/%s/titel" % kind, title, render.ROLE_HEADER)) + out.append(("FINAL/%s/hero" % kind, hero, render.ROLE_HERO)) + for i, detail in enumerate(details): + out.append(("FINAL/%s/detail%d" % (kind, i), detail, render.ROLE_DETAIL)) + for name, label in render.IMAGESET_LABELS.items(): + out.append(("BILDSATZ/%s" % name, label, render.ROLE_FOOTER)) + for name, label in render.IMAGESET_MISSING_LABELS.items(): + out.append(("BILDSATZ-FEHLT/%s" % name, label, render.ROLE_FOOTER)) + out.append(("FREIGABE", render.ARMED_LABEL, render.ROLE_HERO)) + return out + + +_ROLE_FONTS = { + render.ROLE_HEADER: (lambda: render.HEADER_FONT_PX, True), + render.ROLE_HERO: (lambda: render.HERO_FONT_PX, True), + render.ROLE_DETAIL: (lambda: render.DETAIL_FONT_PX, False), + render.ROLE_BIG: (lambda: render.BIG_FONT_PX, True), + render.ROLE_FOOTER: (lambda: render.FOOTER_FONT_PX, True), +} + + +@pytest.mark.parametrize("source,text,role", _fixed_labels(), + ids=[x[0] for x in _fixed_labels()]) +def test_fixed_labels_fit_the_panel_with_room_for_a_wider_font(source, text, role): + """Jede feste Beschriftung muss UNGEKUERZT auf das Panel passen, auch wenn die + Schrift des Zielgeraets breiter baut als die hier installierte.""" + from PIL import Image, ImageDraw, ImageFont + draw = ImageDraw.Draw(Image.new("1", (render.PANEL_WIDTH_PX, + render.PANEL_HEIGHT_PX), 255)) + size_px, bold = _ROLE_FONTS[role] + font = render._load_font(ImageFont, size_px(), bold=bold) + width_px = draw.textlength(text, font=font) 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)) + "{}: {!r} misst {:.1f} px; mit {:.0f} % Reserve fuer eine breitere Schrift " + "bleiben nur {:.1f} px Platz".format( + source, text, width_px, (FOOTER_WIDTH_SAFETY - 1) * 100, max_width_px))