Ablauf optimiert

This commit is contained in:
Michael Otto
2025-11-18 16:10:58 +01:00
parent bf479bdb64
commit cc757452bc
7 changed files with 175 additions and 294 deletions

View File

@@ -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

20
main.py
View File

@@ -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)

View File

@@ -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]

View File

@@ -1,140 +0,0 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis version="3.40.7-Bratislava" styleCategories="Symbology">
<renderer-v2 referencescale="-1" forceraster="0" enableorderby="0" type="singleSymbol" symbollevels="0">
<symbols>
<symbol is_animated="0" frame_rate="10" clip_to_extent="1" type="fill" alpha="1" force_rhr="0" name="0">
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
<layer locked="0" id="{feca00b2-500a-4c9a-b285-67ba2d99d8f6}" enabled="1" class="SimpleLine" pass="0">
<Option type="Map">
<Option type="QString" value="0" name="align_dash_pattern"/>
<Option type="QString" value="square" name="capstyle"/>
<Option type="QString" value="5;2" name="customdash"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="customdash_map_unit_scale"/>
<Option type="QString" value="MM" name="customdash_unit"/>
<Option type="QString" value="0" name="dash_pattern_offset"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="dash_pattern_offset_map_unit_scale"/>
<Option type="QString" value="MM" name="dash_pattern_offset_unit"/>
<Option type="QString" value="0" name="draw_inside_polygon"/>
<Option type="QString" value="round" name="joinstyle"/>
<Option type="QString" value="215,168,255,255,rgb:0.84313725490196079,0.6588235294117647,1,1" name="line_color"/>
<Option type="QString" value="solid" name="line_style"/>
<Option type="QString" value="1.5" name="line_width"/>
<Option type="QString" value="MM" name="line_width_unit"/>
<Option type="QString" value="-0.75" name="offset"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="offset_map_unit_scale"/>
<Option type="QString" value="MM" name="offset_unit"/>
<Option type="QString" value="0" name="ring_filter"/>
<Option type="QString" value="0" name="trim_distance_end"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="trim_distance_end_map_unit_scale"/>
<Option type="QString" value="MM" name="trim_distance_end_unit"/>
<Option type="QString" value="0" name="trim_distance_start"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="trim_distance_start_map_unit_scale"/>
<Option type="QString" value="MM" name="trim_distance_start_unit"/>
<Option type="QString" value="0" name="tweak_dash_pattern_on_corners"/>
<Option type="QString" value="0" name="use_custom_dash"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="width_map_unit_scale"/>
</Option>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
<layer locked="0" id="{fdc4d6fd-0995-41df-bbfb-19970b4fc2cc}" enabled="1" class="SimpleLine" pass="0">
<Option type="Map">
<Option type="QString" value="0" name="align_dash_pattern"/>
<Option type="QString" value="square" name="capstyle"/>
<Option type="QString" value="5;2" name="customdash"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="customdash_map_unit_scale"/>
<Option type="QString" value="MM" name="customdash_unit"/>
<Option type="QString" value="0" name="dash_pattern_offset"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="dash_pattern_offset_map_unit_scale"/>
<Option type="QString" value="MM" name="dash_pattern_offset_unit"/>
<Option type="QString" value="0" name="draw_inside_polygon"/>
<Option type="QString" value="round" name="joinstyle"/>
<Option type="QString" value="204,174,137,255,rgb:0.80000000000000004,0.68235294117647061,0.53725490196078429,1" name="line_color"/>
<Option type="QString" value="solid" name="line_style"/>
<Option type="QString" value="0.5" name="line_width"/>
<Option type="QString" value="MM" name="line_width_unit"/>
<Option type="QString" value="0" name="offset"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="offset_map_unit_scale"/>
<Option type="QString" value="MM" name="offset_unit"/>
<Option type="QString" value="0" name="ring_filter"/>
<Option type="QString" value="0" name="trim_distance_end"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="trim_distance_end_map_unit_scale"/>
<Option type="QString" value="MM" name="trim_distance_end_unit"/>
<Option type="QString" value="0" name="trim_distance_start"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="trim_distance_start_map_unit_scale"/>
<Option type="QString" value="MM" name="trim_distance_start_unit"/>
<Option type="QString" value="0" name="tweak_dash_pattern_on_corners"/>
<Option type="QString" value="0" name="use_custom_dash"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="width_map_unit_scale"/>
</Option>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</symbols>
<rotation/>
<sizescale/>
<data-defined-properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data-defined-properties>
</renderer-v2>
<selection mode="Default">
<selectionColor invalid="1"/>
<selectionSymbol>
<symbol is_animated="0" frame_rate="10" clip_to_extent="1" type="fill" alpha="1" force_rhr="0" name="">
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
<layer locked="0" id="{f18003f5-220a-487f-8a6d-b24facc4c1a5}" enabled="1" class="SimpleFill" pass="0">
<Option type="Map">
<Option type="QString" value="3x:0,0,0,0,0,0" name="border_width_map_unit_scale"/>
<Option type="QString" value="0,0,255,255,rgb:0,0,1,1" name="color"/>
<Option type="QString" value="bevel" name="joinstyle"/>
<Option type="QString" value="0,0" name="offset"/>
<Option type="QString" value="3x:0,0,0,0,0,0" name="offset_map_unit_scale"/>
<Option type="QString" value="MM" name="offset_unit"/>
<Option type="QString" value="35,35,35,255,rgb:0.13725490196078433,0.13725490196078433,0.13725490196078433,1" name="outline_color"/>
<Option type="QString" value="solid" name="outline_style"/>
<Option type="QString" value="0.26" name="outline_width"/>
<Option type="QString" value="MM" name="outline_width_unit"/>
<Option type="QString" value="solid" name="style"/>
</Option>
<data_defined_properties>
<Option type="Map">
<Option type="QString" value="" name="name"/>
<Option name="properties"/>
<Option type="QString" value="collection" name="type"/>
</Option>
</data_defined_properties>
</layer>
</symbol>
</selectionSymbol>
</selection>
<blendMode>0</blendMode>
<featureBlendMode>0</featureBlendMode>
<layerGeometryType>2</layerGeometryType>
</qgis>

View File

@@ -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("<b>Verfahrensgebiet</b>")
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("<b>Flurstücke</b>")
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("")

View File

@@ -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)

112
ui/tabs/working_tab.py Normal file
View File

@@ -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("<b>Verfahrensgebiet</b>")
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("<b>Flurstücke</b>")
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("")