diff --git a/changelog.txt b/changelog.txt index 45632d3..f3c3d0d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,8 @@ --- +Version 26.5.2-testing: +--- +Version 26.5.1-testing: +--- Version 26.5.1-unstable: --- Version 26.4.8-unstable: diff --git a/functions/dialog_wrapper.py b/functions/dialog_wrapper.py index 170bbc0..57bd248 100644 --- a/functions/dialog_wrapper.py +++ b/functions/dialog_wrapper.py @@ -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 + diff --git a/functions/ly_style_wrapper.py b/functions/ly_style_wrapper.py index ad0221a..a3b9a6c 100644 --- a/functions/ly_style_wrapper.py +++ b/functions/ly_style_wrapper.py @@ -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. diff --git a/functions/qgiscore_wrapper.py b/functions/qgiscore_wrapper.py index 8387381..91cc267 100644 --- a/functions/qgiscore_wrapper.py +++ b/functions/qgiscore_wrapper.py @@ -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 # --------------------------------------------------------- diff --git a/functions/qt_wrapper.py b/functions/qt_wrapper.py index 7dcb007..250aa2c 100644 --- a/functions/qt_wrapper.py +++ b/functions/qt_wrapper.py @@ -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 diff --git a/metadata.txt b/metadata.txt index 076200f..1a37a40 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,11 +1,11 @@ [general] -version=26.5.1-unstable +version=26.5.2-testing name=LNO Sachsen | Plugin Basisfunktionen description=Plugin mit Basisfunktionen author=Daniel Helbig, Michael Otto homepage=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_SN_Basis tracker=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_SN_Basis/issues -repository=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_SN_Basis/src/branch/unstable/ +repository=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_SN_Basis/src/branch/testing/ QGISMaximumVersion=4.99 qgisMinimumVersion=3.40 experimental=true