Anzeige für den Eigentümer und Korrekturen im Tab „Eigentümer“ (#56)
„Daten anzeigen“ in der Trefferliste zeigt Name, Anschrift und E-Mail der gewählten Einträge in einem eigenen Fenster: auf einem zweiten Bildschirm im Vollbild, auf dem QGIS-Bildschirm als normales Fenster. Die Sitzung wird von Hand begonnen und beendet; „Anzeige leeren“ entfernt die Daten, ohne das Fenster zu schließen. Konten, IDs und Kennzeichen erscheinen dort nicht, alle Werte werden maskiert. „Korrigieren …“ ändert angezeigte Daten. Die Eigentümerdatei bleibt unverändert; jede Änderung wird mit Zeitpunkt, Konto, Personen-ID, altem und neuem Wert an <Eigentümerdatei>_korrekturen.csv angehängt und sofort angezeigt. Ohne beschreibbaren Ordner wird nach dem Speicherort gefragt. In test_eigentuemer_tab.py sind die beim letzten Mal zusammengezogenen with-Zeilen wieder umbrochen; die Trefferliste bekommt jetzt den Anzeige-Rückruf, der Test dazu ist angepasst. Dritter und letzter Schritt von #56. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+202
-2
@@ -8,10 +8,19 @@ Zwei Bereiche:
|
||||
- **Eigentümersuche:** Eigentümerdatei (CSV-Layer) wählen, Name und/oder Vorname
|
||||
eingeben, „Suchen“ öffnet die Trefferliste.
|
||||
Logik: :mod:`sn_verfahrensgebiet.functions.eigentuemer_suche`.
|
||||
- **Anzeige für den Eigentümer:** Bildschirm wählen, Sitzung beginnen und
|
||||
beenden, Anzeige leeren, angezeigte Daten korrigieren. „Daten anzeigen“ in
|
||||
der Trefferliste füllt das Anzeigefenster (und beginnt bei Bedarf die
|
||||
Sitzung). Korrekturen landen in einer Korrektur-CSV neben der
|
||||
Eigentümerdatei (:mod:`sn_verfahrensgebiet.functions.eigentuemer_korrektur`).
|
||||
"""
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from sn_basis.functions.qt_wrapper import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QComboBox, Qt,
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QComboBox,
|
||||
QFileDialog, Qt,
|
||||
)
|
||||
from sn_basis.functions.qgisui_wrapper import iface
|
||||
from sn_basis.functions.dialog_wrapper import show_info_dialog
|
||||
@@ -28,10 +37,24 @@ from sn_verfahrensgebiet.functions.eigentuemer_suche import (
|
||||
suche_eigentuemer,
|
||||
EigentuemerSucheFehler,
|
||||
)
|
||||
from sn_verfahrensgebiet.functions.eigentuemer_korrektur import (
|
||||
ermittle_aenderungen,
|
||||
korrektur_pfad,
|
||||
schreibe_korrekturen,
|
||||
wende_an,
|
||||
)
|
||||
from sn_verfahrensgebiet.ui.eigentuemer_treffer import EigentuemerTrefferFenster
|
||||
from sn_verfahrensgebiet.ui.eigentuemer_anzeige import (
|
||||
BildschirmAuswahl,
|
||||
EigentuemerAnzeige,
|
||||
qgis_bildschirm,
|
||||
zeige_auf_bildschirm,
|
||||
)
|
||||
from sn_verfahrensgebiet.ui.eigentuemer_korrektur_dialog import frage_korrektur
|
||||
|
||||
_TITEL_SUCHE = "Flurstückssuche"
|
||||
_TITEL_EIGENTUEMER = "Eigentümersuche"
|
||||
_TITEL_ANZEIGE = "Anzeige für den Eigentümer"
|
||||
|
||||
|
||||
class EigentuemerLayerAuswahl(QComboBox):
|
||||
@@ -131,8 +154,53 @@ class EigentuemerTab(QWidget):
|
||||
self.nachname_edit.returnPressed.connect(self._on_eigentuemer_suchen)
|
||||
self.vorname_edit.returnPressed.connect(self._on_eigentuemer_suchen)
|
||||
|
||||
titel_anzeige = QLabel(f"<b>{_TITEL_ANZEIGE}</b>")
|
||||
titel_anzeige.setStyleSheet("margin-top: 12px;")
|
||||
layout.addWidget(titel_anzeige)
|
||||
|
||||
zeile_bildschirm = QHBoxLayout()
|
||||
zeile_bildschirm.addWidget(QLabel("Bildschirm:"))
|
||||
self.bildschirm_combo = BildschirmAuswahl()
|
||||
self.bildschirm_combo.setToolTip(
|
||||
"Bildschirm, den der Eigentümer sieht. Auf einem anderen Bildschirm als QGIS "
|
||||
"öffnet die Anzeige im Vollbild."
|
||||
)
|
||||
zeile_bildschirm.addWidget(self.bildschirm_combo, 1)
|
||||
layout.addLayout(zeile_bildschirm)
|
||||
|
||||
zeile_sitzung = QHBoxLayout()
|
||||
self.btn_sitzung = QPushButton("Sitzung beginnen")
|
||||
self.btn_anzeige_leeren = QPushButton("Anzeige leeren")
|
||||
self.btn_anzeige_leeren.setToolTip(
|
||||
"Entfernt die Daten aus dem Anzeigefenster, ohne es zu schließen – "
|
||||
"vor dem nächsten Eigentümer."
|
||||
)
|
||||
self.btn_korrigieren = QPushButton("Korrigieren …")
|
||||
self.btn_korrigieren.setToolTip(
|
||||
"Angezeigte Daten korrigieren; die Änderung wird in der Korrektur-CSV festgehalten."
|
||||
)
|
||||
for knopf in (self.btn_sitzung, self.btn_anzeige_leeren, self.btn_korrigieren):
|
||||
zeile_sitzung.addWidget(knopf)
|
||||
layout.addLayout(zeile_sitzung)
|
||||
|
||||
self.sitzung_label = QLabel()
|
||||
self.sitzung_label.setWordWrap(True)
|
||||
layout.addWidget(self.sitzung_label)
|
||||
|
||||
self.btn_sitzung.clicked.connect(self._on_sitzung_umschalten)
|
||||
self.btn_anzeige_leeren.clicked.connect(self._on_anzeige_leeren)
|
||||
self.btn_korrigieren.clicked.connect(self._on_korrigieren)
|
||||
|
||||
self._trefferfenster = None
|
||||
self._treffer_layer_id = None
|
||||
self._anzeige = None
|
||||
self._sitzung_seit = None
|
||||
self._angezeigt = []
|
||||
self._anzeige_layer_id = None
|
||||
self._korrektur_pfade = {}
|
||||
self.eigentuemer_layer_combo.aktualisieren()
|
||||
self.bildschirm_combo.aktualisieren()
|
||||
self._aktualisiere_sitzungsstatus()
|
||||
|
||||
self.setLayout(layout)
|
||||
|
||||
@@ -200,6 +268,7 @@ class EigentuemerTab(QWidget):
|
||||
if not treffer:
|
||||
info(_TITEL_EIGENTUEMER, "Keine passenden Einträge gefunden.")
|
||||
return
|
||||
self._treffer_layer_id = layer.id()
|
||||
self._zeige_treffer(treffer)
|
||||
|
||||
def _zeige_treffer(self, treffer) -> None:
|
||||
@@ -207,5 +276,136 @@ class EigentuemerTab(QWidget):
|
||||
alt = getattr(self, "_trefferfenster", None)
|
||||
if alt is not None:
|
||||
alt.close()
|
||||
self._trefferfenster = EigentuemerTrefferFenster(treffer, self)
|
||||
self._trefferfenster = EigentuemerTrefferFenster(
|
||||
treffer, self, beim_anzeigen=self._daten_anzeigen
|
||||
)
|
||||
self._trefferfenster.show()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Anzeige für den Eigentümer
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _on_sitzung_umschalten(self) -> None:
|
||||
if self._anzeige is None:
|
||||
self._sitzung_beginnen()
|
||||
else:
|
||||
self._sitzung_beenden()
|
||||
|
||||
def _sitzung_beginnen(self) -> None:
|
||||
"""Öffnet das Anzeigefenster leer auf dem gewählten Bildschirm."""
|
||||
bildschirm = self.bildschirm_combo.gewaehlter_bildschirm()
|
||||
anzeige = EigentuemerAnzeige(self)
|
||||
anzeige.beim_schliessen = self._anzeige_wurde_geschlossen
|
||||
vollbild = bildschirm is not None and bildschirm is not qgis_bildschirm()
|
||||
zeige_auf_bildschirm(anzeige, bildschirm, vollbild)
|
||||
self._anzeige = anzeige
|
||||
self._sitzung_seit = datetime.now()
|
||||
self._angezeigt = []
|
||||
self._aktualisiere_sitzungsstatus()
|
||||
|
||||
def _sitzung_beenden(self) -> None:
|
||||
"""Schließt das Anzeigefenster; die Korrektur-CSV bleibt, wie sie ist."""
|
||||
anzeige = self._anzeige
|
||||
self._anzeige_wurde_geschlossen()
|
||||
if anzeige is not None:
|
||||
anzeige.beim_schliessen = None
|
||||
anzeige.close()
|
||||
|
||||
def _anzeige_wurde_geschlossen(self) -> None:
|
||||
self._anzeige = None
|
||||
self._sitzung_seit = None
|
||||
self._angezeigt = []
|
||||
self._aktualisiere_sitzungsstatus()
|
||||
|
||||
def _on_anzeige_leeren(self) -> None:
|
||||
"""Entfernt die Daten aus der Anzeige, das Fenster bleibt offen."""
|
||||
if self._anzeige is not None:
|
||||
self._anzeige.leeren()
|
||||
self._angezeigt = []
|
||||
self._aktualisiere_sitzungsstatus()
|
||||
|
||||
def _daten_anzeigen(self, eintraege) -> None:
|
||||
"""Rückruf der Trefferliste: zeigt die gewählten Einträge, beginnt bei Bedarf die Sitzung."""
|
||||
if not eintraege:
|
||||
warning(_TITEL_ANZEIGE, "Bitte in der Trefferliste mindestens einen Eintrag auswählen.")
|
||||
return
|
||||
if self._anzeige is None:
|
||||
self._sitzung_beginnen()
|
||||
self._angezeigt = list(eintraege)
|
||||
self._anzeige_layer_id = self._treffer_layer_id
|
||||
self._anzeige.zeige(self._angezeigt)
|
||||
self._aktualisiere_sitzungsstatus()
|
||||
|
||||
def _aktualisiere_sitzungsstatus(self) -> None:
|
||||
"""Knopftexte und Statuszeile; ohne Namen, damit der Tab nichts preisgibt."""
|
||||
aktiv = self._anzeige is not None
|
||||
self.btn_sitzung.setText("Sitzung beenden" if aktiv else "Sitzung beginnen")
|
||||
self.btn_anzeige_leeren.setEnabled(aktiv and bool(self._angezeigt))
|
||||
self.btn_korrigieren.setEnabled(aktiv and bool(self._angezeigt))
|
||||
if not aktiv:
|
||||
text = "Keine Sitzung. „Daten anzeigen“ in der Trefferliste beginnt sie automatisch."
|
||||
else:
|
||||
anzahl = len(self._angezeigt)
|
||||
inhalt = "leer" if not anzahl else ("1 Eintrag" if anzahl == 1 else f"{anzahl} Einträge")
|
||||
text = f"Sitzung seit {self._sitzung_seit:%H:%M} Uhr – Anzeige: {inhalt}"
|
||||
self.sitzung_label.setText(text)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Korrekturen
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def _on_korrigieren(self) -> None:
|
||||
"""Fragt Korrekturen ab, hält sie in der Korrektur-CSV fest und zeigt sie sofort an."""
|
||||
if not self._angezeigt:
|
||||
warning(_TITEL_ANZEIGE, "Es werden keine Daten angezeigt.")
|
||||
return
|
||||
ergebnis = frage_korrektur(self, self._angezeigt)
|
||||
if ergebnis is None:
|
||||
return
|
||||
index, werte = ergebnis
|
||||
eintrag = self._angezeigt[index]
|
||||
aenderungen = ermittle_aenderungen(eintrag, werte)
|
||||
if not aenderungen:
|
||||
info(_TITEL_ANZEIGE, "Keine Änderung.")
|
||||
return
|
||||
|
||||
pfad = self._korrektur_datei()
|
||||
if not pfad:
|
||||
return
|
||||
try:
|
||||
schreibe_korrekturen(pfad, eintrag, aenderungen)
|
||||
except OSError as exc:
|
||||
self._korrektur_pfade.pop(self._anzeige_layer_id, None)
|
||||
warning(_TITEL_ANZEIGE, f"Die Korrektur-CSV konnte nicht geschrieben werden: {exc.strerror or exc}")
|
||||
return
|
||||
|
||||
self._angezeigt[index] = wende_an(eintrag, aenderungen)
|
||||
if self._anzeige is not None:
|
||||
self._anzeige.zeige(self._angezeigt)
|
||||
anzahl = len(aenderungen)
|
||||
text = "1 Änderung" if anzahl == 1 else f"{anzahl} Änderungen"
|
||||
success(_TITEL_ANZEIGE, f"{text} in „{os.path.basename(pfad)}“ festgehalten.")
|
||||
|
||||
def _korrektur_datei(self):
|
||||
"""Pfad der Korrektur-CSV für die angezeigte Eigentümerdatei.
|
||||
|
||||
Vorgabe ist ``<Eigentümerdatei>_korrekturen.csv`` daneben. Lässt sich
|
||||
daraus kein beschreibbarer Ordner ableiten, wird gefragt. Die Wahl gilt
|
||||
für die Eigentümerdatei, solange der Tab besteht.
|
||||
"""
|
||||
layer_id = self._anzeige_layer_id
|
||||
if layer_id in self._korrektur_pfade:
|
||||
return self._korrektur_pfade[layer_id]
|
||||
layer = hole_layer(layer_id)
|
||||
vorschlag = korrektur_pfad(layer.source()) if layer is not None else None
|
||||
ordner = os.path.dirname(vorschlag) if vorschlag else ""
|
||||
if vorschlag and os.path.isdir(ordner) and os.access(ordner, os.W_OK):
|
||||
pfad = vorschlag
|
||||
else:
|
||||
pfad, _ = QFileDialog.getSaveFileName(
|
||||
self, "Korrektur-CSV wählen", vorschlag or os.path.expanduser("~"), "CSV-Dateien (*.csv)"
|
||||
)
|
||||
if not pfad:
|
||||
return None
|
||||
self._korrektur_pfade[layer_id] = pfad
|
||||
return pfad
|
||||
|
||||
Reference in New Issue
Block a user