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>
212 lines
8.2 KiB
Python
212 lines
8.2 KiB
Python
"""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()
|