Merge pull request 'Bugfix/96 legenden spaltenbreite' (#97) from bugfix/96-legenden-spaltenbreite into unstable
Release Plugin / release (push) Successful in 4s

Reviewed-on: #97
This commit was merged in pull request #97.
This commit is contained in:
2026-10-02 11:30:38 +00:00
6 changed files with 217 additions and 8 deletions
+1
View File
@@ -1,3 +1,4 @@
- Bugfix: Mehrspaltige Legenden waren sehr breit, weil alle Spalten so breit wie der längste Text waren. Legendentext bricht jetzt nach 60 mm um, Spalten sind ungleich breit, der Titel der Basemap-Legende ist zweizeilig – im Test wurde die dreispaltige Basemap-Legende 226 statt 431 mm breit. Standard- und Mindestbreite der Legenden ist die Breite des Kartenschilds (180 mm); sie wird am Layout vermerkt und beim Laden des Projekts wieder gesetzt, weil QGIS sie nicht speichert (#96)
- Bugfix: Bei kleinen Verfahren oder großem Maßstab überlappten sich Legenden, Quellenangabe und Kartenschild, weil sie zusammen höher als die Karte waren. Beim Anlegen einer §38-/§41-Vorlage werden die Legenden jetzt bei Bedarf zwei- oder dreispaltig gesetzt (Kartenrand wird dafür breiter); reicht auch das nicht, wird die Seite höher und Quellenangabe und Kartenschild rutschen nach unten. Größe und Lage der Karte bleiben gleich (#93) - Bugfix: Bei kleinen Verfahren oder großem Maßstab überlappten sich Legenden, Quellenangabe und Kartenschild, weil sie zusammen höher als die Karte waren. Beim Anlegen einer §38-/§41-Vorlage werden die Legenden jetzt bei Bedarf zwei- oder dreispaltig gesetzt (Kartenrand wird dafür breiter); reicht auch das nicht, wird die Seite höher und Quellenangabe und Kartenschild rutschen nach unten. Größe und Lage der Karte bleiben gleich (#93)
- Bugfix: Weiße Basemap-Flächen ohne Kontur (z. B. „Platz“) waren in der Basemap-Legende auf weißem Grund unsichtbar; ihr Legendensymbol bekommt jetzt einen dünnen grauen Rand (0,15 mm), die Karte bleibt unverändert (#92) - Bugfix: Weiße Basemap-Flächen ohne Kontur (z. B. „Platz“) waren in der Basemap-Legende auf weißem Grund unsichtbar; ihr Legendensymbol bekommt jetzt einen dünnen grauen Rand (0,15 mm), die Karte bleibt unverändert (#92)
- Bugfix: Punktlinien des Basemap-Layers (Baumreihe, Hecke, Durchlass, Seilbahnen u. a., 12 Styles) wurden in Karte und Legende nicht gezeichnet – basemap.de nutzt Strichlänge 0 mit runder Kappe, Qt zeichnet Striche der Länge 0 nicht. Neuer Knopf „Punktlinien korrigieren“ im Basemap-Bereich des Druck-Tabs ersetzt die Strichlänge 0 durch 0,01 (festes Muster und zoomabhängiger Ausdruck); die Korrektur lässt sich als Stilvorlage sichern (#90) - Bugfix: Punktlinien des Basemap-Layers (Baumreihe, Hecke, Durchlass, Seilbahnen u. a., 12 Styles) wurden in Karte und Legende nicht gezeichnet – basemap.de nutzt Strichlänge 0 mit runder Kappe, Qt zeichnet Striche der Länge 0 nicht. Neuer Knopf „Punktlinien korrigieren“ im Basemap-Bereich des Druck-Tabs ersetzt die Strichlänge 0 durch 0,01 (festes Muster und zoomabhängiger Ausdruck); die Korrektur lässt sich als Stilvorlage sichern (#90)
+7
View File
@@ -8,6 +8,10 @@ from sn_basis.functions.verfahrensgebiet_manager import (
setup_verfahrensgebiet_listener, setup_verfahrensgebiet_listener,
teardown_verfahrensgebiet_listener, teardown_verfahrensgebiet_listener,
) )
from sn_basis.modules.print_kartenrand import (
setup_mindestbreiten_listener,
teardown_mindestbreiten_listener,
)
from sn_basis.ui.navigation import Navigation from sn_basis.ui.navigation import Navigation
@@ -41,6 +45,8 @@ class BasisPlugin:
# Zentrale Verwaltung des Verfahrensgebiet-Layers starten # Zentrale Verwaltung des Verfahrensgebiet-Layers starten
setup_verfahrensgebiet_listener() setup_verfahrensgebiet_listener()
# Mindestbreite der Drucklegenden nach dem Laden eines Projekts wieder setzen (Basis #96)
setup_mindestbreiten_listener()
for name, plugin in plugins.items(): for name, plugin in plugins.items():
if name.startswith("sn_") and name != "sn_basis": if name.startswith("sn_") and name != "sn_basis":
@@ -57,6 +63,7 @@ class BasisPlugin:
Räumt UI-Komponenten sauber auf. Räumt UI-Komponenten sauber auf.
""" """
teardown_verfahrensgebiet_listener() teardown_verfahrensgebiet_listener()
teardown_mindestbreiten_listener()
if self.ui: if self.ui:
self.ui.remove_all() self.ui.remove_all()
+9 -3
View File
@@ -40,11 +40,13 @@ from sn_basis.functions.qgiscore_wrapper import (
QgsVectorLayer, QgsVectorLayer,
) )
from sn_basis.functions.qt_wrapper import QSize from sn_basis.functions.qt_wrapper import QSize
from sn_basis.modules.print_kartenrand import MIN_SPALTENBREITE_MM, TEXTUMBRUCH_MM
MM = QgsUnitTypes.LayoutMillimeters MM = QgsUnitTypes.LayoutMillimeters
LEGENDE_ID = "Legende Basemap" LEGENDE_ID = "Legende Basemap"
LEGENDE_TITEL = "Kartengrundlage (basemap.de)" # Zweizeilig, damit der Titel nicht die Mindestbreite der Legende bestimmt (Basis #96)
LEGENDE_TITEL = "Kartengrundlage\n(basemap.de)"
HAUPTKARTE_ID = "Hauptkarte" HAUPTKARTE_ID = "Hauptkarte"
HAUPTLEGENDE_ID = "Legende" HAUPTLEGENDE_ID = "Legende"
QUELLENANGABE_ID = "Quellenangabe" QUELLENANGABE_ID = "Quellenangabe"
@@ -330,6 +332,8 @@ def _neues_legendenelement(layout: Any) -> Any:
_aufruf(legende, "setFrameEnabled", True) _aufruf(legende, "setFrameEnabled", True)
_aufruf(legende, "setBackgroundEnabled", False) _aufruf(legende, "setBackgroundEnabled", False)
_aufruf(legende, "setFrameStrokeWidth", _layout_messung(RAHMENSTAERKE_MM)) _aufruf(legende, "setFrameStrokeWidth", _layout_messung(RAHMENSTAERKE_MM))
# Mindestens so breit wie das Kartenschild (Basis #96)
_aufruf(legende, "setMinimumSize", QgsLayoutSize(MIN_SPALTENBREITE_MM, 0.0, MM))
layout.addLayoutItem(legende) layout.addLayoutItem(legende)
return legende return legende
@@ -350,7 +354,8 @@ def mindestgroesse(legende: Any, layout: Any, spalten: int) -> tuple[float, floa
return None return None
einstellungen.setColumnCount(spalten) einstellungen.setColumnCount(spalten)
einstellungen.setSplitLayer(True) einstellungen.setSplitLayer(True)
einstellungen.setEqualColumnWidth(True) einstellungen.setEqualColumnWidth(False)
einstellungen.setAutoWrapLinesAfter(TEXTUMBRUCH_MM)
try: try:
groesse = QgsLegendRenderer(legende.model(), einstellungen).minimumSize(kontext) groesse = QgsLegendRenderer(legende.model(), einstellungen).minimumSize(kontext)
except Exception: except Exception:
@@ -399,7 +404,7 @@ def platziere(layout: Any, legende: Any) -> str:
spalten, groesse = spalten + 1, breiter spalten, groesse = spalten + 1, breiter
_aufruf(legende, "setColumnCount", spalten) _aufruf(legende, "setColumnCount", spalten)
_aufruf(legende, "setSplitLayer", True) _aufruf(legende, "setSplitLayer", True)
_aufruf(legende, "setEqualColumnWidth", True) _aufruf(legende, "setEqualColumnWidth", False)
unten_links = getattr(getattr(QgsLayoutItem, "ReferencePoint", QgsLayoutItem), "LowerLeft", None) unten_links = getattr(getattr(QgsLayoutItem, "ReferencePoint", QgsLayoutItem), "LowerLeft", None)
if unten_links is not None: if unten_links is not None:
@@ -450,6 +455,7 @@ def schreibe_legende(layout: Any, map_item: Any, eintraege: list, symbole: dict[
legende.setAutoUpdateModel(False) legende.setAutoUpdateModel(False)
legende.setLegendFilterByMapEnabled(False) legende.setLegendFilterByMapEnabled(False)
legende.setTitle(LEGENDE_TITEL) legende.setTitle(LEGENDE_TITEL)
_aufruf(legende, "setAutoWrapLinesAfter", TEXTUMBRUCH_MM)
wurzel = legende.model().rootGroup() wurzel = legende.model().rootGroup()
wurzel.removeAllChildren() wurzel.removeAllChildren()
for gruppe, layer_liste in gruppen: for gruppe, layer_liste in gruppen:
+98 -3
View File
@@ -21,23 +21,38 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Callable from typing import Any, Callable
from sn_basis.functions.qgiscore_wrapper import QgsLayoutExporter, QgsLayoutPoint, QgsLayoutSize, QgsUnitTypes from sn_basis.functions.qgiscore_wrapper import (
QGIS_AVAILABLE,
QgsLayoutExporter,
QgsLayoutPoint,
QgsLayoutSize,
QgsProject,
QgsUnitTypes,
)
from sn_basis.functions.qt_wrapper import QSize from sn_basis.functions.qt_wrapper import QSize
MM = QgsUnitTypes.LayoutMillimeters MM = QgsUnitTypes.LayoutMillimeters
RAND_MM = 10.0 # Seitenrand und Abstand Karte → Kartenrand RAND_MM = 10.0 # Seitenrand und Abstand Karte → Kartenrand
MIN_SPALTENBREITE_MM = 180.0 # Breite von Kartenschild und Quellenangabe # Breite von Kartenschild und Quellenangabe (PrintLayout._LEGEND_WIDTH_MM) – zugleich Standard- und
# Mindestbreite der Legenden, damit sie optisch mit dem Kartenschild übereinstimmen.
MIN_SPALTENBREITE_MM = 180.0
ABSTAND_LEGENDEN_MM = 4.0 # unter jeder Legende ABSTAND_LEGENDEN_MM = 4.0 # unter jeder Legende
QUELLE_HOEHE_MM = 20.0 QUELLE_HOEHE_MM = 20.0
ABSTAND_QUELLE_SCHILD_MM = 10.0 ABSTAND_QUELLE_SCHILD_MM = 10.0
SCHILD_HOEHE_MM = 92.0 SCHILD_HOEHE_MM = 92.0
MAX_SPALTEN = 3 MAX_SPALTEN = 3
VERMESSUNG_DPI = 25 # Auflösung des einmaligen Zeichnens zum Vermessen VERMESSUNG_DPI = 25 # Auflösung des einmaligen Zeichnens zum Vermessen
# Legendentext bricht nach dieser Breite um, Spalten sind ungleich breit – sonst bestimmt der längste
# Text die Breite aller Spalten (dreispaltige Basemap-Legende 431 statt 226 mm, Basis #96).
TEXTUMBRUCH_MM = 60.0
HAUPTKARTE_ID = "Hauptkarte" HAUPTKARTE_ID = "Hauptkarte"
LEGENDEN_IDS = ("Legende", "Legende Basemap") # von oben nach unten LEGENDEN_IDS = ("Legende", "Legende Basemap") # von oben nach unten
BASEMAP_LEGENDE_ID = "Legende Basemap" BASEMAP_LEGENDE_ID = "Legende Basemap"
# Layout-Eigenschaft: Mindestbreite der Legenden. QGIS speichert setMinimumSize nicht im Projekt;
# der Vermerk erlaubt, sie beim Laden wieder zu setzen (wende_mindestbreiten_an).
LAYOUT_MINDESTBREITE = "sn_legende_mindestbreite"
QUELLENANGABE_ID = "Quellenangabe" QUELLENANGABE_ID = "Quellenangabe"
# Elemente unterhalb der Legenden, die bei höherer Seite gemeinsam nach unten rutschen # Elemente unterhalb der Legenden, die bei höherer Seite gemeinsam nach unten rutschen
UNTERE_ELEMENTE = ( UNTERE_ELEMENTE = (
@@ -124,7 +139,8 @@ def _rechteck(element: Any) -> tuple[float, float, float, float]:
def _legende_groesse(legende: Any, spalten: int) -> tuple[float, float]: def _legende_groesse(legende: Any, spalten: int) -> tuple[float, float]:
_aufruf(legende, "setColumnCount", spalten) _aufruf(legende, "setColumnCount", spalten)
_aufruf(legende, "setSplitLayer", True) _aufruf(legende, "setSplitLayer", True)
_aufruf(legende, "setEqualColumnWidth", True) _aufruf(legende, "setEqualColumnWidth", False)
_aufruf(legende, "setAutoWrapLinesAfter", TEXTUMBRUCH_MM)
_aufruf(legende, "adjustBoxSize") _aufruf(legende, "adjustBoxSize")
groesse = legende.sizeWithUnits() groesse = legende.sizeWithUnits()
return _wert(groesse, "width"), _wert(groesse, "height") return _wert(groesse, "width"), _wert(groesse, "height")
@@ -137,6 +153,84 @@ def _rendere_legenden(layout: Any, legenden: list[Any]) -> None:
_aufruf(exporter, "renderRegionToImage", legende.sceneBoundingRect(), QSize(), VERMESSUNG_DPI) _aufruf(exporter, "renderRegionToImage", legende.sceneBoundingRect(), QSize(), VERMESSUNG_DPI)
def setze_legenden_mindestbreite(layout: Any, breite: float = MIN_SPALTENBREITE_MM, merken: bool = True) -> int:
"""Gibt den Legenden des Layouts eine Mindestbreite (Standard: Breite des Kartenschilds).
Die Legenden passen sich weiter ihrem Inhalt an, werden aber nie schmaler. Mit ``merken`` wird die
Breite am Layout vermerkt, damit sie nach dem Laden des Projekts wieder gesetzt werden kann.
Returns
-------
int
Anzahl der Legenden, die eine Mindestbreite bekommen haben.
"""
anzahl = 0
for legenden_id in LEGENDEN_IDS:
legende = _aufruf(layout, "itemById", legenden_id)
if legende is not None:
legende.setMinimumSize(QgsLayoutSize(breite, 0.0, MM))
anzahl += 1
if merken:
layout.setCustomProperty(LAYOUT_MINDESTBREITE, float(breite))
return anzahl
def wende_mindestbreiten_an(project: Any | None = None) -> int:
"""Setzt die vermerkten Mindestbreiten der Legenden aller Drucklayouts erneut (nach dem Laden).
Returns
-------
int
Anzahl der Layouts, an denen eine Mindestbreite gesetzt wurde.
"""
project = project or QgsProject.instance()
verwaltung = _aufruf(project, "layoutManager")
anzahl = 0
for layout in _aufruf(verwaltung, "printLayouts", standard=[]) or []:
wert = _aufruf(layout, "customProperty", LAYOUT_MINDESTBREITE, None)
try:
breite = float(wert)
except (TypeError, ValueError):
continue
if breite > 0 and setze_legenden_mindestbreite(layout, breite, merken=False):
anzahl += 1
return anzahl
_listener_aktiv = False
def _on_projekt_gelesen(*_: Any) -> None:
wende_mindestbreiten_an()
def setup_mindestbreiten_listener() -> None:
"""Setzt die Mindestbreiten bei jedem Laden eines Projekts neu (einmal beim Plugin-Start aufrufen)."""
global _listener_aktiv
if _listener_aktiv or not QGIS_AVAILABLE:
return
try:
project = QgsProject.instance()
project.readProject.connect(_on_projekt_gelesen)
_listener_aktiv = True
wende_mindestbreiten_an(project) # bereits geöffnetes Projekt
except Exception:
pass
def teardown_mindestbreiten_listener() -> None:
"""Trennt den Listener wieder (beim Entladen des Plugins; idempotent)."""
global _listener_aktiv
if not _listener_aktiv:
return
try:
QgsProject.instance().readProject.disconnect(_on_projekt_gelesen)
except Exception:
pass
finally:
_listener_aktiv = False
def _verschiebe(element: Any, dy: float) -> None: def _verschiebe(element: Any, dy: float) -> None:
position = element.positionWithUnits() position = element.positionWithUnits()
element.attemptMove(QgsLayoutPoint(_wert(position, "x"), _wert(position, "y") + dy, MM)) element.attemptMove(QgsLayoutPoint(_wert(position, "x"), _wert(position, "y") + dy, MM))
@@ -156,6 +250,7 @@ def ordne_kartenrand_an(layout: Any, rendern: Callable[[Any, list], None] | None
legenden = [l for l in (_aufruf(layout, "itemById", i) for i in LEGENDEN_IDS) if l is not None] legenden = [l for l in (_aufruf(layout, "itemById", i) for i in LEGENDEN_IDS) if l is not None]
for legende in legenden: for legende in legenden:
_aufruf(legende, "setLocked", False) _aufruf(legende, "setLocked", False)
setze_legenden_mindestbreite(layout)
(rendern or _rendere_legenden)(layout, legenden) (rendern or _rendere_legenden)(layout, legenden)
groessen = {n: [_legende_groesse(l, n) for l in legenden] for n in range(1, MAX_SPALTEN + 1)} groessen = {n: [_legende_groesse(l, n) for l in legenden] for n in range(1, MAX_SPALTEN + 1)}
+39 -1
View File
@@ -62,10 +62,11 @@ class _Legende(_Element):
self.spalten = 1 self.spalten = 1
self.bewegt = None self.bewegt = None
self.groesse = None self.groesse = None
self.gleiche_breite = True
def setColumnCount(self, n): self.spalten = n def setColumnCount(self, n): self.spalten = n
def setSplitLayer(self, wert): pass def setSplitLayer(self, wert): pass
def setEqualColumnWidth(self, wert): pass def setEqualColumnWidth(self, wert): self.gleiche_breite = wert
def setReferencePoint(self, punkt): pass def setReferencePoint(self, punkt): pass
def attemptMove(self, punkt): self.bewegt = punkt def attemptMove(self, punkt): self.bewegt = punkt
def attemptResize(self, groesse): self.groesse = groesse def attemptResize(self, groesse): self.groesse = groesse
@@ -214,6 +215,43 @@ class TestPlatzierung(unittest.TestCase):
self.assertEqual(hinweis, "") self.assertEqual(hinweis, "")
self.assertEqual(legende.spalten, 3) self.assertEqual(legende.spalten, 3)
def test_spalten_ungleich_breit(self):
"""Prüft, dass die Basemap-Legende ungleich breite Spalten bekommt (#96)."""
legende, _ = self._platziere(self._layout(), {1: (100.0, 300.0), 2: (200.0, 150.0), 3: (300.0, 100.0)})
self.assertFalse(legende.gleiche_breite)
def test_mindestgroesse_mit_ungleichen_spalten_und_umbruch(self):
"""Prüft, dass der Platzbedarf mit ungleich breiten Spalten und Textumbruch berechnet wird (#96)."""
class Einstellungen:
def __init__(self): self.werte = {}
def __getattr__(self, name): return lambda wert: self.werte.__setitem__(name, wert)
einstellungen = Einstellungen()
legende = type("L", (), {"legendSettings": lambda s: einstellungen, "model": lambda s: None})()
renderer = type("R", (), {"minimumSize": lambda s, k: type("G", (), {"width": lambda s: 80.0, "height": lambda s: 40.0})()})
with patch.object(bll, "QgsLayoutUtils", type("U", (), {"createRenderContextForLayout": staticmethod(lambda l, p: object())})), patch.object(bll, "QgsLegendRenderer", lambda modell, einst: renderer()):
self.assertEqual(bll.mindestgroesse(legende, object(), 2), (80.0, 40.0))
self.assertEqual(einstellungen.werte["setColumnCount"], 2)
self.assertFalse(einstellungen.werte["setEqualColumnWidth"])
self.assertEqual(einstellungen.werte["setAutoWrapLinesAfter"], bll.TEXTUMBRUCH_MM)
def test_neue_basemap_legende_mindestens_so_breit_wie_kartenschild(self):
"""Prüft, dass eine neu angelegte Basemap-Legende die Breite des Kartenschilds als Mindestbreite bekommt."""
class Element:
def __init__(self, layout): self.mindestbreite = None
def __getattr__(self, name): return lambda *a: None
def setMinimumSize(self, groesse):
self.mindestbreite = groesse.width() if callable(groesse.width) else groesse.width
layout = type("L", (), {"addLayoutItem": lambda s, e: None})()
with patch.object(bll, "QgsLayoutItemLegend", Element):
legende = bll._neues_legendenelement(layout)
self.assertEqual(legende.mindestbreite, 180.0)
def test_titel_zweizeilig(self):
"""Prüft, dass der Titel der Basemap-Legende zweizeilig ist, damit er die Mindestbreite nicht bestimmt (#96)."""
self.assertEqual(bll.LEGENDE_TITEL.splitlines(), ["Kartengrundlage", "(basemap.de)"])
def test_nie_breiter_als_der_seitenrand_mit_hinweis(self): def test_nie_breiter_als_der_seitenrand_mit_hinweis(self):
"""Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt.""" """Prüft, dass keine Spaltenzahl gewählt wird, die über den Seitenrand ragt, und dann ein Hinweis kommt."""
legende, hinweis = self._platziere(self._layout(breite=600.0), legende, hinweis = self._platziere(self._layout(breite=600.0),
+63 -1
View File
@@ -96,10 +96,15 @@ class _Legende(_Element):
def __init__(self, x, y, groessen, unten_verankert=False): def __init__(self, x, y, groessen, unten_verankert=False):
super().__init__(x, y, groessen[1][0], groessen[1][1]) super().__init__(x, y, groessen[1][0], groessen[1][1])
self.groessen, self.spalten, self.unten = groessen, 1, unten_verankert self.groessen, self.spalten, self.unten = groessen, 1, unten_verankert
self.gleiche_breite, self.umbruch = True, 0
self.mindestbreite = None
def setColumnCount(self, n): self.spalten = n def setColumnCount(self, n): self.spalten = n
def setSplitLayer(self, wert): pass def setSplitLayer(self, wert): pass
def setEqualColumnWidth(self, wert): pass def setEqualColumnWidth(self, wert): self.gleiche_breite = wert
def setAutoWrapLinesAfter(self, mm): self.umbruch = mm
def setMinimumSize(self, groesse):
self.mindestbreite = groesse.width() if callable(groesse.width) else groesse.width
def setLocked(self, wert): pass def setLocked(self, wert): pass
def adjustBoxSize(self): def adjustBoxSize(self):
self.b, self.h = self.groessen[self.spalten] self.b, self.h = self.groessen[self.spalten]
@@ -119,6 +124,10 @@ class _Seite:
class _Layout: class _Layout:
def __init__(self, elemente, seite): def __init__(self, elemente, seite):
self.elemente, self.seite = elemente, seite self.elemente, self.seite = elemente, seite
self.eigenschaften = {}
def setCustomProperty(self, k, v): self.eigenschaften[k] = v
def customProperty(self, k, standard=None): return self.eigenschaften.get(k, standard)
def itemById(self, element_id): return self.elemente.get(element_id) def itemById(self, element_id): return self.elemente.get(element_id)
def pageCollection(self): return type("P", (), {"page": lambda s, i: self.seite})() def pageCollection(self): return type("P", (), {"page": lambda s, i: self.seite})()
@@ -151,6 +160,21 @@ class TestOrdneKartenrandAn(unittest.TestCase):
self.assertEqual(elemente["Hauptkarte"].verschoben, []) self.assertEqual(elemente["Hauptkarte"].verschoben, [])
self.assertEqual(layout.seite.b, 180 + 450 + kr.RAND_MM) 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_legenden_mindestens_so_breit_wie_kartenschild(self):
"""Prüft, dass die Legenden die Breite des Kartenschilds als Mindestbreite bekommen und das Layout sie für das Neuladen vermerkt."""
layout, elemente = self._layout({1: (60, 300), 2: (120, 160), 3: (175, 110)})
ordne_kartenrand_an(layout, rendern=lambda *a: None)
self.assertEqual(elemente["Legende"].mindestbreite, kr.MIN_SPALTENBREITE_MM)
self.assertEqual(kr.MIN_SPALTENBREITE_MM, 180.0) # Breite von Kartenschild und Quellenangabe
self.assertEqual(float(layout.customProperty(kr.LAYOUT_MINDESTBREITE)), kr.MIN_SPALTENBREITE_MM)
def test_passt_nichts_wird_verschoben(self): def test_passt_nichts_wird_verschoben(self):
"""Prüft, dass bei ausreichend hoher Karte weder Seite noch Kartenschild verändert werden.""" """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)}) layout, elemente = self._layout({1: (100, 10), 2: (200, 6), 3: (300, 4)})
@@ -165,6 +189,44 @@ class TestOrdneKartenrandAn(unittest.TestCase):
self.assertIsNone(ordne_kartenrand_an(_Layout({}, _Seite(100, 100)), rendern=lambda *a: None)) self.assertIsNone(ordne_kartenrand_an(_Layout({}, _Seite(100, 100)), rendern=lambda *a: None))
class TestMindestbreiteBeimLaden(unittest.TestCase):
def _projekt(self, *layouts):
verwaltung = type("V", (), {"printLayouts": lambda s: list(layouts)})()
return type("P", (), {"layoutManager": lambda s: verwaltung})()
def test_mindestbreite_nur_fuer_vermerkte_layouts(self):
"""Prüft, dass beim Laden die Mindestbreite nur an Layouts mit Vermerk wieder gesetzt wird."""
mit = _Layout({"Legende": _Legende(0, 0, {1: (60, 10)}), "Legende Basemap": _Legende(0, 0, {1: (90, 10)})}, _Seite(1, 1))
mit.setCustomProperty(kr.LAYOUT_MINDESTBREITE, "180.0")
ohne = _Layout({"Legende": _Legende(0, 0, {1: (60, 10)})}, _Seite(1, 1))
self.assertEqual(kr.wende_mindestbreiten_an(self._projekt(mit, ohne)), 1)
self.assertEqual(mit.elemente["Legende"].mindestbreite, 180.0)
self.assertEqual(mit.elemente["Legende Basemap"].mindestbreite, 180.0)
self.assertIsNone(ohne.elemente["Legende"].mindestbreite)
def test_listener_verbinden_und_trennen(self):
"""Prüft, dass der Listener readProject einmal verbindet, beim Trennen wieder löst und beides mehrfach aufgerufen werden darf."""
signal = MagicMock()
projekt = MagicMock()
projekt.readProject = signal
projekt.layoutManager.return_value.printLayouts.return_value = []
with patch.object(kr, "QGIS_AVAILABLE", True), patch.object(kr, "QgsProject") as qgs_projekt:
qgs_projekt.instance.return_value = projekt
kr.setup_mindestbreiten_listener()
kr.setup_mindestbreiten_listener()
self.assertEqual(signal.connect.call_count, 1)
kr.teardown_mindestbreiten_listener()
kr.teardown_mindestbreiten_listener()
self.assertEqual(signal.disconnect.call_count, 1)
def test_ohne_qgis_kein_listener(self):
"""Prüft, dass im Mock-Modus kein Listener verbunden wird."""
with patch.object(kr, "QGIS_AVAILABLE", False), patch.object(kr, "QgsProject") as qgs_projekt:
kr.setup_mindestbreiten_listener()
qgs_projekt.instance.assert_not_called()
class TestAnbindungPrintLayout(unittest.TestCase): class TestAnbindungPrintLayout(unittest.TestCase):
def test_vorlage_ordnet_kartenrand_an(self): def test_vorlage_ordnet_kartenrand_an(self):