"""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("Mehrfachauswahl mit Strg + Umschalt") 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]], ) -> list[tuple[str, str]]: """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). Ob nach dem Laden der weitere Workflow (Gemarkung/Flurstücke/Flächenvergleich) fortgesetzt wird, klärt eine separate Stop-Abfrage nach dem Laden – nicht mehr dieser Dialog. 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_landkreis_objekte → {kombinationen[:1]}") return kombinationen[:1] 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("Mehrfachauswahl mit Strg + Umschalt") 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() # 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 [] 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 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))