Files
Plugin_SN_Plan41/ui/tab_a_ui.py

372 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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üfWorkflow / 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("")