# sn_plan41/ui/tab_a_ui.py – UI für Tab A (Daten) from __future__ import annotations from typing import Optional from sn_basis.functions.qt_wrapper import ( QWidget, QVBoxLayout, QLabel, QPushButton, QToolButton, QFileDialog, QMessageBox, ToolButtonTextBesideIcon, ArrowDown, ArrowRight, SizePolicyPreferred, SizePolicyMaximum, ComboBox, ) from sn_basis.functions.qgisui_wrapper import QgsFileWidget, QgsMapLayerComboBox from sn_basis.functions.qgiscore_wrapper import QgsProject, QgsMapLayerProxyModel from sn_basis.functions.variable_wrapper import get_variable, set_variable from sn_plan41.ui.tab_a_logic import TabALogic # Prüf‑Workflow / DataGrabber (werden zur Laufzeit vom Pruefmanager/Pruefern verwendet) from sn_basis.modules.Dateipruefer import Dateipruefer from sn_basis.modules.Pruefmanager import Pruefmanager from sn_basis.modules.DataGrabber import DataGrabber from sn_basis.modules.linkpruefer import Linkpruefer from sn_basis.modules.stilpruefer import Stilpruefer # Raumfilter-Optionen RAUMFILTER_VAR = "Raumfilter" RAUMFILTER_OPTIONS = ("Verfahrensgebiet", "Pufferlayer", "ohne") RAUMFILTER_DEFAULT = "Pufferlayer" pm = Pruefmanager(ui_modus="qgis") lp = Linkpruefer() sp = Stilpruefer() class TabA(QWidget): """ UI-Klasse für Tab A (Daten). Diese bereinigte Version enthält ausschließlich UI-Elemente und einfache, nicht-validierende Callback-Handler. Alle fachlichen Prüfungen und Fehlerbehandlungen werden zur Laufzeit vom Pruefmanager und den Prüfern übernommen. """ tab_title = "Daten" def __init__(self, parent=None, pruefmanager=None, link_pruefer=None, stil_pruefer=None, build_ui=True): super().__init__(parent) self.parent = parent self.tab_title = "Daten" # Logik-Adapter (TabALogic verwaltet persistente Projektvariablen) self.logic = TabALogic(pruefmanager=pruefmanager, link_pruefer=link_pruefer, stil_pruefer=stil_pruefer) # Prüfmanager-Instanz (UI-Modus wird zur Laufzeit vom Pruefmanager gehandhabt) self.pruefmanager = Pruefmanager(ui_modus="qgis") # DataGrabber-Instanz (synchroner Aufruf; Prüfungen übernimmt Pruefmanager/Pruefer) self.data_grabber = DataGrabber(pruefmanager=self.pruefmanager) # Platzhalter, die vom Plugin oder Nutzer gesetzt werden können self._attributes_list = [] # optionale Attributliste (z. B. Excel-Import) self._pufferlayer = None # optionaler Layer (Verfahrensgebiet) self.verfahrens_db: Optional[str] = None self.lokale_linkliste: Optional[str] = None # UI-Widget-Referenz für Raumfilter self._raumfilter_combo: Optional[ComboBox] = None if build_ui: self._build_ui() self._restore_state() # --------------------------------------------------------- # UI-Aufbau # --------------------------------------------------------- def _build_ui(self) -> None: main_layout = QVBoxLayout() main_layout.setSpacing(4) main_layout.setContentsMargins(4, 4, 4, 4) # Verfahrens-Datenbank Gruppe self.group_button = QToolButton() self.group_button.setText("Verfahrens-Datenbank") self.group_button.setCheckable(True) self.group_button.setChecked(True) self.group_button.setToolButtonStyle(ToolButtonTextBesideIcon) self.group_button.setArrowType(ArrowDown) self.group_button.setStyleSheet("font-weight: bold;") self.group_button.toggled.connect(self._toggle_group) main_layout.addWidget(self.group_button) self.group_content = QWidget() self.group_content.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum) group_layout = QVBoxLayout() group_layout.setSpacing(2) group_layout.setContentsMargins(10, 4, 4, 4) group_layout.addWidget(QLabel("bestehende Datei auswählen")) self.file_widget = QgsFileWidget() self.file_widget.setStorageMode(QgsFileWidget.GetFile) self.file_widget.setFilter("Geopackage (*.gpkg)") self.file_widget.fileChanged.connect(self._on_verfahrens_db_changed) group_layout.addWidget(self.file_widget) group_layout.addWidget(QLabel("-oder-")) self.btn_new = QPushButton("Neue Verfahrens-DB anlegen") self.btn_new.clicked.connect(self._create_new_gpkg) group_layout.addWidget(self.btn_new) self.group_content.setLayout(group_layout) main_layout.addWidget(self.group_content) # Optionale Linkliste self.optional_button = QToolButton() self.optional_button.setText("Optional: Lokale Linkliste") self.optional_button.setCheckable(True) self.optional_button.setChecked(False) self.optional_button.setToolButtonStyle(ToolButtonTextBesideIcon) self.optional_button.setArrowType(ArrowRight) self.optional_button.setStyleSheet("font-weight: bold; margin-top: 6px;") self.optional_button.toggled.connect(self._toggle_optional) main_layout.addWidget(self.optional_button) self.optional_content = QWidget() self.optional_content.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum) optional_layout = QVBoxLayout() optional_layout.setSpacing(2) optional_layout.setContentsMargins(10, 4, 4, 20) optional_layout.addWidget(QLabel("(frei lassen für globale Linkliste)")) self.linkliste_widget = QgsFileWidget() self.linkliste_widget.setStorageMode(QgsFileWidget.GetFile) self.linkliste_widget.setFilter("Excelliste (*.xlsx)") self.linkliste_widget.fileChanged.connect(self._on_linkliste_changed) optional_layout.addWidget(self.linkliste_widget) self.optional_content.setLayout(optional_layout) self.optional_content.setVisible(False) main_layout.addWidget(self.optional_content) # Layer-Auswahl layer_label = QLabel("Verfahrensgebiet-Layer auswählen") layer_label.setStyleSheet("font-weight: bold; margin-top: 6px;") main_layout.addWidget(layer_label) self.layer_combo = QgsMapLayerComboBox() self.layer_combo.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum) self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer) self.layer_combo.layerChanged.connect(self._on_layer_changed) main_layout.addWidget(self.layer_combo) # Raumfilter-Label + ComboBox (unterhalb der Layer-Auswahl) main_layout.addWidget(QLabel("Raumfilter")) self._raumfilter_combo = ComboBox(self) # Fülle Optionen (Wrapper stellt addItems bereit) try: self._raumfilter_combo.addItems(list(RAUMFILTER_OPTIONS)) except Exception: # fallback: iterativ hinzufügen, falls Wrapper andere API hat for opt in RAUMFILTER_OPTIONS: if hasattr(self._raumfilter_combo, "addItem"): self._raumfilter_combo.addItem(opt) # Initialisiere Auswahl aus Projekt-Variable oder Default stored = get_variable(RAUMFILTER_VAR, scope="project") if isinstance(stored, str) and stored in RAUMFILTER_OPTIONS: try: self._raumfilter_combo.setCurrentText(stored) except Exception: try: idx = self._raumfilter_combo.findText(stored) if idx is not None and idx >= 0: self._raumfilter_combo.setCurrentIndex(idx) except Exception: pass else: try: self._raumfilter_combo.setCurrentText(RAUMFILTER_DEFAULT) except Exception: try: idx = self._raumfilter_combo.findText(RAUMFILTER_DEFAULT) if idx is not None and idx >= 0: self._raumfilter_combo.setCurrentIndex(idx) except Exception: pass # persistiere Default, falls noch kein Wert gesetzt if not stored: set_variable(RAUMFILTER_VAR, RAUMFILTER_DEFAULT, scope="project") # Signal: bei Änderung Variable setzen try: self._raumfilter_combo.currentTextChanged.connect(self._on_raumfilter_changed) except Exception: try: self._raumfilter_combo.current_text_changed.connect(self._on_raumfilter_changed) except Exception: pass main_layout.addWidget(self._raumfilter_combo) # Aktion: Fachdaten laden self.btn_load = QPushButton("Fachdaten laden") self.btn_load.clicked.connect(self._on_load_fachdaten) main_layout.addWidget(self.btn_load) main_layout.addStretch(1) self.setLayout(main_layout) # --------------------------------------------------------- # State Restore (UI-Wiederherstellung ohne Prüfungen) # --------------------------------------------------------- def _restore_state(self) -> None: db = self.logic.load_verfahrens_db() if db: self.verfahrens_db = db try: self.file_widget.setFilePath(db) except Exception: pass self._update_group_color() link = self.logic.load_linkliste() if link: self.lokale_linkliste = link try: self.linkliste_widget.setFilePath(link) except Exception: pass layer_id = self.logic.load_verfahrensgebiet_layer_id() if layer_id: layer = QgsProject.instance().mapLayer(layer_id) if layer: self.layer_combo.setLayer(layer) # Raumfilter aus Variable wiederherstellen (falls Combo existiert) try: stored = get_variable(RAUMFILTER_VAR, scope="project") if stored and self._raumfilter_combo is not None: try: self._raumfilter_combo.setCurrentText(stored) except Exception: idx = self._raumfilter_combo.findText(stored) if idx is not None and idx >= 0: self._raumfilter_combo.setCurrentIndex(idx) except Exception: pass # --------------------------------------------------------- # UI-Callbacks (ohne Prüfungen / Exceptions) # --------------------------------------------------------- def _toggle_group(self, checked: bool) -> None: self.group_button.setArrowType(ArrowDown if checked else ArrowRight) self.group_content.setVisible(checked) def _toggle_optional(self, checked: bool) -> None: self.optional_button.setArrowType(ArrowDown if checked else ArrowRight) self.optional_content.setVisible(checked) def _on_verfahrens_db_changed(self, path: str) -> None: self.verfahrens_db = path self.logic.set_verfahrens_db(path) self._update_group_color() def _on_linkliste_changed(self, path: str) -> None: self.lokale_linkliste = path self.logic.set_linkliste(path) def _on_layer_changed(self, layer) -> None: self.logic.save_verfahrensgebiet_layer(layer) self._pufferlayer = layer def _create_new_gpkg(self) -> None: file_path, _ = QFileDialog.getSaveFileName( self, "Neue Verfahrens-Datenbank anlegen", "", "Geopackage (*.gpkg)", ) if not file_path: return if not file_path.lower().endswith(".gpkg"): file_path += ".gpkg" # Delegation an TabALogic; TabALogic / Pruefmanager übernehmen Prüfungen self.logic.create_new_verfahrens_db(file_path) self.verfahrens_db = file_path try: self.file_widget.setFilePath(file_path) except Exception: pass self._update_group_color() def _on_load_fachdaten(self) -> None: """ Platzhalter-Handler für 'Fachdaten laden'. Keine Prüfungen oder Exception-Handling hier. Die fachliche Prüfung und Fehlerbehandlung erfolgen zur Laufzeit durch den Pruefmanager und die Prüfer, die vom DataGrabber verwendet werden. """ pfad = self.file_widget.filePath() # Dateipruefer wird zur Laufzeit verwendet; hier nur der Aufruf pruefer = Dateipruefer(pfad=pfad, temporaer_erlaubt=True) ergebnis = pruefer.pruefe() ergebnis = self.pruefmanager.verarbeite(ergebnis) zielpfad = None if ergebnis.kontext is not None: try: zielpfad = str(ergebnis.kontext) except Exception: zielpfad = ergebnis.kontext self.data_grabber.run( attributes_list=self._attributes_list, pufferlayer=self._pufferlayer, zielpfad=zielpfad, temporaer=(ergebnis.aktion == "temporaer_erzeugen"), temporaer_erlaubt=True, ) # --------------------------------------------------------- # Raumfilter Callback # --------------------------------------------------------- def _on_raumfilter_changed(self, value: str) -> None: # Persistiere Auswahl in Projekt-Variable; Prüfungen übernimmt die Laufzeitlogik set_variable(RAUMFILTER_VAR, value, scope="project") # --------------------------------------------------------- # UI-Helfer # --------------------------------------------------------- def _prompt_user_to_select_file(self) -> None: fname, _ = QFileDialog.getOpenFileName( self, "Verfahrens-DB auswählen", "", "Geopackage (*.gpkg)", ) if fname: try: self.file_widget.setFilePath(fname) except Exception: try: self.file_widget.setFileName(fname) except Exception: self.file_widget.setProperty("filePath", fname) self.verfahrens_db = fname self.logic.set_verfahrens_db(fname) self._update_group_color() def _update_group_color(self) -> None: if self.verfahrens_db: self.group_button.setStyleSheet("font-weight: bold;") else: self.group_button.setStyleSheet("")