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>
195 lines
9.7 KiB
Python
195 lines
9.7 KiB
Python
"""
|
||
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()
|