Files
Plugin_SN_Basis/ui/widgets/basemap_bereich.py
T
DanielandClaude Opus 5.5 f9241a11f9 Basemap-Bereich im Druck-Tab mit lokalen Stilvorlagen (#88)
Einklappbarer Bereich „Basemap“: Auswertebereich (Verfahrensgebiet/Druckbereich),
Basemap-Legende beim Erstellen an/aus, „Basemap-Legende aktualisieren“ für
bestehende Vorlagen und Stilvorlagen des Basemap-Layers. Stilvorlagen liegen lokal im
QGIS-Profil (QML plus Begleitdatei mit Legendenauswahl), mehrere Varianten, eine als
Standard markierbar; geladen wird nur auf Knopfdruck nach Rückfrage, fremde Varianten
(andere Style-Namen) erst nach zweiter Bestätigung.

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

272 lines
11 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.
"""
sn_basis/ui/widgets/basemap_bereich.py – einklappbarer Bereich „Basemap“ im Druck-Tab
Einstellungen der Basemap-Legende (Auswertebereich, Erzeugen beim Anlegen der Vorlage),
„Basemap-Legende aktualisieren“ für bestehende Vorlagen und lokale Stilvorlagen des
Basemap-Layers (laden, speichern, als Standard markieren, löschen). Basis #88.
Die Handler enthalten nur Ablauf und Rückfragen; die Logik liegt in
``modules/basemap_legende`` und ``modules/basemap_stilvorlagen``.
"""
from __future__ import annotations
from typing import Any, Optional
from sn_basis.functions.dialog_wrapper import ask_text, ask_yes_no
from sn_basis.functions.message_wrapper import info, success, warning
from sn_basis.functions.qgiscore_wrapper import QgsProject
from sn_basis.functions.qt_wrapper import (
ArrowDown,
ArrowRight,
QCheckBox,
QComboBox,
QHBoxLayout,
QLabel,
QPushButton,
QToolButton,
QVBoxLayout,
QWidget,
ToolButtonTextBesideIcon,
)
from sn_basis.modules import basemap_legende as legende
from sn_basis.modules.basemap_legende_layout import layouts_mit_basemap_legende
from sn_basis.modules.basemap_stilvorlagen import BasemapStilvorlagen
TITEL = "Basemap"
KEIN_LAYER_TEXT = "Kein Basemap-Layer im Projekt"
class BasemapBereich(QWidget):
"""Einklappbarer Basemap-Bereich; standardmäßig eingeklappt."""
def __init__(self, parent: Optional[Any] = None, project: Any | None = None,
stilvorlagen: BasemapStilvorlagen | None = None) -> None:
super().__init__(parent)
self._project = project
self._stilvorlagen = stilvorlagen
self._build_ui()
self.aktualisieren()
# --- Aufbau ---------------------------------------------------------
def _build_ui(self) -> None:
"""von Test abgesehen, weil reiner Widget-Aufbau; im QGIS-Test geprüft"""
haupt = QVBoxLayout()
haupt.setSpacing(4)
haupt.setContentsMargins(0, 0, 0, 0)
knopf = QToolButton()
knopf.setText(TITEL)
knopf.setCheckable(True)
knopf.setChecked(False)
knopf.setToolButtonStyle(ToolButtonTextBesideIcon)
knopf.setArrowType(ArrowRight)
knopf.setStyleSheet("font-weight: bold; margin-top: 6px;")
knopf.toggled.connect(self._umschalten)
self._knopf = knopf
haupt.addWidget(knopf)
inhalt = QWidget()
spalte = QVBoxLayout()
spalte.setSpacing(4)
spalte.setContentsMargins(10, 4, 4, 4)
self._status = QLabel(KEIN_LAYER_TEXT)
self._status.setWordWrap(True)
spalte.addWidget(self._status)
spalte.addWidget(self._ueberschrift("Legende"))
spalte.addWidget(QLabel("Auswertebereich"))
self._bereich_combo = QComboBox(self)
for schluessel, text in legende.AUSWERTEBEREICHE.items():
self._bereich_combo.addItem(text, schluessel)
self._bereich_combo.currentIndexChanged.connect(self._on_bereich_geaendert)
spalte.addWidget(self._bereich_combo)
self._erzeugen_cb = QCheckBox("Basemap-Legende beim Erstellen der Vorlage erzeugen (§ 38, § 41)", self)
self._erzeugen_cb.toggled.connect(self._on_erzeugen_geaendert)
spalte.addWidget(self._erzeugen_cb)
spalte.addWidget(QLabel("Vorlage"))
self._layout_combo = QComboBox(self)
spalte.addWidget(self._layout_combo)
self._aktualisieren_btn = QPushButton("Basemap-Legende aktualisieren", self)
self._aktualisieren_btn.clicked.connect(self._on_legende_aktualisieren)
spalte.addWidget(self._aktualisieren_btn)
spalte.addWidget(self._ueberschrift("Stilvorlage"))
self._stil_combo = QComboBox(self)
spalte.addWidget(self._stil_combo)
zeile = QHBoxLayout()
for text, handler in (
("Laden", self._on_stil_laden),
("Speichern unter …", self._on_stil_speichern),
("Als Standard", self._on_stil_standard),
("Löschen", self._on_stil_loeschen),
):
knopf_stil = QPushButton(text, self)
knopf_stil.clicked.connect(lambda *_, h=handler: h())
zeile.addWidget(knopf_stil)
spalte.addLayout(zeile)
inhalt.setLayout(spalte)
inhalt.setVisible(False)
self._inhalt = inhalt
haupt.addWidget(inhalt)
self.setLayout(haupt)
@staticmethod
def _ueberschrift(text: str) -> Any:
beschriftung = QLabel(text)
beschriftung.setStyleSheet("font-weight: bold; margin-top: 4px;")
return beschriftung
# --- Zustand --------------------------------------------------------
def _projekt(self) -> Any:
return getattr(self, "_project", None) or QgsProject.instance()
def _vorlagen(self) -> BasemapStilvorlagen:
if getattr(self, "_stilvorlagen", None) is None:
self._stilvorlagen = BasemapStilvorlagen()
return self._stilvorlagen
def basemap_layer(self) -> Any | None:
"""Erster Basemap-Layer des Projekts (sichtbar oder nicht)."""
try:
layer = list(self._projekt().mapLayers().values())
except Exception:
return None
gefunden = legende.finde_basemap_layer(layer)
return gefunden[0] if gefunden else None
def _umschalten(self, offen: bool) -> None:
self._knopf.setArrowType(ArrowDown if offen else ArrowRight)
self._inhalt.setVisible(offen)
if offen:
self.aktualisieren()
def aktualisieren(self) -> None:
"""Liest Projektvariablen, Layouts und Stilvorlagen neu ein (z. B. nach Projektwechsel)."""
layer = self.basemap_layer()
self._status.setText(f"Basemap-Layer: {layer.name()}" if layer is not None else KEIN_LAYER_TEXT)
bereich = legende.lies_auswertebereich()
self._bereich_combo.blockSignals(True)
self._bereich_combo.setCurrentIndex(max(0, self._bereich_combo.findData(bereich)))
self._bereich_combo.blockSignals(False)
self._erzeugen_cb.blockSignals(True)
self._erzeugen_cb.setChecked(legende.legende_erzeugen_aktiv())
self._erzeugen_cb.blockSignals(False)
self._layout_combo.clear()
self._layout_combo.addItems(layouts_mit_basemap_legende(self._projekt()))
self._aktualisieren_btn.setEnabled(layer is not None and self._layout_combo.count() > 0)
self._stil_combo.clear()
for vorlage in self._vorlagen().liste():
self._stil_combo.addItem(f"{vorlage.anzeigename}{' ★' if vorlage.standard else ''}", vorlage.name)
standard = self._vorlagen().standard()
if standard:
self._stil_combo.setCurrentIndex(max(0, self._stil_combo.findData(standard)))
# --- Handler: Legende -----------------------------------------------
def _on_bereich_geaendert(self, *_: Any) -> None:
bereich = self._bereich_combo.currentData()
if bereich in legende.AUSWERTEBEREICHE:
legende.setze_auswertebereich(bereich)
def _on_erzeugen_geaendert(self, aktiv: bool) -> None:
legende.setze_legende_erzeugen(bool(aktiv))
def _on_legende_aktualisieren(self) -> None:
name = self._layout_combo.currentText()
layout = self._projekt().layoutManager().layoutByName(name) if name else None
if layout is None:
warning(TITEL, "Bitte eine Vorlage mit Basemap-Legende wählen.")
return
try:
ergebnis = legende.BasemapLegende(self._projekt()).aktualisiere(layout)
except Exception as fehler:
warning(TITEL, f"Die Basemap-Legende konnte nicht aktualisiert werden: {fehler}")
return
if ergebnis["status"] == "ok":
success(TITEL, ergebnis["meldung"])
elif ergebnis["status"] != "abgebrochen":
info(TITEL, ergebnis["meldung"])
# --- Handler: Stilvorlagen ------------------------------------------
def _gewaehlte_vorlage(self) -> str:
return str(self._stil_combo.currentData() or "")
def _on_stil_laden(self) -> None:
layer = self.basemap_layer()
name = self._gewaehlte_vorlage()
if layer is None or not name:
warning(TITEL, "Kein Basemap-Layer oder keine Stilvorlage gewählt.")
return
if not ask_yes_no("Stilvorlage laden",
f"Darstellung von „{layer.name()}“ durch die Stilvorlage ersetzen?", default=False):
return
unbekannt = self._vorlagen().pruefe_passung(layer, name)
if unbekannt and not ask_yes_no(
"Stilvorlage laden",
f"{len(unbekannt)} Styles der Vorlage gibt es in diesem Layer nicht – die Vorlage stammt "
"vermutlich von einer anderen Basemap-Variante. Trotzdem laden?",
default=False,
):
return
try:
self._vorlagen().laden(layer, name)
except ValueError as fehler:
warning(TITEL, str(fehler))
return
auswahl = self._vorlagen().legendenauswahl(name)
if auswahl is not None:
import json
from sn_basis.functions.variable_wrapper import set_variable
set_variable(legende.AUSWAHL_VAR, json.dumps(auswahl), scope="project")
success(TITEL, "Stilvorlage geladen.")
def _on_stil_speichern(self) -> None:
layer = self.basemap_layer()
if layer is None:
warning(TITEL, KEIN_LAYER_TEXT)
return
anzeigename, ok = ask_text("Stilvorlage speichern", "Name der Stilvorlage:",
default_text=self._stil_combo.currentText().replace(" ★", ""))
if not ok or not anzeigename.strip():
return
if self._vorlagen().vorhanden(anzeigename) and not ask_yes_no(
"Stilvorlage speichern", f"Die Stilvorlage „{anzeigename}“ gibt es schon. Ersetzen?", default=False
):
return
try:
self._vorlagen().speichern(layer, anzeigename, legende.lies_auswahl())
except ValueError as fehler:
warning(TITEL, str(fehler))
return
self.aktualisieren()
success(TITEL, f"Stilvorlage „{anzeigename.strip()}“ gespeichert.")
def _on_stil_standard(self) -> None:
name = self._gewaehlte_vorlage()
if not name:
return
try:
self._vorlagen().setze_standard(name)
except ValueError as fehler:
warning(TITEL, str(fehler))
return
self.aktualisieren()
def _on_stil_loeschen(self) -> None:
name = self._gewaehlte_vorlage()
if not name or not ask_yes_no("Stilvorlage löschen",
f"Stilvorlage „{self._stil_combo.currentText()}“ löschen?", default=False):
return
self._vorlagen().loeschen(name)
self.aktualisieren()