Files
Plugin_SN_Basis/tests/test_print_kartenrand.py
T
DanielandClaude Opus 5.5 9cff75b674 Mehrspaltige Legenden schmaler (#96)
Mit gleichen Spaltenbreiten bestimmte der längste Legendentext die Breite aller Spalten;
die dreispaltige Basemap-Legende wurde 431 mm breit. Jetzt: ungleich breite Spalten,
automatischer Textumbruch nach 60 mm (TEXTUMBRUCH_MM in print_kartenrand, auch für die
Basemap-Legende) und zweizeiliger Titel „Kartengrundlage / (basemap.de)“, der sonst die
Mindestbreite bestimmte. Im QGIS-Test: dreispaltig 226 statt 431 mm, einspaltig 89 statt 163 mm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:20:15 +02:00

195 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Tests für modules/print_kartenrand.py – Anordnung des Kartenrands neben der Karte (Basis #93).
Bei kleinen Verfahren oder großem Maßstab ist die Karte niedriger als Legenden, Quellenangabe
und Kartenschild zusammen; die Elemente überlappten sich. Die Anordnung wählt bis zu drei
Legendenspalten (Kartenrand wird dafür breiter) und vergrößert notfalls die Seitenhöhe. Karte
bleibt in Größe und Lage gleich. Die Wirkung im echten Layout ist im QGIS-Test geprüft.
"""
import unittest
from unittest.mock import MagicMock, patch
from sn_basis.modules import print_kartenrand as kr
from sn_basis.modules.print_layout import PrintLayout
from sn_basis.modules.print_kartenrand import berechne_anordnung, ordne_kartenrand_an
KARTE = (10.0, 10.0, 360.0, 300.0) # links, oben, Breite, Höhe in mm
SEITE = (580.0, 320.0)
FEST = kr.QUELLE_HOEHE_MM + kr.ABSTAND_QUELLE_SCHILD_MM + kr.SCHILD_HOEHE_MM # 122 mm
def _legenden(*je_spalten):
"""Eine Legende je Argument: (w1, h1, w2, h2, w3, h3)."""
return {n: [(l[2 * (n - 1)], l[2 * (n - 1) + 1]) for l in je_spalten] for n in (1, 2, 3)}
class TestBerechneAnordnung(unittest.TestCase):
def test_passt_einspaltig_nichts_aendert_sich(self):
"""Prüft, dass bei genug Platz neben der Karte einspaltig angeordnet wird und Seite und Kartenschild bleiben."""
anordnung = berechne_anordnung(KARTE, SEITE, _legenden((80, 100, 120, 50, 160, 34)))
self.assertEqual(anordnung.spalten, 1)
self.assertEqual((anordnung.seite_breite, anordnung.seite_hoehe), SEITE)
self.assertEqual(anordnung.verschiebung_y, 0.0)
self.assertEqual(anordnung.spaltenbreite, kr.MIN_SPALTENBREITE_MM)
def test_mehr_spalten_und_breiterer_rand(self):
"""Prüft, dass bei zu hohen Legenden die kleinste passende Spaltenzahl gewählt und der Kartenrand dafür verbreitert wird."""
# Hauptlegende + Basemap-Legende; einspaltig 250 mm, zweispaltig 140 mm hoch – passt in 300 mm Karte ab 2 Spalten
legenden = _legenden((150, 150, 300, 80, 450, 55), (170, 100, 340, 60, 510, 40))
anordnung = berechne_anordnung(KARTE, SEITE, legenden)
self.assertEqual(anordnung.spalten, 2)
self.assertEqual(anordnung.spaltenbreite, 340)
spalte_links = KARTE[0] + KARTE[2] + kr.RAND_MM
self.assertEqual(anordnung.seite_breite, spalte_links + 340 + kr.RAND_MM)
self.assertEqual(anordnung.seite_hoehe, SEITE[1])
self.assertEqual(anordnung.verschiebung_y, 0.0)
def test_seite_wird_hoeher_wenn_auch_drei_spalten_nicht_reichen(self):
"""Prüft, dass die Seite höher wird und Quellenangabe und Kartenschild nach unten rutschen, wenn auch drei Spalten nicht reichen."""
karte = (10.0, 10.0, 160.0, 80.0) # kleines Verfahren
anordnung = berechne_anordnung(karte, (380.0, 100.0), _legenden((150, 300, 300, 160, 450, 110)))
self.assertEqual(anordnung.spalten, 3)
benoetigt = 110 + kr.ABSTAND_LEGENDEN_MM + FEST
self.assertAlmostEqual(anordnung.seite_hoehe, karte[1] + benoetigt + kr.RAND_MM)
self.assertAlmostEqual(anordnung.verschiebung_y, benoetigt - karte[3])
self.assertEqual(anordnung.spaltenbreite, 450)
def test_seite_wird_nie_kleiner(self):
"""Prüft, dass Seitenbreite und -höhe nie unter die ursprüngliche Seitengröße fallen."""
anordnung = berechne_anordnung(KARTE, (900.0, 600.0), _legenden((80, 50, 120, 30, 160, 20)))
self.assertEqual((anordnung.seite_breite, anordnung.seite_hoehe), (900.0, 600.0))
def test_ohne_legenden_nur_kartenschild(self):
"""Prüft, dass ohne Legenden nur Quellenangabe und Kartenschild zählen und die Seite bei zu niedriger Karte höher wird."""
karte = (10.0, 10.0, 100.0, 100.0)
anordnung = berechne_anordnung(karte, (310.0, 120.0), {1: [], 2: [], 3: []})
self.assertEqual(anordnung.spalten, 1)
self.assertAlmostEqual(anordnung.verschiebung_y, FEST - 100.0)
self.assertAlmostEqual(anordnung.seite_hoehe, 10.0 + FEST + kr.RAND_MM)
# ---------------------------------------------------------------- Layout-Fakes
class _Punkt:
def __init__(self, x, y): self._x, self._y = x, y
def x(self): return self._x
def y(self): return self._y
class _Element:
def __init__(self, x, y, b, h):
self.x, self.y, self.b, self.h = x, y, b, h
self.verschoben = []
def positionWithUnits(self): return _Punkt(self.x, self.y)
def sceneBoundingRect(self):
return type("R", (), {"x": lambda s: self.x, "y": lambda s: self.y,
"width": lambda s: self.b, "height": lambda s: self.h})()
def attemptMove(self, punkt):
neu_y = punkt.y() if callable(punkt.y) else punkt.y
self.verschoben.append(neu_y)
self.y = neu_y
class _Legende(_Element):
def __init__(self, x, y, groessen, unten_verankert=False):
super().__init__(x, y, groessen[1][0], groessen[1][1])
self.groessen, self.spalten, self.unten = groessen, 1, unten_verankert
self.gleiche_breite, self.umbruch = True, 0
def setColumnCount(self, n): self.spalten = n
def setSplitLayer(self, wert): pass
def setEqualColumnWidth(self, wert): self.gleiche_breite = wert
def setAutoWrapLinesAfter(self, mm): self.umbruch = mm
def setLocked(self, wert): pass
def adjustBoxSize(self):
self.b, self.h = self.groessen[self.spalten]
def sizeWithUnits(self):
return type("S", (), {"width": lambda s: self.b, "height": lambda s: self.h})()
def attemptResize(self, groesse): pass
class _Seite:
def __init__(self, b, h): self.b, self.h = b, h
def pageSize(self): return type("S", (), {"width": lambda s: self.b, "height": lambda s: self.h})()
def setPageSize(self, groesse):
self.b = groesse.width() if callable(groesse.width) else groesse.width
self.h = groesse.height() if callable(groesse.height) else groesse.height
class _Layout:
def __init__(self, elemente, seite):
self.elemente, self.seite = elemente, seite
def itemById(self, element_id): return self.elemente.get(element_id)
def pageCollection(self): return type("P", (), {"page": lambda s, i: self.seite})()
class TestOrdneKartenrandAn(unittest.TestCase):
def _layout(self, legende_groessen):
karte = _Element(10, 10, 160, 80)
elemente = {
"Hauptkarte": karte,
"Legende": _Legende(180, 10, legende_groessen),
"Quellenangabe": _Element(180, -32, 180, 20),
"Kartenschild": _Element(180, -2, 180, 92),
"Aktenzeichen": _Element(196, 87, 171, 4),
}
return _Layout(elemente, _Seite(380, 100)), elemente
def test_seite_hoeher_und_kartenschild_verschoben(self):
"""Prüft, dass bei zu niedriger Karte die Legende dreispaltig wird, die Seite wächst und Quellenangabe und Kartenschild um denselben Betrag nach unten rutschen."""
layout, elemente = self._layout({1: (150, 300), 2: (300, 160), 3: (450, 110)})
anordnung = ordne_kartenrand_an(layout, rendern=lambda *a: None)
self.assertEqual(elemente["Legende"].spalten, 3)
self.assertGreater(layout.seite.h, 100)
dy = anordnung.verschiebung_y
self.assertGreater(dy, 0)
self.assertAlmostEqual(elemente["Quellenangabe"].y, -32 + dy)
self.assertAlmostEqual(elemente["Kartenschild"].y, -2 + dy)
self.assertAlmostEqual(elemente["Aktenzeichen"].y, 87 + dy)
self.assertEqual(elemente["Hauptkarte"].verschoben, [])
self.assertEqual(layout.seite.b, 180 + 450 + kr.RAND_MM)
def test_spalten_ungleich_breit_und_text_umgebrochen(self):
"""Prüft, dass die Legenden ungleich breite Spalten und einen automatischen Textumbruch bekommen, damit mehrspaltige Legenden nicht unnötig breit werden (#96)."""
layout, elemente = self._layout({1: (150, 300), 2: (300, 160), 3: (450, 110)})
ordne_kartenrand_an(layout, rendern=lambda *a: None)
self.assertFalse(elemente["Legende"].gleiche_breite)
self.assertEqual(elemente["Legende"].umbruch, kr.TEXTUMBRUCH_MM)
def test_passt_nichts_wird_verschoben(self):
"""Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden."""
layout, elemente = self._layout({1: (100, 10), 2: (200, 6), 3: (300, 4)})
elemente["Hauptkarte"].h = 300
anordnung = ordne_kartenrand_an(layout, rendern=lambda *a: None)
self.assertEqual(anordnung.spalten, 1)
self.assertEqual((layout.seite.b, layout.seite.h), (380, 100))
self.assertEqual(elemente["Kartenschild"].verschoben, [])
def test_ohne_karte_keine_anordnung(self):
"""Prüft, dass ein Layout ohne Hauptkarte unverändert bleibt."""
self.assertIsNone(ordne_kartenrand_an(_Layout({}, _Seite(100, 100)), rendern=lambda *a: None))
class TestAnbindungPrintLayout(unittest.TestCase):
def test_vorlage_ordnet_kartenrand_an(self):
"""Prüft, dass PrintLayout beim Anlegen einer Vorlage den Kartenrand anordnen lässt."""
layout = MagicMock()
with patch("sn_basis.modules.print_kartenrand.ordne_kartenrand_an") as anordnen:
PrintLayout(project=MagicMock())._ordne_kartenrand_an(layout)
anordnen.assert_called_once_with(layout)
def test_fehler_bei_der_anordnung_verhindert_die_vorlage_nicht(self):
"""Prüft, dass ein Fehler bei der Anordnung nur als Warnung gemeldet wird und keine Ausnahme durchreicht."""
with patch("sn_basis.modules.print_kartenrand.ordne_kartenrand_an", side_effect=RuntimeError("kaputt")), patch("sn_basis.functions.message_wrapper.warning") as warnung:
PrintLayout(project=MagicMock())._ordne_kartenrand_an(MagicMock())
self.assertIn("kaputt", warnung.call_args[0][1])
if __name__ == "__main__":
unittest.main()