2026-10-06 12:44:45 +02:00
|
|
|
|
"""sn_verfahrensgebiet/ui/tabs/eigentuemer_tab.py – Tab „Eigentümer“ (#56).
|
|
|
|
|
|
|
2026-10-06 13:04:16 +02:00
|
|
|
|
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`.
|
2026-10-06 12:44:45 +02:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from sn_basis.functions.qt_wrapper import (
|
2026-10-06 13:04:16 +02:00
|
|
|
|
QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QComboBox, Qt,
|
2026-10-06 12:44:45 +02:00
|
|
|
|
)
|
|
|
|
|
|
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,
|
|
|
|
|
|
)
|
2026-10-06 13:04:16 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.eigentuemer_suche import (
|
|
|
|
|
|
eigentuemerlayer_im_projekt,
|
|
|
|
|
|
hole_layer,
|
|
|
|
|
|
suche_eigentuemer,
|
|
|
|
|
|
EigentuemerSucheFehler,
|
|
|
|
|
|
)
|
|
|
|
|
|
from sn_verfahrensgebiet.ui.eigentuemer_treffer import EigentuemerTrefferFenster
|
2026-10-06 12:44:45 +02:00
|
|
|
|
|
|
|
|
|
|
_TITEL_SUCHE = "Flurstückssuche"
|
2026-10-06 13:04:16 +02:00
|
|
|
|
_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()
|
2026-10-06 12:44:45 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EigentuemerTab(QWidget):
|
2026-10-06 13:04:16 +02:00
|
|
|
|
"""Tab für Flurstückssuche und Eigentümersuche.
|
2026-10-06 12:44:45 +02:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
2026-10-06 13:04:16 +02:00
|
|
|
|
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()
|
|
|
|
|
|
|
2026-10-06 12:44:45 +02:00
|
|
|
|
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
|
2026-10-06 13:04:16 +02:00
|
|
|
|
|
|
|
|
|
|
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()
|