Files
Plugin_SN_Verfahrensgebiet/ui/tabs/eigentuemer_tab.py
T
DanielandClaude Opus 5.5 29719a8a99 Eigentümersuche mit Trefferliste im Tab „Eigentümer“ (#56)
Eigentümerdatei (CSV-Layer mit Nachname/Vorname) wählen, Name und/oder
Vorname eingeben: Eine nicht modale Trefferliste zeigt Name, Anschrift,
E-Mail und die Kennzeichen „verstorben“/„Rechtsperson“. Gleiche Personen
mit gleicher Anschrift werden zusammengefasst; Groß-/Kleinschreibung und
Umlautschreibweise spielen keine Rolle. Von QGIS als Zahl gelesene PLZ
bekommen ihre führende Null zurück. Meldungen enthalten keine Namen.

Die Auswahltabelle aus „Verfahrensgebiete aus Landkreis laden“ ist als
baue_auswahltabelle herausgelöst und wird von der Trefferliste mitgenutzt.

Zweiter Schritt von #56; Anzeigefenster und Korrekturen folgen.

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

212 lines
8.2 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_verfahrensgebiet/ui/tabs/eigentuemer_tab.py – Tab „Eigentümer“ (#56).
Zwei Bereiche:
- **Flurstückssuche:** Gemarkung (Name oder Nummer) und Flurstücksnummer
eingeben, „Suchen“ wählt die Treffer im Layer „Flurstücke“ aus und zoomt hin.
Logik: :mod:`sn_verfahrensgebiet.functions.flurstueck_suche`.
- **Eigentümersuche:** Eigentümerdatei (CSV-Layer) wählen, Name und/oder Vorname
eingeben, „Suchen“ öffnet die Trefferliste.
Logik: :mod:`sn_verfahrensgebiet.functions.eigentuemer_suche`.
"""
from sn_basis.functions.qt_wrapper import (
QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QComboBox, Qt,
)
from sn_basis.functions.qgisui_wrapper import iface
from sn_basis.functions.dialog_wrapper import show_info_dialog
from sn_basis.functions.message_wrapper import success, info, warning
from sn_verfahrensgebiet.functions.flurstueck_suche import (
flurstueck_suchen,
FlurstueckSucheFehler,
KeinFlurstueckslayer,
)
from sn_verfahrensgebiet.functions.eigentuemer_suche import (
eigentuemerlayer_im_projekt,
hole_layer,
suche_eigentuemer,
EigentuemerSucheFehler,
)
from sn_verfahrensgebiet.ui.eigentuemer_treffer import EigentuemerTrefferFenster
_TITEL_SUCHE = "Flurstückssuche"
_TITEL_EIGENTUEMER = "Eigentümersuche"
class EigentuemerLayerAuswahl(QComboBox):
"""Auswahl der Eigentümerdatei; zeigt nur Layer mit den Spalten Nachname und Vorname.
Die Liste wird beim Aufklappen neu aufgebaut, damit eine gerade geladene CSV
ohne Projektsignale erscheint.
"""
def aktualisieren(self) -> None:
"""Baut die Liste neu auf und behält die bisherige Auswahl, solange es den Layer noch gibt."""
bisher = self.currentData()
self.clear()
for layer in eigentuemerlayer_im_projekt():
self.addItem(layer.name(), layer.id())
index = self.findData(bisher) if bisher else -1
self.setCurrentIndex(index if index >= 0 else 0)
def showPopup(self) -> None:
self.aktualisieren()
super().showPopup()
class EigentuemerTab(QWidget):
"""Tab für Flurstückssuche und Eigentümersuche.
Attributes:
tab_title: Anzeigetitel des Tabs ("Eigentümer")
"""
tab_title = "Eigentümer"
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
layout.setAlignment(Qt.AlignmentFlag.AlignTop)
layout.addWidget(QLabel("<b>Flurstückssuche</b>"))
raster = QGridLayout()
raster.addWidget(QLabel("Gemarkung:"), 0, 0)
self.gemarkung_edit = QLineEdit()
self.gemarkung_edit.setPlaceholderText("Name oder Nummer, z. B. 0268")
raster.addWidget(self.gemarkung_edit, 0, 1)
raster.addWidget(QLabel("Flurstück:"), 1, 0)
self.flurstueck_edit = QLineEdit()
self.flurstueck_edit.setPlaceholderText("z. B. 562 oder 12/3")
raster.addWidget(self.flurstueck_edit, 1, 1)
layout.addLayout(raster)
zeile_suchen = QHBoxLayout()
zeile_suchen.addStretch()
self.btn_flurstueck_suchen = QPushButton("Suchen")
self.btn_flurstueck_suchen.setToolTip(
"Sucht im Layer „Flurstücke“. Ohne Nenner werden alle Flurstücke "
"mit diesem Zähler gefunden."
)
zeile_suchen.addWidget(self.btn_flurstueck_suchen)
layout.addLayout(zeile_suchen)
self.btn_flurstueck_suchen.clicked.connect(self._on_flurstueck_suchen)
self.gemarkung_edit.returnPressed.connect(self._on_flurstueck_suchen)
self.flurstueck_edit.returnPressed.connect(self._on_flurstueck_suchen)
titel_eigentuemer = QLabel("<b>Eigentümersuche</b>")
titel_eigentuemer.setStyleSheet("margin-top: 12px;")
layout.addWidget(titel_eigentuemer)
raster_eig = QGridLayout()
raster_eig.addWidget(QLabel("Eigentümerdatei:"), 0, 0)
self.eigentuemer_layer_combo = EigentuemerLayerAuswahl()
self.eigentuemer_layer_combo.setToolTip(
"CSV-Layer mit den Spalten Nachname und Vorname (Serienbriefexport). "
"Die CSV von Hand ins Projekt laden; die Liste wird beim Aufklappen aktualisiert."
)
raster_eig.addWidget(self.eigentuemer_layer_combo, 0, 1)
raster_eig.addWidget(QLabel("Name:"), 1, 0)
self.nachname_edit = QLineEdit()
raster_eig.addWidget(self.nachname_edit, 1, 1)
raster_eig.addWidget(QLabel("Vorname:"), 2, 0)
self.vorname_edit = QLineEdit()
raster_eig.addWidget(self.vorname_edit, 2, 1)
layout.addLayout(raster_eig)
zeile_eig_suchen = QHBoxLayout()
zeile_eig_suchen.addStretch()
self.btn_eigentuemer_suchen = QPushButton("Suchen")
self.btn_eigentuemer_suchen.setToolTip(
"Findet Einträge, deren Name und Vorname die Eingaben enthalten. "
"Gleiche Personen mit gleicher Anschrift werden zusammengefasst."
)
zeile_eig_suchen.addWidget(self.btn_eigentuemer_suchen)
layout.addLayout(zeile_eig_suchen)
self.btn_eigentuemer_suchen.clicked.connect(self._on_eigentuemer_suchen)
self.nachname_edit.returnPressed.connect(self._on_eigentuemer_suchen)
self.vorname_edit.returnPressed.connect(self._on_eigentuemer_suchen)
self._trefferfenster = None
self.eigentuemer_layer_combo.aktualisieren()
self.setLayout(layout)
def _on_flurstueck_suchen(self) -> None:
"""Sucht das eingegebene Flurstück, wählt die Treffer aus und zoomt hin."""
gemarkung = self.gemarkung_edit.text().strip()
nummer = self.flurstueck_edit.text().strip()
try:
ergebnis = flurstueck_suchen(gemarkung, nummer)
except KeinFlurstueckslayer as exc:
show_info_dialog(_TITEL_SUCHE, str(exc), self)
return
except FlurstueckSucheFehler as exc:
warning(_TITEL_SUCHE, str(exc))
return
if not ergebnis.feature_ids:
info(_TITEL_SUCHE, f"Flurstück {nummer} in Gemarkung {gemarkung} nicht gefunden.")
return
ergebnis.layer.selectByIds(ergebnis.feature_ids)
self._zoome_auf_auswahl(ergebnis.layer, ergebnis.feature_ids)
anzahl = len(ergebnis.feature_ids)
text = "1 Flurstück" if anzahl == 1 else f"{anzahl} Flurstücke"
success(_TITEL_SUCHE, f"{text} im Layer „{ergebnis.layer.name()}“ ausgewählt.")
@staticmethod
def _zoome_auf_auswahl(layer, feature_ids) -> bool:
"""Zoomt auf die Auswahl und lässt die Treffer kurz aufblinken.
``zoomToSelected`` rechnet selbst in das KBS der Kartenansicht um.
Gibt ``False`` zurück, wenn keine Kartenansicht verfügbar ist.
"""
try:
canvas = iface.mapCanvas()
canvas.zoomToSelected(layer)
canvas.flashFeatureIds(layer, feature_ids)
return True
except Exception:
return False
def _on_eigentuemer_suchen(self) -> None:
"""Sucht in der gewählten Eigentümerdatei und zeigt die Trefferliste.
Meldungen enthalten bewusst keine Namen (Datenschutz).
"""
layer = hole_layer(self.eigentuemer_layer_combo.currentData())
if layer is None:
self.eigentuemer_layer_combo.aktualisieren()
layer = hole_layer(self.eigentuemer_layer_combo.currentData())
if layer is None:
warning(
_TITEL_EIGENTUEMER,
"Keine Eigentümerdatei gewählt. Bitte den Serienbriefexport (CSV) "
"als Layer laden und hier auswählen.",
)
return
try:
treffer = suche_eigentuemer(layer, self.nachname_edit.text(), self.vorname_edit.text())
except EigentuemerSucheFehler as exc:
warning(_TITEL_EIGENTUEMER, str(exc))
return
if not treffer:
info(_TITEL_EIGENTUEMER, "Keine passenden Einträge gefunden.")
return
self._zeige_treffer(treffer)
def _zeige_treffer(self, treffer) -> None:
"""Ersetzt eine offene Trefferliste durch die neue."""
alt = getattr(self, "_trefferfenster", None)
if alt is not None:
alt.close()
self._trefferfenster = EigentuemerTrefferFenster(treffer, self)
self._trefferfenster.show()