- Ist weder Verfahrensnummer noch Landkreis in den Projekteigenschaften gesetzt, wird jetzt per Dialog ein Landkreis abgefragt (inkl. "Alle Landkreise") statt direkt abzubrechen. - Verfahrensgebiet-spezifische ALKIS-Dialoge aus sn_basis hierher verschoben: neue Datei functions/verfahrensgebiet_dialoge.py (ask_bror_kombinationen, ask_landkreis_objekte, ask_landkreis_auswahl). - Bugfix: BROR-Layer bekommt seinen Style (BauRaumOderBodenordnungsrecht.qml) jetzt direkt beim Laden in verfahrensgebiet_alkis_komplett(), unabhängig vom weiteren Ablauf. working_tab.py überspringt bei "Nur Objekte laden" jetzt den gesamten VG-Nachbehandlungsblock (Style/Extent/Haken/Combobox/ Erfolgsmeldung), da dort kein echter VG-Layer entsteht. Detailprüfung/ Verifikation im echten QGIS (nicht nur per Unittest) steht noch aus. - Bugfix: Erfolgsmeldung "Layer wurde ... geladen (inkl. Flurstücksverschneidung)" erscheint nicht mehr bei "Nur Objekte laden". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
300 lines
11 KiB
Python
300 lines
11 KiB
Python
"""sn_verfahrensgebiet/functions/verfahrensgebiet_dialoge.py – Dialoge zum
|
||
Laden eines Verfahrensgebiets aus ALKIS (Qt5/6/Mock-kompatibel).
|
||
"""
|
||
from typing import Any, Optional
|
||
from sn_basis.functions.qt_wrapper import (
|
||
QT_VERSION, exec_dialog, QInputDialog, QLineEdit,
|
||
QDialog, QDialogButtonBox, QListWidget, QListWidgetItem, QAbstractItemView,
|
||
QTableWidget, QTableWidgetItem, QTableWidgetSelectionRange,
|
||
QIcon, QVBoxLayout, QHBoxLayout, QLabel, QCheckBox,
|
||
DialogAccepted, ExtendedSelection, SelectRows, NoEditTriggers, AscendingOrder,
|
||
)
|
||
from sn_basis.functions.landkreise import ALLE_LANDKREISE
|
||
import os
|
||
|
||
# .../python/plugins (drei Ebenen über dieser Datei)
|
||
_PLUGINS_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||
_WAPPEN_ICON = os.path.join(_PLUGINS_DIR, "sn_basis", "favicon.ico")
|
||
|
||
|
||
def ask_bror_kombinationen(
|
||
parent: Any,
|
||
kombinationen: list[tuple[str, str]],
|
||
) -> list[tuple[str, str]]:
|
||
"""Zeigt Dialog zur Auswahl einer oder mehrerer BROR-Kombinationen
|
||
(adv_name + bezeichnung).
|
||
|
||
Wird aufgerufen, wenn der WFS mehrere verschiedene Kombinationen aus
|
||
``adv_name`` und ``bezeichnung`` zurückliefert. Es können ein oder
|
||
mehrere Objekte gemeinsam ausgewählt und geladen (verschmolzen) werden.
|
||
|
||
Parameters
|
||
----------
|
||
parent :
|
||
Eltern-Widget oder None.
|
||
kombinationen :
|
||
Sortierte Liste von ``(adv_name, bezeichnung)``-Tupeln.
|
||
|
||
Returns
|
||
-------
|
||
list[tuple[str, str]]
|
||
Die gewählten ``(adv_name, bezeichnung)``-Tupel. Leere Liste bei
|
||
Abbruch oder wenn nichts ausgewählt wurde.
|
||
"""
|
||
if not kombinationen:
|
||
return []
|
||
|
||
if QT_VERSION == 0: # Mock-Modus
|
||
print(f"Mock-Modus: ask_bror_kombinationen → {kombinationen[:1]}")
|
||
return kombinationen[:1]
|
||
|
||
dialog = QDialog(parent)
|
||
dialog.setWindowTitle("Verfahrensgebiet aus ALKIS laden")
|
||
if os.path.exists(_WAPPEN_ICON):
|
||
dialog.setWindowIcon(QIcon(_WAPPEN_ICON))
|
||
layout = QVBoxLayout(dialog)
|
||
|
||
hinweis = QLabel(
|
||
f"Es wurden {len(kombinationen)} Objekte gefunden.\n\n"
|
||
"Bitte wählen Sie eines oder mehrere Objekte aus:"
|
||
)
|
||
hinweis.setWordWrap(True)
|
||
layout.addWidget(hinweis)
|
||
|
||
list_widget = QListWidget()
|
||
list_widget.setSelectionMode(ExtendedSelection)
|
||
for adv, bez in kombinationen:
|
||
item = QListWidgetItem(f"{adv} | {bez}")
|
||
list_widget.addItem(item)
|
||
list_widget.item(0).setSelected(True)
|
||
layout.addWidget(list_widget, 1) # Stretch-Faktor: wächst mit Fenstergröße
|
||
|
||
mehrfachauswahl_hinweis = QLabel("<small>Mehrfachauswahl mit Strg + Umschalt</small>")
|
||
layout.addWidget(mehrfachauswahl_hinweis)
|
||
|
||
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
||
buttons.accepted.connect(dialog.accept)
|
||
buttons.rejected.connect(dialog.reject)
|
||
layout.addWidget(buttons)
|
||
|
||
dialog.adjustSize()
|
||
breite = dialog.sizeHint().width()
|
||
|
||
# Liste auf max. 5 sichtbare Zeilen begrenzen, Rest per Scrollbar
|
||
sichtbare_zeilen = min(len(kombinationen), 5)
|
||
zeilenhoehe = list_widget.sizeHintForRow(0) if list_widget.count() else 20
|
||
# + Puffer für eine ggf. eingeblendete horizontale Scrollbar (bei langen
|
||
# Einträgen), damit diese nicht die letzte Zeile abschneidet.
|
||
scrollbar_puffer = list_widget.horizontalScrollBar().sizeHint().height()
|
||
list_hoehe = zeilenhoehe * sichtbare_zeilen + 2 * list_widget.frameWidth() + 4 + scrollbar_puffer
|
||
list_widget.setMinimumHeight(list_hoehe)
|
||
|
||
dialog.resize(breite, dialog.sizeHint().height())
|
||
|
||
if exec_dialog(dialog) != DialogAccepted:
|
||
return []
|
||
|
||
ausgewaehlte_indizes = sorted(list_widget.row(item) for item in list_widget.selectedItems())
|
||
return [kombinationen[i] for i in ausgewaehlte_indizes]
|
||
|
||
|
||
def ask_landkreis_objekte(
|
||
parent: Any,
|
||
kombinationen: list[tuple[str, str]],
|
||
) -> tuple[list[tuple[str, str]], bool]:
|
||
"""Zeigt Dialog zur Auswahl eines, mehrerer oder aller BROR-Objekte eines
|
||
Landkreises (Fallback, wenn keine Verfahrensnummer gesetzt ist).
|
||
|
||
Wie :func:`ask_bror_kombinationen`, zusätzlich mit einer "Alle
|
||
auswählen"-Checkbox (da hier potenziell deutlich mehr Treffer als bei der
|
||
VKZ-Suche auftreten) und einer "Nur laden"-Checkbox, mit der gesteuert
|
||
wird, ob nach dem Laden der weitere Workflow (Gemarkung/Flurstücke/
|
||
Flächenvergleich) fortgesetzt werden soll.
|
||
|
||
Parameters
|
||
----------
|
||
parent :
|
||
Eltern-Widget oder None.
|
||
kombinationen :
|
||
Sortierte Liste von ``(adv_name, bezeichnung)``-Tupeln.
|
||
|
||
Returns
|
||
-------
|
||
tuple[list[tuple[str, str]], bool]
|
||
``(ausgewaehlte_kombinationen, nur_laden)``. ``([], False)`` bei
|
||
Abbruch oder wenn nichts ausgewählt wurde.
|
||
"""
|
||
if not kombinationen:
|
||
return [], False
|
||
|
||
if QT_VERSION == 0: # Mock-Modus
|
||
print(f"Mock-Modus: ask_landkreis_objekte → {kombinationen[:1]}")
|
||
return kombinationen[:1], False
|
||
|
||
dialog = QDialog(parent)
|
||
dialog.setWindowTitle("Verfahrensgebiete aus Landkreis laden")
|
||
if os.path.exists(_WAPPEN_ICON):
|
||
dialog.setWindowIcon(QIcon(_WAPPEN_ICON))
|
||
layout = QVBoxLayout(dialog)
|
||
|
||
hinweis = QLabel(
|
||
f"Es wurden {len(kombinationen)} Objekte gefunden.\n\n"
|
||
"Bitte wählen Sie eines oder mehrere Objekte aus:"
|
||
)
|
||
hinweis.setWordWrap(True)
|
||
layout.addWidget(hinweis)
|
||
|
||
suche_zeile = QHBoxLayout()
|
||
suche_zeile.addWidget(QLabel("Suche:"))
|
||
suche_feld = QLineEdit()
|
||
suche_feld.setPlaceholderText("Nummer oder Name …")
|
||
suche_zeile.addWidget(suche_feld)
|
||
layout.addLayout(suche_zeile)
|
||
|
||
alle_auswaehlen = QCheckBox("Alle auswählen")
|
||
layout.addWidget(alle_auswaehlen)
|
||
|
||
# Standardsortierung nach Bezeichnung; per Klick auf Spaltenkopf änderbar
|
||
# (setSortingEnabled aktiviert die eingebaute Tabellensortierung).
|
||
sortierte_kombinationen = sorted(kombinationen, key=lambda kb: kb[1])
|
||
|
||
table = QTableWidget(len(sortierte_kombinationen), 2)
|
||
table.setHorizontalHeaderLabels(["Nummer", "Name"])
|
||
table.setSelectionMode(ExtendedSelection)
|
||
table.setSelectionBehavior(SelectRows)
|
||
table.setEditTriggers(NoEditTriggers)
|
||
table.verticalHeader().setVisible(False)
|
||
table.verticalHeader().setDefaultSectionSize(22) # flachere Zeilen
|
||
table.horizontalHeader().setStretchLastSection(True)
|
||
table.setSortingEnabled(False)
|
||
for row, (adv, bez) in enumerate(sortierte_kombinationen):
|
||
table.setItem(row, 0, QTableWidgetItem(bez))
|
||
table.setItem(row, 1, QTableWidgetItem(adv))
|
||
table.setSortingEnabled(True)
|
||
table.sortByColumn(0, AscendingOrder) # Standard: aufsteigend nach Nummer
|
||
table.resizeColumnToContents(0)
|
||
table.setColumnWidth(0, max(110, table.columnWidth(0)))
|
||
table.selectRow(0)
|
||
layout.addWidget(table, 1) # Stretch-Faktor: wächst mit Fenstergröße
|
||
|
||
def _suche_geaendert(text):
|
||
text_lower = text.strip().lower()
|
||
for row in range(table.rowCount()):
|
||
bez_text = table.item(row, 0).text().lower()
|
||
adv_text = table.item(row, 1).text().lower()
|
||
treffer = not text_lower or text_lower in bez_text or text_lower in adv_text
|
||
table.setRowHidden(row, not treffer)
|
||
|
||
suche_feld.textChanged.connect(_suche_geaendert)
|
||
|
||
def _alle_auswaehlen_umschalten(checked):
|
||
table.clearSelection()
|
||
if checked:
|
||
letzte_spalte = table.columnCount() - 1
|
||
for row in range(table.rowCount()):
|
||
if not table.isRowHidden(row):
|
||
# setRangeSelected (statt selectRow) fügt der Selektion
|
||
# hinzu, ohne vorherige Zeilen wieder abzuwählen.
|
||
table.setRangeSelected(
|
||
QTableWidgetSelectionRange(row, 0, row, letzte_spalte), True
|
||
)
|
||
|
||
alle_auswaehlen.stateChanged.connect(_alle_auswaehlen_umschalten)
|
||
|
||
mehrfachauswahl_hinweis = QLabel("<small>Mehrfachauswahl mit Strg + Umschalt</small>")
|
||
layout.addWidget(mehrfachauswahl_hinweis)
|
||
|
||
nur_laden_checkbox = QCheckBox("Nur Objekte laden (ohne weitere Prüfungen)")
|
||
layout.addWidget(nur_laden_checkbox)
|
||
|
||
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
||
buttons.accepted.connect(dialog.accept)
|
||
buttons.rejected.connect(dialog.reject)
|
||
layout.addWidget(buttons)
|
||
|
||
dialog.adjustSize()
|
||
# Bezeichnungs-Spalte + großzügige Breite für den ADV-Namen (lange Namen
|
||
# sollen möglichst ohne Abschneiden sichtbar sein) + Rahmen/Scrollbar-Puffer.
|
||
breite = max(dialog.sizeHint().width(), table.columnWidth(0) + 480)
|
||
|
||
# Tabelle auf max. 15 sichtbare Zeilen begrenzen, Rest per Scrollbar – hier
|
||
# potenziell deutlich mehr Treffer als bei der VKZ-Suche, daher großzügiger
|
||
# bemessen als bei ask_bror_kombinationen (dort 5).
|
||
sichtbare_zeilen = min(len(sortierte_kombinationen), 15)
|
||
zeilenhoehe = table.verticalHeader().defaultSectionSize() if table.rowCount() else 20
|
||
kopfzeile_hoehe = table.horizontalHeader().height()
|
||
# + Puffer für eine ggf. eingeblendete horizontale Scrollbar (bei langen
|
||
# Einträgen), damit diese nicht die letzte Zeile abschneidet.
|
||
scrollbar_puffer = table.horizontalScrollBar().sizeHint().height()
|
||
table_hoehe = (
|
||
zeilenhoehe * sichtbare_zeilen + kopfzeile_hoehe
|
||
+ 2 * table.frameWidth() + 4 + scrollbar_puffer
|
||
)
|
||
table.setMinimumHeight(table_hoehe)
|
||
|
||
dialog.resize(breite, dialog.sizeHint().height())
|
||
|
||
if exec_dialog(dialog) != DialogAccepted:
|
||
return [], False
|
||
|
||
ausgewaehlte_zeilen = sorted(set(item.row() for item in table.selectedItems()))
|
||
ausgewaehlt = [(table.item(row, 1).text(), table.item(row, 0).text()) for row in ausgewaehlte_zeilen]
|
||
return ausgewaehlt, nur_laden_checkbox.isChecked()
|
||
|
||
|
||
def ask_landkreis_auswahl(
|
||
parent: Any,
|
||
landkreise: list[tuple[str, str]],
|
||
) -> Optional[str]:
|
||
"""Fragt per Dropdown-Dialog einen Landkreis ab (Fallback, wenn weder
|
||
Verfahrensnummer noch Landkreis in den Projekteigenschaften gesetzt ist).
|
||
|
||
Parameters
|
||
----------
|
||
parent :
|
||
Eltern-Widget oder None.
|
||
landkreise :
|
||
Liste von ``(kennzahl, name)``-Tupeln, z.B. ``LANDKREISE`` aus
|
||
:mod:`sn_basis.functions.landkreise`.
|
||
|
||
Die Auswahlliste beginnt mit einem leeren Platzhalter (der Nutzer muss
|
||
aktiv einen Eintrag wählen) und enthält zusätzlich "Alle Landkreise"
|
||
(Sentinel :data:`~sn_basis.functions.landkreise.ALLE_LANDKREISE`).
|
||
|
||
Returns
|
||
-------
|
||
Optional[str]
|
||
Gewählte Landkreiskennzahl, :data:`ALLE_LANDKREISE` bei Auswahl aller
|
||
Landkreise, oder ``None`` bei Abbruch, leerem Platzhalter oder leerer
|
||
Landkreisliste.
|
||
"""
|
||
if not landkreise:
|
||
return None
|
||
|
||
if QT_VERSION == 0: # Mock-Modus
|
||
print("Mock-Modus: ask_landkreis_auswahl → None (keine aktive Auswahl)")
|
||
return None
|
||
|
||
ALLE_LABEL = "Alle Landkreise"
|
||
namen = [""] + [ALLE_LABEL] + [name for _, name in landkreise]
|
||
|
||
dialog = QInputDialog(parent)
|
||
dialog.setWindowTitle("Landkreis auswählen")
|
||
if os.path.exists(_WAPPEN_ICON):
|
||
dialog.setWindowIcon(QIcon(_WAPPEN_ICON))
|
||
dialog.setLabelText("Landkreis:")
|
||
dialog.setComboBoxItems(namen)
|
||
dialog.setComboBoxEditable(False)
|
||
|
||
if exec_dialog(dialog) != DialogAccepted:
|
||
return None
|
||
|
||
name = dialog.textValue()
|
||
if not name:
|
||
return None
|
||
if name == ALLE_LABEL:
|
||
return ALLE_LANDKREISE
|
||
|
||
name_zu_kennzahl = {n: k for k, n in landkreise}
|
||
return name_zu_kennzahl.get(str(name))
|