From cc757452bc73c7b62d9eb75287646fb5b1850eb3 Mon Sep 17 00:00:00 2001 From: Michael Otto Date: Tue, 18 Nov 2025 16:10:58 +0100 Subject: [PATCH] Ablauf optimiert --- .../verfahrensgebiet_alkis.py | 81 +++++----- main.py | 22 ++- ui/dockwidget.py | 5 +- ui/tabs/Verfahrensgebiet.qml | 140 ------------------ ui/tabs/tab_a.py | 98 ------------ ui/tabs/tab_b.py | 11 -- ui/tabs/working_tab.py | 112 ++++++++++++++ 7 files changed, 175 insertions(+), 294 deletions(-) rename logic/alkis_verfahrensgebiet.py => functions/verfahrensgebiet_alkis.py (57%) delete mode 100644 ui/tabs/Verfahrensgebiet.qml delete mode 100644 ui/tabs/tab_a.py delete mode 100644 ui/tabs/tab_b.py create mode 100644 ui/tabs/working_tab.py diff --git a/logic/alkis_verfahrensgebiet.py b/functions/verfahrensgebiet_alkis.py similarity index 57% rename from logic/alkis_verfahrensgebiet.py rename to functions/verfahrensgebiet_alkis.py index 4977de0..0565023 100644 --- a/logic/alkis_verfahrensgebiet.py +++ b/functions/verfahrensgebiet_alkis.py @@ -1,7 +1,6 @@ -import os +from enum import Enum from qgis.core import ( - QgsVectorLayer, QgsProject, QgsFeature, QgsField, - QgsFeatureRequest, QgsGeometry + QgsVectorLayer, QgsProject, QgsFeature, QgsField, QgsGeometry ) from qgis.PyQt.QtWidgets import QMessageBox from qgis.PyQt.QtCore import QVariant @@ -11,31 +10,47 @@ alkis_NAS_url = "https://geodienste.sachsen.de/aaa/public_alkis/nas/wfs" typename = "adv:AX_BauRaumOderBodenordnungsrecht" -def alkis_verfahrensgebiet(tab_widget): +class LoadStatus(Enum): + NONE = "none" # nichts geladen + FIRST = "first" # erstmalig geladen + RELOAD = "reload" # neu geladen + KEEP = "keep" # vorhandener Layer behalten + + +def _check_existing_layer(tab_widget): + project = QgsProject.instance() + existing_layers = project.mapLayersByName("Verfahrensgebiet") + if not existing_layers: + return None, LoadStatus.FIRST + + reply = QMessageBox.question( + tab_widget, + "Layer bereits vorhanden", + "Der Layer 'Verfahrensgebiet' existiert bereits.\n" + "Möchten Sie ihn löschen und neu laden?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No + ) + if reply == QMessageBox.StandardButton.Yes: + for lyr in existing_layers: + project.removeMapLayer(lyr.id()) + return None, LoadStatus.RELOAD + else: + return existing_layers[0], LoadStatus.KEEP + + +def verfahrensgebiet_alkis(tab_widget): verfahrensnummer = get_variable("verfahrensnummer") if not verfahrensnummer: QMessageBox.critical(tab_widget, "Fehler", "Die Projektvariable 'sn_verfahrensnummer' ist nicht gesetzt.") tab_widget.setze_haken(tab_widget.haken1, False) - return None, False + return None, LoadStatus.NONE - project = QgsProject.instance() - existing_layers = project.mapLayersByName("Verfahrensgebiet") - if existing_layers: - reply = QMessageBox.question( - tab_widget, - "Layer bereits vorhanden", - "Der Layer 'Verfahrensgebiet' existiert bereits.\n" - "Möchten Sie ihn löschen und neu laden?", - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, - QMessageBox.StandardButton.No - ) - if reply == QMessageBox.StandardButton.Yes: - for lyr in existing_layers: - project.removeMapLayer(lyr.id()) - else: - # Vorhandenen Layer zurückgeben, kein Neu-Laden - return existing_layers[0], False + # Vorhandenen Layer prüfen + existing, status = _check_existing_layer(tab_widget) + if status == LoadStatus.KEEP: + return existing, status # WFS mit Filter laden wfs_uri_find = ( @@ -47,39 +62,31 @@ def alkis_verfahrensgebiet(tab_widget): if not temp_layer.isValid(): QMessageBox.critical(tab_widget, "Fehler", "Layer konnte nicht geladen werden.") - return None, False + return None, LoadStatus.NONE features = list(temp_layer.getFeatures()) if not features: QMessageBox.critical(tab_widget, "Fehler", f"Verfahrenskennzeichen {verfahrensnummer} nicht im WFS gefunden.") - return None, False - - # Geometrie bestimmen - if len(features) == 1: - union_geom = features[0].geometry() - else: - geoms = [f.geometry() for f in features] - union_geom = QgsGeometry.unaryUnion(geoms) + return None, LoadStatus.NONE + union_geom = QgsGeometry.unaryUnion([f.geometry() for f in features]) if union_geom is None or union_geom.isEmpty(): QMessageBox.critical(tab_widget, "Fehler", "Keine Geometrien zum Verschmelzen gefunden.") - return None, False + return None, LoadStatus.NONE - # Memory-Layer mit festem Namen "Verfahrensgebiet" + # Memory-Layer erzeugen crs = temp_layer.crs().authid() memory_layer = QgsVectorLayer(f"Polygon?crs={crs}", "Verfahrensgebiet", "memory") pr = memory_layer.dataProvider() - - # Feld "VKZ" hinzufügen pr.addAttributes([QgsField("VKZ", QVariant.String)]) memory_layer.updateFields() - # Feature mit Geometrie erstellen feat_union = QgsFeature() feat_union.setGeometry(union_geom) feat_union.setAttributes([verfahrensnummer]) pr.addFeatures([feat_union]) memory_layer.updateExtents() - return memory_layer, True + # Status: FIRST oder RELOAD + return memory_layer, status diff --git a/main.py b/main.py index 322a495..ee6305d 100644 --- a/main.py +++ b/main.py @@ -2,7 +2,6 @@ from qgis.utils import plugins from sn_basis.ui.dockmanager import DockManager from .ui.dockwidget import DockWidget - class Verfahrensgebiet: def __init__(self, iface): self.iface = iface @@ -13,8 +12,11 @@ class Verfahrensgebiet: self.plugin_name = self.__class__.__name__ self.dock_name = f"sn_dock_{self.plugin_name.lower()}" + def _basis(self): + return plugins.get("sn_basis") + def initGui(self): - basis = plugins.get("sn_basis") + basis = self._basis() if basis and basis.ui: self.action = basis.ui.add_action( self.plugin_name, @@ -31,12 +33,22 @@ class Verfahrensgebiet: self.dockwidget = None if self.action: - basis = plugins.get("sn_basis") + basis = self._basis() if basis and basis.ui: - # Action aus Menü und Toolbar entfernen basis.ui.remove_action(self.action) self.action = None + # def run(self): + # if not self.dockwidget: + # self.dockwidget = DockWidget(self.iface.mainWindow(), subtitle=self.plugin_name) + # self.dockwidget.setObjectName(self.dock_name) + # self.dockwidget.action = self.action + + # DockManager.show(self.dockwidget) + + # basis = self._basis() + # if basis and basis.ui: + # basis.ui.set_active_plugin(self.action) def run(self): self.dockwidget = DockWidget(self.iface.mainWindow(), subtitle=self.plugin_name) self.dockwidget.setObjectName(self.dock_name) @@ -49,4 +61,4 @@ class Verfahrensgebiet: # Toolbar-Button als aktiv markieren basis = plugins.get("sn_basis") if basis and basis.ui: - basis.ui.set_active_plugin(self.action) + basis.ui.set_active_plugin(self.action) \ No newline at end of file diff --git a/ui/dockwidget.py b/ui/dockwidget.py index 876aa9b..2705985 100644 --- a/ui/dockwidget.py +++ b/ui/dockwidget.py @@ -1,8 +1,7 @@ from sn_basis.ui.tabs.settings_tab import SettingsTab -from sn_verfahrensgebiet.ui.tabs.tab_a import TabA -from sn_verfahrensgebiet.ui.tabs.tab_b import TabB +from sn_verfahrensgebiet.ui.tabs.working_tab import WorkingTab from sn_basis.ui.base_dockwidget import BaseDockWidget class DockWidget(BaseDockWidget): - tabs = [TabA, SettingsTab] + tabs = [WorkingTab, SettingsTab] diff --git a/ui/tabs/Verfahrensgebiet.qml b/ui/tabs/Verfahrensgebiet.qml deleted file mode 100644 index 474e368..0000000 --- a/ui/tabs/Verfahrensgebiet.qml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - 2 - diff --git a/ui/tabs/tab_a.py b/ui/tabs/tab_a.py deleted file mode 100644 index d01b568..0000000 --- a/ui/tabs/tab_a.py +++ /dev/null @@ -1,98 +0,0 @@ -from qgis.PyQt.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox -from qgis.PyQt.QtGui import QIcon, QPixmap -from qgis.PyQt.QtCore import Qt, QTimer -from qgis.core import Qgis, QgsProject, QgsMessageLog -from qgis.utils import iface -import os - -from sn_verfahrensgebiet.logic.alkis_verfahrensgebiet import alkis_verfahrensgebiet -from sn_basis.logic.utils import zoom_to_layer - -class TabA(QWidget): - tab_title = "Bearbeitung" - - def __init__(self, parent=None): - super().__init__(parent) - - layout = QVBoxLayout() - layout.setAlignment(Qt.AlignmentFlag.AlignTop) - - # Abschnitt 1: Verfahrensgebiet - row_verf_header = QHBoxLayout() - lbl_verf = QLabel("Verfahrensgebiet") - self.haken1 = self._haken_label() - row_verf_header.addWidget(lbl_verf) - row_verf_header.addWidget(self.haken1) - row_verf_header.addStretch() - layout.addLayout(row_verf_header) - - row_verf_buttons = QHBoxLayout() - self.btn_verf_alkis = QPushButton("Aus ALKIS laden") - self.btn_verf_shape = QPushButton("Aus Shape laden") - row_verf_buttons.addWidget(self.btn_verf_alkis) - row_verf_buttons.addWidget(self.btn_verf_shape) - layout.addLayout(row_verf_buttons) - - # Abschnitt 2: Flurstücke - row_flurst_header = QHBoxLayout() - lbl_flurst = QLabel("Flurstücke") - self.haken2 = self._haken_label() - row_flurst_header.addWidget(lbl_flurst) - row_flurst_header.addWidget(self.haken2) - row_flurst_header.addStretch() - layout.addLayout(row_flurst_header) - - row_flurst_buttons = QHBoxLayout() - self.btn_flurst_alkis = QPushButton("Aus ALKIS laden") - self.btn_flurst_shape = QPushButton("Aus Shape laden") - row_flurst_buttons.addWidget(self.btn_flurst_alkis) - row_flurst_buttons.addWidget(self.btn_flurst_shape) - layout.addLayout(row_flurst_buttons) - - layout.addStretch() - self.setLayout(layout) - - # Beispiel: Haken anzeigen nach Klick - # self.button1.clicked.connect(lambda: alkis_verfahrensgebiet(self)) - self.btn_verf_alkis.clicked.connect(lambda: self.handle_button1()) - - #self.button2.clicked.connect(lambda: self.setze_haken(self.haken2, True)) - - def handle_button1(self): - layer, neu_geladen = alkis_verfahrensgebiet(self) - - if not layer or not layer.isValid(): - return - - if neu_geladen: - QgsProject.instance().addMapLayer(layer) - iface.mapCanvas().setExtent(layer.extent()) - iface.mapCanvas().refresh() - # Beispiel: Haken setzen oder Meldung ausgeben - self.setze_haken(self.haken1, True) - # QMessageBox.information(self, "Info", "Layer 'Verfahrensgebiet' wurde neu geladen.") - else: - # Kein Zoom, kein Neu-Laden - QgsMessageLog.logMessage("Vorhandener Layer behalten.", "sn_verfahrensgebiet", level=Qgis.Info) - - - def _haken_label(self): - label = QLabel() - label.setFixedSize(20, 20) -# label.setAlignment(Qt.AlignCenter) - label.setAlignment(Qt.AlignmentFlag.AlignCenter) - label.setScaledContents(False) - label.setText("") - return label - - def setze_haken(self, label: QLabel, aktiv: bool): - """Zeigt oder entfernt den grünen Haken.""" - if aktiv: - # grünes Unicode-Häkchen - label.setPixmap(QPixmap()) - label.setText("✓") - label.setStyleSheet("color: #2ea043; font-weight: bold;") - else: - label.setPixmap(QPixmap()) - label.setText("") - label.setStyleSheet("") diff --git a/ui/tabs/tab_b.py b/ui/tabs/tab_b.py deleted file mode 100644 index 9d5d608..0000000 --- a/ui/tabs/tab_b.py +++ /dev/null @@ -1,11 +0,0 @@ -from qgis.PyQt.QtWidgets import QWidget, QVBoxLayout, QLabel, QTextEdit - -class TabB(QWidget): - tab_title = "Tab B" - - def __init__(self, parent=None): - super().__init__(parent) - layout = QVBoxLayout() - layout.addWidget(QLabel("Plugin1 – Tab B")) - layout.addWidget(QTextEdit("Mehrzeiliger Text für Plugin1")) - self.setLayout(layout) diff --git a/ui/tabs/working_tab.py b/ui/tabs/working_tab.py new file mode 100644 index 0000000..2b75098 --- /dev/null +++ b/ui/tabs/working_tab.py @@ -0,0 +1,112 @@ +from qgis.PyQt.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox +from qgis.PyQt.QtCore import Qt +from qgis.core import Qgis, QgsProject, QgsMessageLog +from qgis.utils import iface + +from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import verfahrensgebiet_alkis, LoadStatus +from sn_basis.functions.messages import success, info, warning, error +from sn_basis.functions.styles import apply_style + +class WorkingTab(QWidget): + tab_title = "Bearbeitung" + + def __init__(self, parent=None): + super().__init__(parent) + + layout = QVBoxLayout() + layout.setAlignment(Qt.AlignmentFlag.AlignTop) + + # Abschnitt 1: Verfahrensgebiet + row_verf_header = QHBoxLayout() + lbl_verf = QLabel("Verfahrensgebiet") + self.haken_verf = self._haken_label() + row_verf_header.addWidget(lbl_verf) + row_verf_header.addWidget(self.haken_verf) + row_verf_header.addStretch() + layout.addLayout(row_verf_header) + + row_verf_buttons = QHBoxLayout() + self.btn_verf_alkis = QPushButton("Aus ALKIS laden") + self.btn_verf_shape = QPushButton("Aus Shape laden") + row_verf_buttons.addWidget(self.btn_verf_alkis) + row_verf_buttons.addWidget(self.btn_verf_shape) + layout.addLayout(row_verf_buttons) + + # Abschnitt 2: Flurstücke (Platzhalter für spätere Implementierung) + row_flurst_header = QHBoxLayout() + lbl_flurst = QLabel("Flurstücke") + self.haken_flurst = self._haken_label() + row_flurst_header.addWidget(lbl_flurst) + row_flurst_header.addWidget(self.haken_flurst) + row_flurst_header.addStretch() + layout.addLayout(row_flurst_header) + + row_flurst_buttons = QHBoxLayout() + self.btn_flurst_alkis = QPushButton("Aus ALKIS laden") + self.btn_flurst_shape = QPushButton("Aus Shape laden") + row_flurst_buttons.addWidget(self.btn_flurst_alkis) + row_flurst_buttons.addWidget(self.btn_flurst_shape) + layout.addLayout(row_flurst_buttons) + + layout.addStretch() + self.setLayout(layout) + + # Signale verbinden + self.btn_verf_alkis.clicked.connect(self.handle_verf_alkis) + # self.btn_verf_shape.clicked.connect(self.handle_verf_shape) + # self.btn_flurst_alkis.clicked.connect(self.handle_flurst_alkis) + # self.btn_flurst_shape.clicked.connect(self.handle_flurst_shape) + + def handle_verf_alkis(self): + self._set_busy(self.btn_verf_alkis, True) + try: + layer, status = verfahrensgebiet_alkis(self) + if not layer or not layer.isValid(): + return + + if status in (LoadStatus.FIRST, LoadStatus.RELOAD): + # Gemeinsame Logik für erstmaliges und erneutes Laden + QgsProject.instance().addMapLayer(layer) + iface.mapCanvas().setExtent(layer.extent()) + iface.mapCanvas().refresh() + + apply_style(layer, "verfahrensgebiet.qml") + + self.setze_haken(self.haken_verf, True) + + # Unterschied nur in der Meldung + msg = "erstmalig geladen" if status == LoadStatus.FIRST else "neu geladen" + success("Verfahrensgebiet", f"Layer wurde {msg}.", duration=5) + + elif status == LoadStatus.KEEP: + info("Verfahrensgebiet", "Vorhandener Layer wurde behalten.", duration=4) + + elif status == LoadStatus.NONE: + warning("Verfahrensgebiet", "Layer konnte nicht geladen werden.", duration=None) + + finally: + self._set_busy(self.btn_verf_alkis, False) + + def _set_busy(self, button: QPushButton, busy: bool): + button.setEnabled(not busy) + if busy: + button.setText("Lade ...") + else: + button.setText("Aus ALKIS laden") + + def _haken_label(self) -> QLabel: + label = QLabel() + label.setFixedSize(20, 20) + label.setAlignment(Qt.AlignmentFlag.AlignCenter) + label.setText("") # Start ohne Haken + return label + + def setze_haken(self, label: QLabel, aktiv: bool): + """Zeigt oder entfernt den grünen Haken (Unicode), ohne Theme-Icons.""" + if aktiv: + label.setText("✓") + label.setStyleSheet("color: #2ea043; font-weight: bold;") + else: + label.setText("") + label.setStyleSheet("") +