Basemap-Legende: Auswertung und Legendenelement im Drucklayout (#88)

Beim Erstellen einer §38-/§41-Vorlage wertet BasemapLegende die Kacheln des sichtbaren
basemap.de-Layers im Verfahrensgebiet (wahlweise Druckbereich) kachelweise über
selectByGeometry aus, prüft die Renderer-Styles mit derselben Zoomlogik wie QGIS und
bildet daraus Legendeneinträge. Ein Mehrfachauswahl-Dialog (neu: ask_mehrfachauswahl)
lässt Kategorien abwählen; die Auswahl wird im Projekt gemerkt. Die gewählten Einträge
stehen mit der Signatur des Projektlayers (zoomabhängige Werte festgeschrieben) in
einem eigenen Legendenelement über der Quellenangabe. Ein eigenes Element ist nötig,
weil die nach Karteninhalt filternde Hauptlegende die Hilfslayer ausblenden würde.

Wrapper um die nötigen QGIS-/Qt-Symbole mit Mock ergänzt (eigener Block in
qgiscore_wrapper; QgsRectangle hatte bisher keinen Mock).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 11:57:09 +02:00
co-authored by Claude Opus 5.5
parent 7408740dfe
commit b549cb8a5a
11 changed files with 2223 additions and 0 deletions
+110
View File
@@ -409,3 +409,113 @@ def create_progress_dialog(total: int, title: str = "Fortschritt", label: str =
"""von Test abgesehen, weil vernachlässigbar; HD 18.09.2026"""
return ProgressDialog(total, title, label)
def ask_mehrfachauswahl(
titel: str,
hinweis: str,
eintraege: list[dict],
vorauswahl: set[str] | list[str],
parent: Any = None,
) -> Optional[list[str]]:
"""Zeigt eine Liste mit Häkchen, in der Einträge an- und abgewählt werden.
Parameters
----------
eintraege :
Dicts mit ``schluessel`` und ``text``, optional ``gruppe`` (Zwischenüberschrift; Einträge
einer Gruppe müssen aufeinander folgen) und ``icon`` (z. B. Symbolvorschau).
vorauswahl :
Schlüssel der anfangs angehakten Einträge.
Returns
-------
list[str] | None
Schlüssel der angehakten Einträge in Listenreihenfolge, ``None`` bei Abbruch.
Im Mock-Modus die Vorauswahl.
"""
vorauswahl = set(vorauswahl)
if QT_VERSION == 0: # Mock-Modus
return [e["schluessel"] for e in eintraege if e["schluessel"] in vorauswahl]
from sn_basis.functions.qt_wrapper import (
QDialog, QDialogButtonBox, QFont, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPushButton,
QVBoxLayout, DialogAccepted, ItemIsEnabled, ItemIsUserCheckable, CheckStateChecked,
CheckStateUnchecked, UserRole,
)
dialog = QDialog(parent)
dialog.setWindowTitle(titel)
layout = QVBoxLayout(dialog)
beschriftung = QLabel(hinweis)
beschriftung.setWordWrap(True)
layout.addWidget(beschriftung)
suche = QLineEdit()
suche.setPlaceholderText("Suche …")
layout.addWidget(suche)
liste = QListWidget()
kopf_schrift = QFont()
kopf_schrift.setBold(True)
zeilen: list[tuple[Any, dict]] = []
koepfe: dict[str, Any] = {}
letzte_gruppe = None
for eintrag in eintraege:
gruppe = eintrag.get("gruppe")
if gruppe and gruppe != letzte_gruppe:
kopf = QListWidgetItem(gruppe)
kopf.setFlags(ItemIsEnabled)
kopf.setFont(kopf_schrift)
liste.addItem(kopf)
koepfe[gruppe] = kopf
letzte_gruppe = gruppe
zeile = QListWidgetItem(eintrag["text"])
if eintrag.get("icon") is not None:
zeile.setIcon(eintrag["icon"])
zeile.setFlags(ItemIsUserCheckable | ItemIsEnabled)
zeile.setCheckState(CheckStateChecked if eintrag["schluessel"] in vorauswahl else CheckStateUnchecked)
zeile.setData(UserRole, eintrag["schluessel"])
liste.addItem(zeile)
zeilen.append((zeile, eintrag))
layout.addWidget(liste, 1)
def _filtern(text: str) -> None:
suchtext = text.strip().lower()
sichtbare_gruppen = set()
for zeile, eintrag in zeilen:
treffer = not suchtext or suchtext in eintrag["text"].lower() \
or suchtext in (eintrag.get("gruppe") or "").lower()
zeile.setHidden(not treffer)
if treffer:
sichtbare_gruppen.add(eintrag.get("gruppe"))
for gruppe, kopf in koepfe.items():
kopf.setHidden(gruppe not in sichtbare_gruppen)
def _alle_setzen(zustand: Any) -> None:
for zeile, _ in zeilen:
if not zeile.isHidden():
zeile.setCheckState(zustand)
suche.textChanged.connect(_filtern)
knopfzeile = QHBoxLayout()
alle = QPushButton("Alle auswählen")
alle.clicked.connect(lambda *_: _alle_setzen(CheckStateChecked))
keine = QPushButton("Keine auswählen")
keine.clicked.connect(lambda *_: _alle_setzen(CheckStateUnchecked))
knopfzeile.addWidget(alle)
knopfzeile.addWidget(keine)
knopfzeile.addStretch(1)
layout.addLayout(knopfzeile)
knoepfe = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
knoepfe.accepted.connect(dialog.accept)
knoepfe.rejected.connect(dialog.reject)
layout.addWidget(knoepfe)
dialog.resize(460, 560)
if exec_dialog(dialog) != DialogAccepted:
return None
return [eintrag["schluessel"] for zeile, eintrag in zeilen if zeile.checkState() == CheckStateChecked]