Compare commits

...

12 Commits

Author SHA1 Message Date
Michael Otto 9dc15b5b92 auf neuen Release-Workflow umgestellt
Release Plugin / release (push) Successful in 5s
2026-07-02 11:02:04 +02:00
release-bot 319d425670 Release 26.6.1-unstable 2026-06-19 09:53:11 +00:00
Daniel 970343cd98 changelog.txt aktualisiert
Release Plugin / release (push) Successful in 4s
2026-06-19 11:52:15 +02:00
Daniel 3d9bfdb316 Merge pull request 'Api integration plan41' (#50) from API-Integration_Plan41 into unstable
Reviewed-on: #50
2026-06-19 11:48:35 +02:00
Daniel 15e48f58af VLN-API integriert 2026-06-19 11:47:36 +02:00
release-bot 933b3f9cb6 Release 26.5.2-testing 2026-05-22 12:30:19 +00:00
Daniel f6b62395dd Merge pull request 'Feature/vkz differenzierung verfahrensgebiet' (#49) from feature/vkz-differenzierung-verfahrensgebiet into testing
Release Plugin / release (push) Successful in 4s
Reviewed-on: #49
2026-05-22 14:29:36 +02:00
Daniel 025a5f5e13 Messagebox-Fix "VG" in "Verfahrensgebiet" 2026-05-22 14:26:58 +02:00
Daniel e196a4896f Fokusverlust bei Dialogauswahl gefixt 2026-05-22 08:56:26 +02:00
Daniel 63a2e8d63a dialogbox für BROR-Abfrage ergänzt 2026-05-22 08:41:12 +02:00
release-bot 79b827c009 Release 26.5.1-testing 2026-05-06 11:14:30 +00:00
Daniel 0e695ba595 Merge pull request 'Grenzpunkt-Funktionen für Verfahrensgebiet' (#47) from unstable into testing
Release Plugin / release (push) Successful in 4s
Reviewed-on: #47
2026-05-06 13:13:33 +02:00
6 changed files with 216 additions and 5 deletions
@@ -1,5 +1,5 @@
[general]
version=26.5.1-unstable
version=
name=LNO Sachsen | Plugin Basisfunktionen
description=Plugin mit Basisfunktionen
author=Daniel Helbig, Michael Otto
+7
View File
@@ -1,4 +1,11 @@
---
Version 26.6.1-unstable:
- VLN-API zum Laden der Plan41-Maßnahmen integriert
---
Version 26.5.2-testing:
---
Version 26.5.1-testing:
---
Version 26.5.1-unstable:
---
Version 26.4.8-unstable:
+68 -2
View File
@@ -200,7 +200,7 @@ def ask_detailpruefung_oder_vg_laden(
Eltern-Widget oder None.
title : str
Dialog-Titel.
message : str
message : str
Hauptmeldung mit Flächen-Informationen.
Returns
@@ -218,7 +218,7 @@ def ask_detailpruefung_oder_vg_laden(
msg.setText(message)
detail_btn = msg.addButton("Detailprüfung starten", QMessageBox.ButtonRole.AcceptRole)
vg_btn = msg.addButton("Bodenordnungsobjekt als VG laden", QMessageBox.ButtonRole.ActionRole)
vg_btn = msg.addButton("Bodenordnungsobjekt als Verfahrensgebiet laden", QMessageBox.ButtonRole.ActionRole)
msg.addButton("Abbrechen", QMessageBox.ButtonRole.RejectRole)
exec_dialog(msg)
@@ -324,7 +324,73 @@ class ProgressDialog:
if self._dlg is not None:
self._dlg.close()
def raise_to_front(self) -> None:
"""Bringt den Fortschrittsdialog in den Vordergrund.
Wird aufgerufen, nachdem ein modaler Subdialog (z.B. Auswahlbox)
geschlossen wurde und der Fortschrittsdialog wieder sichtbar sein soll.
"""
if QT_VERSION == 0:
return
if self._dlg is not None:
try:
self._dlg.raise_()
self._dlg.activateWindow()
QCoreApplication.processEvents()
except Exception:
pass
def create_progress_dialog(total: int, title: str = "Fortschritt", label: str = "Verarbeite...") -> ProgressDialog:
return ProgressDialog(total, title, label)
def ask_bror_kombination(
parent: Any,
kombinationen: list[tuple[str, str]],
) -> Optional[tuple[str, str]]:
"""Zeigt Dialog zur Auswahl einer BROR-Kombination (adv_name + bezeichnung).
Wird aufgerufen, wenn der WFS mehrere verschiedene Kombinationen aus
``adv_name`` und ``bezeichnung`` zurückliefert, sodass der Nutzer
entscheiden muss, welche Objekte in die Pipeline einfließen sollen.
Parameters
----------
parent :
Eltern-Widget oder None.
kombinationen :
Sortierte Liste von ``(adv_name, bezeichnung)``-Tupeln.
Returns
-------
Optional[tuple[str, str]]
Das gewählte ``(adv_name, bezeichnung)``-Tupel oder ``None`` bei
Abbruch bzw. leerer Eingabe.
"""
if not kombinationen:
return None
if QT_VERSION == 0: # Mock-Modus
print(f"Mock-Modus: ask_bror_kombination → {kombinationen[0]}")
return kombinationen[0]
items = [f"{adv} | {bez}" for adv, bez in kombinationen]
item, ok = QInputDialog.getItem(
parent,
"BROR-Objekt auswählen",
"Mehrere BauRaumOderBodenordnungsrecht-Objekte gefunden.\n"
"Bitte wählen Sie die gewünschte Kombination\n"
"aus ADV-Name und Bezeichnung:",
items,
0,
False,
)
if not ok:
return None
try:
idx = items.index(item)
return kombinationen[idx]
except ValueError:
return None
+19
View File
@@ -5,6 +5,25 @@ from sn_basis.functions.sys_wrapper import get_plugin_root, join_path
from sn_basis.modules.stilpruefer import Stilpruefer
from typing import Optional
def apply_style_from_path(layer, style_path: str) -> bool:
"""
Wendet einen Layerstil anhand eines vollständigen Pfades an.
Im Gegensatz zu :func:`apply_style` wird der Pfad extern bereitgestellt;
es findet keine eigene Stilprüfung statt.
"""
if not layer_exists(layer):
return False
try:
ok, _ = layer.loadNamedStyle(str(style_path))
if ok:
getattr(layer, "triggerRepaint", lambda: None)()
return True
except Exception:
pass
return False
def apply_style(layer, style_name: str) -> bool:
"""
Wendet einen Layerstil an, sofern er gültig ist.
+89
View File
@@ -38,6 +38,12 @@ QgsLayoutPoint: Type[Any]
QgsLayoutSize: Type[Any]
QgsUnitTypes: Type[Any]
QgsLayoutItem: Type[Any]
QgsBlockingNetworkRequest: Type[Any]
QgsJsonExporter: Type[Any]
QgsJsonUtils: Type[Any]
GEOM_POINT: Any = None
GEOM_LINE: Any = None
GEOM_POLYGON: Any = None
MAP_GRID_STYLE_MARKERS: Any = None
MAP_GRID_STYLE_FRAME_ANNOTATIONS: Any = None
@@ -91,6 +97,9 @@ try:
QgsLayoutSize as _QgsLayoutSize,
QgsUnitTypes as _QgsUnitTypes,
QgsLayoutItem as _QgsLayoutItem,
QgsBlockingNetworkRequest as _QgsBlockingNetworkRequest,
QgsJsonExporter as _QgsJsonExporter,
QgsJsonUtils as _QgsJsonUtils,
)
QgsProject = _QgsProject
@@ -118,6 +127,32 @@ try:
QgsLayoutSize = _QgsLayoutSize
QgsUnitTypes = _QgsUnitTypes
QgsLayoutItem = _QgsLayoutItem
QgsBlockingNetworkRequest = _QgsBlockingNetworkRequest
QgsJsonExporter = _QgsJsonExporter
QgsJsonUtils = _QgsJsonUtils
# Geometrietyp-Konstanten (QGIS 4 / QGIS 3.30+ vs. ältere QGIS-3)
def _resolve_geom_type(*paths):
for obj, *attrs in paths:
val = obj
for attr in attrs:
val = getattr(val, attr, None)
if val is None:
break
else:
if val is not None:
return val
return None
GEOM_POINT = _resolve_geom_type(
(_Qgis, "GeometryType", "Point"),
) or getattr(__import__("qgis.core", fromlist=["QgsWkbTypes"]), "QgsWkbTypes", type("_", (), {"PointGeometry": 0})).PointGeometry
GEOM_LINE = _resolve_geom_type(
(_Qgis, "GeometryType", "Line"),
) or getattr(__import__("qgis.core", fromlist=["QgsWkbTypes"]), "QgsWkbTypes", type("_", (), {"LineGeometry": 1})).LineGeometry
GEOM_POLYGON = _resolve_geom_type(
(_Qgis, "GeometryType", "Polygon"),
) or getattr(__import__("qgis.core", fromlist=["QgsWkbTypes"]), "QgsWkbTypes", type("_", (), {"PolygonGeometry": 2})).PolygonGeometry
def _resolve_qgis_enum(*paths: tuple[Any, ...]) -> Any:
for path in paths:
@@ -777,6 +812,60 @@ except Exception:
QgsVectorFileWriter = _MockQgsVectorFileWriter
# Geometrietyp-Konstanten (Mock-Werte entsprechen QgsWkbTypes)
GEOM_POINT = 0
GEOM_LINE = 1
GEOM_POLYGON = 2
class _MockQgsBlockingNetworkRequest:
NoError = 0
def __init__(self):
self._reply = None
def get(self, request: Any) -> int:
return self.NoError
def post(self, request: Any, data: bytes) -> int:
return self.NoError
def put(self, request: Any, data: bytes) -> int:
return self.NoError
def reply(self) -> Any:
return self._reply
def errorMessage(self) -> str:
return ""
QgsBlockingNetworkRequest = _MockQgsBlockingNetworkRequest
class _MockQgsJsonUtils:
@staticmethod
def stringToFields(text: str) -> Any:
return type("_Fields", (), {"toList": lambda self: []})()
@staticmethod
def stringToFeatureList(text: str, fields: Any) -> list:
return []
QgsJsonUtils = _MockQgsJsonUtils
class _MockQgsJsonExporter:
def __init__(self, layer: Any = None):
self._layer = layer
def setSourceCrs(self, crs: Any) -> None:
pass
def setTransformGeometries(self, transform: bool) -> None:
pass
def exportFeatures(self, features: list) -> str:
return '{"type":"FeatureCollection","features":[]}'
QgsJsonExporter = _MockQgsJsonExporter
# ---------------------------------------------------------
# Netzwerk
# ---------------------------------------------------------
+32 -2
View File
@@ -45,6 +45,7 @@ QComboBox: Type[Any] = object
QCheckBox: Type[Any] = object
QHBoxLayout: Type[Any] = object
QFont: Type[Any] = object
QSettings: Type[Any] = object
def exec_dialog(dialog: Any) -> Any:
@@ -99,7 +100,8 @@ try:
QUrl as _QUrl,
QCoreApplication as _QCoreApplication,
Qt as _Qt,
QVariant as _QVariant
QVariant as _QVariant,
QSettings as _QSettings,
)
from qgis.PyQt.QtNetwork import (
QNetworkRequest as _QNetworkRequest,
@@ -140,6 +142,7 @@ try:
QVariant = _QVariant
QHBoxLayout = _QHBoxLayout
QFont = _QFont
QSettings = _QSettings
# ✅ QT6 ENUMS
YES = QMessageBox.StandardButton.Yes
NO = QMessageBox.StandardButton.No
@@ -199,7 +202,8 @@ except (ImportError, AttributeError):
QUrl as _QUrl,
QCoreApplication as _QCoreApplication,
Qt as _Qt,
QVariant as _QVariant
QVariant as _QVariant,
QSettings as _QSettings,
)
from PyQt5.QtNetwork import (
QNetworkRequest as _QNetworkRequest,
@@ -238,6 +242,7 @@ except (ImportError, AttributeError):
QVariant = _QVariant
QHBoxLayout= _QHBoxLayout
QFont = _QFont
QSettings = _QSettings
# ✅ PYQT5 ENUMS
YES = QMessageBox.Yes
@@ -560,6 +565,31 @@ except (ImportError, AttributeError):
pass
QHBoxLayout = _MockQHBoxLayout
class _MockQSettings:
def __init__(self, *args, **kwargs):
self._data: dict = {}
self._group = ""
def beginGroup(self, group: str) -> None:
self._group = group
def endGroup(self) -> None:
self._group = ""
def setValue(self, key: str, value: Any) -> None:
full = "%s/%s" % (self._group, key) if self._group else key
self._data[full] = value
def value(self, key: str, default: Any = "") -> Any:
full = "%s/%s" % (self._group, key) if self._group else key
return self._data.get(full, default)
def remove(self, key: str) -> None:
full = "%s/%s" % (self._group, key) if self._group else key
self._data.pop(full, None)
QSettings = _MockQSettings
class _MockQCheckBox:
def __init__(self, text: str = "", *args, **kwargs):
self._text = text