Seitenleiste statt Werkzeugleiste
Erik, 08.09.2026: "ich will keine Toolbar, sondern so eine Seitenleiste auf der rechten Seite." - vln_karten/panel.py (neu): andockbares QDockWidget, rechts angedockt, mit den Abschnitten Anmeldung, Verfahren, OGC API und Klassisch (GeoJSON) sowie einer Statuszeile. Reine Oberflaeche; Klicks gehen als Rueckrufe ans Plugin. - Verfahrensliste bekommt ein Suchfeld (Hausregel: Auswahllisten immer mit Filter). Die Auswahl bleibt beim Filtern erhalten, "kein Treffer" liefert kein VKZ. - Der Stil-Knopf hat jetzt ein Haekchen "als Vorlage fuer alle Verfahren"; die Umschalttaste wirkt weiterhin. - Abmelden ist ein eigener Knopf (verwirft den gespeicherten API-Key). - Menue Web -> VLN Karten -> "Seitenleiste anzeigen" blendet sie wieder ein. - tools/headless_panel_test.py: prueft Zustaende, Filter, Rueckrufe und rendert das Layout als PNG (QGIS 4.2 offscreen). Lauf am 08.09.2026: 17 Pruefungen, 0 Fehler, 285 Verfahren in der Liste. - README und metadata.txt (0.3.0) nachgezogen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -122,7 +122,7 @@ Verbandsmitarbeiter alle (`?vkz=` in `/ogc/collections` grenzt ein).
|
|||||||
|
|
||||||
### Mit dem Plugin
|
### Mit dem Plugin
|
||||||
|
|
||||||
Toolbar-Knopf **„Alle Layer (OGC API) laden"**: legt nach dem Login eine
|
Knopf **„Alle Layer laden"** in der Seitenleiste: legt nach dem Login eine
|
||||||
QGIS-Authentifizierungskonfiguration *API-Header* (`X-API-Key`) an — das Passwort
|
QGIS-Authentifizierungskonfiguration *API-Header* (`X-API-Key`) an — das Passwort
|
||||||
bleibt draußen —, holt `GET /ogc/collections?vkz=…` und hängt jede Sammlung als
|
bleibt draußen —, holt `GET /ogc/collections?vkz=…` und hängt jede Sammlung als
|
||||||
`OAPIF`-Layer in eine Gruppe „VLN <Verfahren>" (Untergruppen Vorerhebung /
|
`OAPIF`-Layer in eine Gruppe „VLN <Verfahren>" (Untergruppen Vorerhebung /
|
||||||
@@ -159,9 +159,10 @@ die Sammlung gespeicherten Stil, sonst die **Vorlage der Art** (`kas-*-flaeche`
|
|||||||
Karte alter Stand aller Verfahren), sonst einen erzeugten Standard — kategorisiert nach `typ`
|
Karte alter Stand aller Verfahren), sonst einen erzeugten Standard — kategorisiert nach `typ`
|
||||||
mit den Farben des Web-GIS (Wege dunkel, Wasser blau, Ökologie grün, Problemstellen rot).
|
mit den Farben des Web-GIS (Wege dunkel, Wasser blau, Ökologie grün, Problemstellen rot).
|
||||||
|
|
||||||
- **Plugin:** wendet den Stil beim Laden automatisch an. Toolbar-Knopf **„Stil des aktiven
|
- **Plugin:** wendet den Stil beim Laden automatisch an. Der Knopf **„Stil des aktiven
|
||||||
Layers speichern“** schreibt den in QGIS eingestellten Stil zurück; mit gedrückter
|
Layers speichern“** in der Seitenleiste schreibt den in QGIS eingestellten Stil zurück;
|
||||||
**Umschalttaste** als Vorlage für die ganze Art (alle Verfahren).
|
mit dem Häkchen **„als Vorlage für alle Verfahren“** (oder gedrückter Umschalttaste) gilt
|
||||||
|
er für die ganze Art.
|
||||||
- **Ohne Plugin:** Layer → Eigenschaften → Stil → *Stil laden* … bzw. die QML-Datei von der
|
- **Ohne Plugin:** Layer → Eigenschaften → Stil → *Stil laden* … bzw. die QML-Datei von der
|
||||||
URL holen. Zurückschreiben geht per `PUT` (z. B. `curl -T stil.qml -H "X-API-Key: …"`).
|
URL holen. Zurückschreiben geht per `PUT` (z. B. `curl -T stil.qml -H "X-API-Key: …"`).
|
||||||
- **Berechtigung:** Lesen darf jeder, der die Sammlung sieht. Speichern und Löschen nur
|
- **Berechtigung:** Lesen darf jeder, der die Sammlung sieht. Speichern und Löschen nur
|
||||||
@@ -226,8 +227,10 @@ erlebbar:
|
|||||||
- **Einmaliger Login** (E-Mail/Passwort) — der API-Key wird in den
|
- **Einmaliger Login** (E-Mail/Passwort) — der API-Key wird in den
|
||||||
QGIS-Einstellungen gespeichert und über QGIS-Neustarts hinweg
|
QGIS-Einstellungen gespeichert und über QGIS-Neustarts hinweg
|
||||||
wiederverwendet; bei 401/403 öffnet sich der Anmeldedialog erneut.
|
wiederverwendet; bei 401/403 öffnet sich der Anmeldedialog erneut.
|
||||||
- **Verfahrens-Auswahl in der Toolbar** (aus `GET /tgen`), gemerkt je
|
- **Seitenleiste rechts** (andockbar, seit 08.09.2026 anstelle der Werkzeugleiste):
|
||||||
QGIS-Projektdatei.
|
Anmeldung, Verfahrensliste aus `GET /tgen` mit Suchfeld, Lade- und Speicherknöpfe,
|
||||||
|
Statuszeile. Wieder einblenden über *Web → VLN Karten → Seitenleiste anzeigen*.
|
||||||
|
- **Verfahrens-Auswahl** gemerkt je QGIS-Projektdatei.
|
||||||
- **Buttons je Layer:** Verfahrensgebiet (`umringe`), Plan 41 (`p41`),
|
- **Buttons je Layer:** Verfahrensgebiet (`umringe`), Plan 41 (`p41`),
|
||||||
Karte alter Stand (`kas`), Wertermittlung (`we`) — vollständig
|
Karte alter Stand (`kas`), Wertermittlung (`we`) — vollständig
|
||||||
geladen über den Listen-Endpunkt mit Paging. Weitere Layer (z. B.
|
geladen über den Listen-Endpunkt mit Paging. Weitere Layer (z. B.
|
||||||
@@ -241,7 +244,8 @@ erlebbar:
|
|||||||
|
|
||||||
| Datei | Zweck |
|
| Datei | Zweck |
|
||||||
|---|---|
|
|---|---|
|
||||||
| [vln_karten/plugin.py](vln_karten/plugin.py) | Toolbar, Aktionen, Verfahrens-Auswahl, `DATASETS`-Registry |
|
| [vln_karten/plugin.py](vln_karten/plugin.py) | Ablaufsteuerung: Anmeldung, Laden, Speichern, Hochladen, `DATASETS`-Registry |
|
||||||
|
| [vln_karten/panel.py](vln_karten/panel.py) | Seitenleiste (Anmeldung, Verfahrensfilter, Knöpfe, Statuszeile) |
|
||||||
| [vln_karten/api_client.py](vln_karten/api_client.py) | HTTP-Client: Login, Paging-Loader, PUT, RFC-7807-Fehler |
|
| [vln_karten/api_client.py](vln_karten/api_client.py) | HTTP-Client: Login, Paging-Loader, PUT, RFC-7807-Fehler |
|
||||||
| [vln_karten/layer_manager.py](vln_karten/layer_manager.py) | GeoJSON ↔ Memory-Layer, Geometrie-Splitting/-Vereinigung |
|
| [vln_karten/layer_manager.py](vln_karten/layer_manager.py) | GeoJSON ↔ Memory-Layer, Geometrie-Splitting/-Vereinigung |
|
||||||
| [vln_karten/ogc_layers.py](vln_karten/ogc_layers.py) | OGC API Features: Auth-Konfiguration (API-Header), Sammlungen → OAPIF-Layer |
|
| [vln_karten/ogc_layers.py](vln_karten/ogc_layers.py) | OGC API Features: Auth-Konfiguration (API-Header), Sammlungen → OAPIF-Layer |
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Seitenleiste headless pruefen: Zustaende, Filter, Rueckrufe — und als PNG rendern.
|
||||||
|
|
||||||
|
Aufruf (macOS, QGIS 4.2 im Bundle; VLN_TOKEN = API-Key aus dem Login):
|
||||||
|
|
||||||
|
APP=/Applications/QGIS-final-4_2_0.app; LIB="$APP/Contents/Resources/python3.12"
|
||||||
|
QT_QPA_PLATFORM=offscreen PROJ_DATA="$APP/Contents/Resources/qgis/proj" \
|
||||||
|
PYTHONPATH="$LIB:$LIB/lib-dynload:$LIB/site-packages" \
|
||||||
|
"$APP/Contents/MacOS/python3.12" tools/headless_panel_test.py /tmp/panel.png
|
||||||
|
|
||||||
|
Holt die echte Verfahrensliste (GET /tgen) und prueft Zustaende, Filter und Rueckrufe der
|
||||||
|
Seitenleiste; das PNG zeigt das Layout ohne QGIS-Fenster.
|
||||||
|
"""
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from qgis.core import QgsApplication
|
||||||
|
|
||||||
|
app = QgsApplication([], True)
|
||||||
|
QgsApplication.setPrefixPath("/Applications/QGIS-final-4_2_0.app/Contents/MacOS", True)
|
||||||
|
QgsApplication.setPluginPath("/Applications/QGIS-final-4_2_0.app/Contents/PlugIns/qgis")
|
||||||
|
app.initQgis()
|
||||||
|
from qgis.PyQt.QtNetwork import QNetworkProxyFactory
|
||||||
|
QNetworkProxyFactory.setUseSystemConfiguration(False)
|
||||||
|
from qgis.PyQt.QtCore import QSize
|
||||||
|
|
||||||
|
from vln_karten.panel import VlnPanel
|
||||||
|
from vln_karten.plugin import DATASETS
|
||||||
|
from vln_karten.api_client import KartenApiClient
|
||||||
|
|
||||||
|
ZIEL = sys.argv[1]
|
||||||
|
FEHLER, GERUFEN = [], []
|
||||||
|
|
||||||
|
|
||||||
|
def pruefe(bed, text):
|
||||||
|
print((" OK " if bed else " FEHL ") + text)
|
||||||
|
if not bed:
|
||||||
|
FEHLER.append(text)
|
||||||
|
|
||||||
|
|
||||||
|
rueckrufe = {name: (lambda *a, n=name: GERUFEN.append((n, a)))
|
||||||
|
for name in ("login", "logout", "verfahren_gewaehlt", "load_ogc",
|
||||||
|
"save_style", "load_dataset", "upload")}
|
||||||
|
panel = VlnPanel(None, callbacks=rueckrufe,
|
||||||
|
datensaetze=[(k, d["label"]) for k, d in DATASETS.items()])
|
||||||
|
|
||||||
|
print("== abgemeldet ==")
|
||||||
|
pruefe(panel.login_knopf.isVisible() or not panel.logout_knopf.isVisibleTo(panel),
|
||||||
|
"Anmelde-Knopf sichtbar, Abmelden verborgen")
|
||||||
|
pruefe(not panel.ogc_knopf.isEnabled() and not panel.combo.isEnabled(),
|
||||||
|
"Knoepfe und Auswahl gesperrt")
|
||||||
|
|
||||||
|
print("== angemeldet, echte Verfahrensliste ==")
|
||||||
|
client = KartenApiClient(api_key=os.environ["VLN_TOKEN"])
|
||||||
|
verfahren = client.get_verfahren()
|
||||||
|
panel.set_angemeldet(True, "erik.sefkow@…")
|
||||||
|
panel.set_verfahren(verfahren)
|
||||||
|
pruefe(panel.ogc_knopf.isEnabled(), "Knoepfe frei")
|
||||||
|
pruefe(panel.anzahl_eintraege() == len(verfahren) + 1,
|
||||||
|
"Liste gefuellt: %d Verfahren + Platzhalter" % len(verfahren))
|
||||||
|
|
||||||
|
print("== Filter ==")
|
||||||
|
panel.filter_feld.setText("berb")
|
||||||
|
treffer = [panel.combo.itemText(i) for i in range(panel.combo.count())][1:]
|
||||||
|
pruefe(treffer and all("berb" in t.lower() for t in treffer),
|
||||||
|
"gefiltert auf %d Treffer: %s" % (len(treffer), ", ".join(treffer)[:70]))
|
||||||
|
panel.filter_feld.setText("gibtsnicht")
|
||||||
|
pruefe(panel.combo.count() == 1 and panel.combo.currentData() is None,
|
||||||
|
"kein Treffer -> Platzhalter, kein VKZ")
|
||||||
|
panel.filter_feld.clear()
|
||||||
|
pruefe(panel.anzahl_eintraege() == len(verfahren) + 1, "Filter geleert -> Liste wieder voll")
|
||||||
|
|
||||||
|
print("== Auswahl + Rueckruf ==")
|
||||||
|
panel.waehle_vkz("27027")
|
||||||
|
pruefe(panel.aktuelles_vkz() == "27027", "Wiederherstellen ohne Rueckruf: %s" % panel.aktuelles_vkz())
|
||||||
|
pruefe(not any(n == "verfahren_gewaehlt" for n, _ in GERUFEN), "waehle_vkz loest keinen Rueckruf aus")
|
||||||
|
panel.combo.setCurrentIndex(panel.combo.findData(verfahren[0]["vkz"]))
|
||||||
|
pruefe(any(n == "verfahren_gewaehlt" for n, _ in GERUFEN), "Nutzerauswahl meldet ans Plugin")
|
||||||
|
panel.waehle_vkz("27027")
|
||||||
|
|
||||||
|
print("== Knoepfe ==")
|
||||||
|
panel.ogc_knopf.click(); panel.stil_knopf.click(); panel.upload_knopf.click()
|
||||||
|
panel.datensatz_knoepfe[0].click()
|
||||||
|
namen = [n for n, _ in GERUFEN]
|
||||||
|
for erwartet in ("load_ogc", "save_style", "upload", "load_dataset"):
|
||||||
|
pruefe(erwartet in namen, "Rueckruf %s" % erwartet)
|
||||||
|
ds = [a for n, a in GERUFEN if n == "load_dataset"]
|
||||||
|
pruefe(ds and ds[0][0] == list(DATASETS)[0], "Datensatz-Knopf uebergibt Schluessel: %s" % (ds[0] if ds else "-"))
|
||||||
|
pruefe(not panel.als_vorlage(), "Vorlage-Haken standardmaessig aus")
|
||||||
|
panel.vorlage_haken.setChecked(True)
|
||||||
|
pruefe(panel.als_vorlage(), "Vorlage-Haken wirkt")
|
||||||
|
panel.vorlage_haken.setChecked(False)
|
||||||
|
|
||||||
|
print("== Bild ==")
|
||||||
|
panel.set_status("Stil gespeichert: kas-27027-flaeche (Vorlage)")
|
||||||
|
panel.widget().resize(QSize(300, 760))
|
||||||
|
panel.widget().grab().save(ZIEL)
|
||||||
|
print(" ", ZIEL, os.path.getsize(ZIEL), "Bytes")
|
||||||
|
|
||||||
|
app.exitQgis()
|
||||||
|
print("== Ergebnis: %d Fehler ==" % len(FEHLER))
|
||||||
|
for f in FEHLER:
|
||||||
|
print(" -", f)
|
||||||
|
sys.exit(1 if FEHLER else 0)
|
||||||
@@ -5,7 +5,7 @@ qgisMaximumVersion=4.99
|
|||||||
supportsQt6=True
|
supportsQt6=True
|
||||||
description=Beispiel: Karten-API des VLN Managers aus QGIS ansteuern — OGC API Features (alle Layer editierbar, Vorerhebung bis Maßnahmenblatt) und der klassische GeoJSON-Weg.
|
description=Beispiel: Karten-API des VLN Managers aus QGIS ansteuern — OGC API Features (alle Layer editierbar, Vorerhebung bis Maßnahmenblatt) und der klassische GeoJSON-Weg.
|
||||||
about=Referenz-Implementierung für die VLN-Manager-Karten-API: Login (mail/password -> userauth-API-Key), vollständiges Laden je VKZ mit Paging (EPSG:25833), Splitten gemischter Geometrietypen in Memory-Layer und vereintes Zurückschreiben per PUT.
|
about=Referenz-Implementierung für die VLN-Manager-Karten-API: Login (mail/password -> userauth-API-Key), vollständiges Laden je VKZ mit Paging (EPSG:25833), Splitten gemischter Geometrietypen in Memory-Layer und vereintes Zurückschreiben per PUT.
|
||||||
version=0.2.0
|
version=0.3.0
|
||||||
author=Erik
|
author=Erik
|
||||||
email=phpwelt@gmail.com
|
email=phpwelt@gmail.com
|
||||||
tracker=https://entwicklung.flurneuordnung-sachsen.de/VLN_SN/API_Karte_QGISDemo/issues
|
tracker=https://entwicklung.flurneuordnung-sachsen.de/VLN_SN/API_Karte_QGISDemo/issues
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Seitenleiste (andockbares Panel) des Plugins — rechts in QGIS.
|
||||||
|
|
||||||
|
Ersetzt die frühere Werkzeugleiste (Erik, 08.09.2026: „keine Toolbar, sondern eine
|
||||||
|
Seitenleiste auf der rechten Seite"). Das Panel ist reine Oberfläche: es hält keinen
|
||||||
|
Zustand der Fachlogik, sondern meldet Klicks über Rückrufe (``callbacks``) an
|
||||||
|
``VlnKartenPlugin`` und wird von dort über ``set_angemeldet`` / ``set_verfahren``
|
||||||
|
umgeschaltet.
|
||||||
|
|
||||||
|
Aufbau von oben nach unten:
|
||||||
|
1. Anmeldung — Status + Knopf (Anmelden/Abmelden)
|
||||||
|
2. Verfahren — Suchfeld (filtert) + Auswahlliste
|
||||||
|
3. OGC API — alle Layer laden, Stil speichern (mit Häkchen „für alle Verfahren")
|
||||||
|
4. Klassisch — die einzelnen GeoJSON-Datensätze, aktiven Layer hochladen
|
||||||
|
5. Statuszeile
|
||||||
|
"""
|
||||||
|
|
||||||
|
from qgis.PyQt.QtCore import Qt
|
||||||
|
from qgis.PyQt.QtWidgets import (QCheckBox, QComboBox, QDockWidget, QFrame, QGroupBox,
|
||||||
|
QHBoxLayout, QLabel, QLineEdit, QPushButton, QSizePolicy,
|
||||||
|
QVBoxLayout, QWidget)
|
||||||
|
|
||||||
|
PANEL_TITEL = "VLN Karten"
|
||||||
|
PANEL_OBJEKT = "VlnKartenPanel"
|
||||||
|
|
||||||
|
|
||||||
|
def _klein(widget):
|
||||||
|
"""Hinweistexte eine Stufe kleiner und gedämpft — wie in QGIS-Panels üblich.
|
||||||
|
|
||||||
|
Die Farbe kommt aus der Palette (``palette(mid)``), damit der Text in hellen wie
|
||||||
|
dunklen QGIS-Themen lesbar bleibt; ``setEnabled(False)`` wäre dafür der falsche Weg,
|
||||||
|
weil es das Widget zugleich unbedienbar macht.
|
||||||
|
"""
|
||||||
|
schrift = widget.font()
|
||||||
|
schrift.setPointSizeF(max(8.0, schrift.pointSizeF() - 1.0))
|
||||||
|
widget.setFont(schrift)
|
||||||
|
widget.setWordWrap(True)
|
||||||
|
widget.setStyleSheet("color: palette(mid);")
|
||||||
|
return widget
|
||||||
|
|
||||||
|
|
||||||
|
class VlnPanel(QDockWidget):
|
||||||
|
"""Die Seitenleiste. ``callbacks`` bündelt die Rückrufe ins Plugin."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None, callbacks=None, datensaetze=()):
|
||||||
|
super().__init__(PANEL_TITEL, parent)
|
||||||
|
self.setObjectName(PANEL_OBJEKT)
|
||||||
|
self.setAllowedAreas(Qt.DockWidgetArea.RightDockWidgetArea
|
||||||
|
| Qt.DockWidgetArea.LeftDockWidgetArea)
|
||||||
|
self._cb = callbacks or {}
|
||||||
|
self._verfahren = [] # [(beschriftung, vkz)] — ungefiltert
|
||||||
|
self._stumm = False # unterdrückt Signale beim programmatischen Füllen
|
||||||
|
|
||||||
|
inhalt = QWidget()
|
||||||
|
aussen = QVBoxLayout(inhalt)
|
||||||
|
aussen.setContentsMargins(8, 8, 8, 8)
|
||||||
|
aussen.setSpacing(10)
|
||||||
|
|
||||||
|
aussen.addWidget(self._bau_anmeldung())
|
||||||
|
aussen.addWidget(self._bau_verfahren())
|
||||||
|
aussen.addWidget(self._bau_ogc())
|
||||||
|
aussen.addWidget(self._bau_klassisch(datensaetze))
|
||||||
|
aussen.addStretch(1)
|
||||||
|
aussen.addWidget(self._bau_status())
|
||||||
|
|
||||||
|
inhalt.setMinimumWidth(260)
|
||||||
|
self.setWidget(inhalt)
|
||||||
|
self.set_angemeldet(False)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Aufbau
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _bau_anmeldung(self):
|
||||||
|
kasten = QGroupBox("Anmeldung")
|
||||||
|
lage = QVBoxLayout(kasten)
|
||||||
|
lage.setSpacing(6)
|
||||||
|
self.status_label = QLabel("Nicht angemeldet")
|
||||||
|
self.status_label.setWordWrap(True)
|
||||||
|
self.login_knopf = QPushButton("Anmelden …")
|
||||||
|
self.login_knopf.clicked.connect(lambda: self._ruf("login"))
|
||||||
|
self.logout_knopf = QPushButton("Abmelden")
|
||||||
|
self.logout_knopf.clicked.connect(lambda: self._ruf("logout"))
|
||||||
|
zeile = QHBoxLayout()
|
||||||
|
zeile.addWidget(self.login_knopf)
|
||||||
|
zeile.addWidget(self.logout_knopf)
|
||||||
|
lage.addWidget(self.status_label)
|
||||||
|
lage.addLayout(zeile)
|
||||||
|
return kasten
|
||||||
|
|
||||||
|
def _bau_verfahren(self):
|
||||||
|
kasten = QGroupBox("Verfahren")
|
||||||
|
lage = QVBoxLayout(kasten)
|
||||||
|
lage.setSpacing(6)
|
||||||
|
self.filter_feld = QLineEdit()
|
||||||
|
self.filter_feld.setPlaceholderText("filtern (Name oder Kennzeichen) …")
|
||||||
|
self.filter_feld.setClearButtonEnabled(True)
|
||||||
|
self.filter_feld.textChanged.connect(self._filter_geaendert)
|
||||||
|
self.combo = QComboBox()
|
||||||
|
self.combo.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
|
self.combo.setToolTip("Teilnehmergemeinschaft, für die geladen und gespeichert wird")
|
||||||
|
self.combo.currentIndexChanged.connect(self._auswahl_geaendert)
|
||||||
|
lage.addWidget(self.filter_feld)
|
||||||
|
lage.addWidget(self.combo)
|
||||||
|
return kasten
|
||||||
|
|
||||||
|
def _bau_ogc(self):
|
||||||
|
kasten = QGroupBox("OGC API (empfohlen)")
|
||||||
|
lage = QVBoxLayout(kasten)
|
||||||
|
lage.setSpacing(6)
|
||||||
|
self.ogc_knopf = QPushButton("Alle Layer laden")
|
||||||
|
self.ogc_knopf.setToolTip("Verfahrenslayer, Vorerhebung und Maßnahmenblätter als "
|
||||||
|
"editierbare OAPIF-Layer; QGIS schreibt Änderungen direkt zurück")
|
||||||
|
self.ogc_knopf.clicked.connect(lambda: self._ruf("load_ogc"))
|
||||||
|
self.stil_knopf = QPushButton("Stil des aktiven Layers speichern")
|
||||||
|
self.stil_knopf.setToolTip("Braucht die Gruppe MODUL_KARTENSTIL (Verbandsmitarbeiter "
|
||||||
|
"immer)")
|
||||||
|
self.stil_knopf.clicked.connect(lambda: self._ruf("save_style"))
|
||||||
|
self.vorlage_haken = QCheckBox("als Vorlage für alle Verfahren")
|
||||||
|
self.vorlage_haken.setToolTip("Der Stil gilt dann für diese Art in jedem Verfahren, "
|
||||||
|
"z. B. für jede Karte alter Stand")
|
||||||
|
lage.addWidget(self.ogc_knopf)
|
||||||
|
lage.addWidget(self.stil_knopf)
|
||||||
|
lage.addWidget(self.vorlage_haken)
|
||||||
|
return kasten
|
||||||
|
|
||||||
|
def _bau_klassisch(self, datensaetze):
|
||||||
|
kasten = QGroupBox("Klassisch (GeoJSON)")
|
||||||
|
lage = QVBoxLayout(kasten)
|
||||||
|
lage.setSpacing(6)
|
||||||
|
lage.addWidget(_klein(QLabel("Lädt eine Kopie; Änderungen gehen erst mit "
|
||||||
|
"„Aktiven Layer hochladen“ zurück.")))
|
||||||
|
self.datensatz_knoepfe = []
|
||||||
|
for schluessel, beschriftung in datensaetze:
|
||||||
|
knopf = QPushButton(beschriftung)
|
||||||
|
knopf.clicked.connect(lambda _=False, s=schluessel: self._ruf("load_dataset", s))
|
||||||
|
lage.addWidget(knopf)
|
||||||
|
self.datensatz_knoepfe.append(knopf)
|
||||||
|
linie = QFrame()
|
||||||
|
linie.setFrameShape(QFrame.Shape.HLine)
|
||||||
|
linie.setFrameShadow(QFrame.Shadow.Sunken)
|
||||||
|
lage.addWidget(linie)
|
||||||
|
self.upload_knopf = QPushButton("Aktiven Layer hochladen")
|
||||||
|
self.upload_knopf.clicked.connect(lambda: self._ruf("upload"))
|
||||||
|
lage.addWidget(self.upload_knopf)
|
||||||
|
return kasten
|
||||||
|
|
||||||
|
def _bau_status(self):
|
||||||
|
self.status_zeile = _klein(QLabel(""))
|
||||||
|
self.status_zeile.setToolTip("letzte Aktion")
|
||||||
|
return self.status_zeile
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Zustand (vom Plugin gesetzt)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def set_angemeldet(self, angemeldet, wer=None):
|
||||||
|
self.status_label.setText(("Angemeldet: %s" % wer) if angemeldet and wer
|
||||||
|
else ("Angemeldet" if angemeldet else "Nicht angemeldet"))
|
||||||
|
self.login_knopf.setVisible(not angemeldet)
|
||||||
|
self.logout_knopf.setVisible(angemeldet)
|
||||||
|
for widget in (self.filter_feld, self.combo, self.ogc_knopf, self.stil_knopf,
|
||||||
|
self.vorlage_haken, self.upload_knopf, *self.datensatz_knoepfe):
|
||||||
|
widget.setEnabled(angemeldet)
|
||||||
|
if not angemeldet:
|
||||||
|
self._stumm = True
|
||||||
|
self._verfahren = []
|
||||||
|
self.combo.clear()
|
||||||
|
self.combo.addItem("— bitte anmelden —", None)
|
||||||
|
self.filter_feld.clear()
|
||||||
|
self._stumm = False
|
||||||
|
|
||||||
|
def set_verfahren(self, verfahren):
|
||||||
|
"""``verfahren``: Liste von ``{"vkz":…, "name":…}`` aus der API."""
|
||||||
|
self._verfahren = [
|
||||||
|
("%s — %s" % (v["vkz"], v["name"]) if v.get("name") else str(v["vkz"]), v["vkz"])
|
||||||
|
for v in verfahren
|
||||||
|
]
|
||||||
|
self._liste_fuellen()
|
||||||
|
|
||||||
|
def set_status(self, text):
|
||||||
|
self.status_zeile.setText(text or "")
|
||||||
|
|
||||||
|
def aktuelles_vkz(self):
|
||||||
|
return self.combo.currentData()
|
||||||
|
|
||||||
|
def aktueller_text(self):
|
||||||
|
return self.combo.currentText()
|
||||||
|
|
||||||
|
def als_vorlage(self):
|
||||||
|
return self.vorlage_haken.isChecked()
|
||||||
|
|
||||||
|
def waehle_vkz(self, vkz):
|
||||||
|
"""Setzt die Auswahl ohne Rückruf (Wiederherstellen aus der Projektdatei)."""
|
||||||
|
self._stumm = True
|
||||||
|
index = self.combo.findData(vkz) if vkz else -1
|
||||||
|
self.combo.setCurrentIndex(index if index >= 0 else 0)
|
||||||
|
self._stumm = False
|
||||||
|
|
||||||
|
def anzahl_eintraege(self):
|
||||||
|
return self.combo.count()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Innenleben
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _liste_fuellen(self, muster=""):
|
||||||
|
"""Füllt die Auswahlliste, gefiltert; die bisherige Auswahl bleibt wenn möglich."""
|
||||||
|
vorher = self.combo.currentData()
|
||||||
|
muster = (muster or "").strip().lower()
|
||||||
|
treffer = [(t, v) for t, v in self._verfahren if muster in t.lower()]
|
||||||
|
self._stumm = True
|
||||||
|
self.combo.clear()
|
||||||
|
if not self._verfahren:
|
||||||
|
self.combo.addItem("— keine Verfahren —", None)
|
||||||
|
elif not treffer:
|
||||||
|
self.combo.addItem("— kein Treffer —", None)
|
||||||
|
else:
|
||||||
|
self.combo.addItem("— Verfahren wählen —", None)
|
||||||
|
for beschriftung, vkz in treffer:
|
||||||
|
self.combo.addItem(beschriftung, vkz)
|
||||||
|
index = self.combo.findData(vorher) if vorher else -1
|
||||||
|
self.combo.setCurrentIndex(index if index >= 0 else 0)
|
||||||
|
self._stumm = False
|
||||||
|
if index < 0 and vorher:
|
||||||
|
self._auswahl_geaendert(0)
|
||||||
|
|
||||||
|
def _filter_geaendert(self, text):
|
||||||
|
self._liste_fuellen(text)
|
||||||
|
|
||||||
|
def _auswahl_geaendert(self, _index):
|
||||||
|
if not self._stumm:
|
||||||
|
self._ruf("verfahren_gewaehlt", self.combo.currentData())
|
||||||
|
|
||||||
|
def _ruf(self, name, *args):
|
||||||
|
rueckruf = self._cb.get(name)
|
||||||
|
if rueckruf:
|
||||||
|
rueckruf(*args)
|
||||||
+73
-93
@@ -4,20 +4,20 @@ Ablauf:
|
|||||||
- Der API-Key wird nach dem ersten Login in den QGIS-Einstellungen
|
- Der API-Key wird nach dem ersten Login in den QGIS-Einstellungen
|
||||||
gespeichert und bei jedem QGIS-Start wiederverwendet (kein erneuter
|
gespeichert und bei jedem QGIS-Start wiederverwendet (kein erneuter
|
||||||
Login nötig, solange der Key gültig ist).
|
Login nötig, solange der Key gültig ist).
|
||||||
- Eine Toolbar-Auswahlliste zeigt alle Verfahren (TGs) aus GET /tgen.
|
- Die Bedienung sitzt in einer andockbaren Seitenleiste rechts (``panel.py``,
|
||||||
Das gewählte Verfahren wird in der Projektdatei gemerkt und beim
|
seit 08.09.2026 anstelle der früheren Werkzeugleiste). Sie zeigt Anmeldung,
|
||||||
Öffnen des Projekts wiederhergestellt.
|
Verfahrensliste aus GET /tgen mit Suchfeld sowie die Knöpfe zum Laden,
|
||||||
- Buttons laden Verfahrensgebiet / Plan 41 / Karte alter Stand für das
|
Speichern und Hochladen.
|
||||||
gewählte Verfahren bzw. laden den aktiven Layer zur API hoch.
|
- Das gewählte Verfahren wird in der Projektdatei gemerkt und beim Öffnen
|
||||||
|
des Projekts wiederhergestellt.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from functools import partial
|
from qgis.PyQt.QtCore import QSettings, Qt, QTimer
|
||||||
|
from qgis.PyQt.QtWidgets import QAction, QDialog, QMessageBox
|
||||||
from qgis.PyQt.QtCore import QSettings, QTimer
|
|
||||||
from qgis.PyQt.QtWidgets import QAction, QComboBox, QDialog, QMessageBox
|
|
||||||
from qgis.core import Qgis, QgsDataSourceUri, QgsProject
|
from qgis.core import Qgis, QgsDataSourceUri, QgsProject
|
||||||
|
|
||||||
from .api_client import ApiError, AuthError, KartenApiClient
|
from .api_client import ApiError, AuthError, KartenApiClient
|
||||||
|
from .panel import VlnPanel
|
||||||
from .layer_manager import (
|
from .layer_manager import (
|
||||||
PROP_DATASET,
|
PROP_DATASET,
|
||||||
PROP_VERFAHREN,
|
PROP_VERFAHREN,
|
||||||
@@ -70,48 +70,39 @@ class VlnKartenPlugin:
|
|||||||
self.iface = iface
|
self.iface = iface
|
||||||
self.client = KartenApiClient()
|
self.client = KartenApiClient()
|
||||||
self.actions = []
|
self.actions = []
|
||||||
self.toolbar = None
|
self.panel = None
|
||||||
self.combo = None
|
|
||||||
# Unterdrückt das Mitschreiben in die Projektdatei, während die
|
|
||||||
# Auswahl programmatisch gesetzt wird.
|
|
||||||
self._restoring = False
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Plugin-Lebenszyklus
|
# Plugin-Lebenszyklus
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def initGui(self):
|
def initGui(self):
|
||||||
self.toolbar = self.iface.addToolBar("VLN Karten")
|
# Seitenleiste statt Werkzeugleiste (Erik, 08.09.2026). Die OGC-Knöpfe stehen oben:
|
||||||
self.toolbar.setObjectName("VlnKartenToolbar")
|
# sie laden native, editierbare OAPIF-Layer, die QGIS selbst zurückschreibt (Part 4).
|
||||||
|
# Die GeoJSON-Knöpfe darunter sind der klassische Weg (Kopie laden, später hochladen).
|
||||||
self.login_action = self._add_action("Anmelden …", self.run_login)
|
self.panel = VlnPanel(
|
||||||
|
self.iface.mainWindow(),
|
||||||
self.combo = QComboBox()
|
callbacks={
|
||||||
self.combo.setMinimumWidth(280)
|
"login": self.run_login,
|
||||||
self.combo.setToolTip("Verfahren (Teilnehmergemeinschaft) auswählen")
|
"logout": self.run_logout,
|
||||||
self.combo.currentIndexChanged.connect(self.on_verfahren_changed)
|
"verfahren_gewaehlt": self.on_verfahren_changed,
|
||||||
self.toolbar.addWidget(self.combo)
|
"load_ogc": self.run_load_ogc,
|
||||||
|
"save_style": self.run_save_style,
|
||||||
self.dataset_actions = []
|
"load_dataset": self.run_load_dataset,
|
||||||
# OGC API Features (seit 07.09.2026) — der empfohlene Weg, darum ganz vorn: alle
|
"upload": self.run_upload_active_layer,
|
||||||
# Sammlungen des Verfahrens als native, editierbare OAPIF-Layer; QGIS schreibt jede
|
},
|
||||||
# Änderung selbst zurück (Part 4). Die GeoJSON-Knöpfe darunter sind der klassische Weg.
|
datensaetze=[(key, dataset["label"]) for key, dataset in DATASETS.items()],
|
||||||
self.dataset_actions.append(
|
|
||||||
self._add_action("Alle Layer (OGC API) laden", self.run_load_ogc)
|
|
||||||
)
|
|
||||||
self.dataset_actions.append(
|
|
||||||
self._add_action("Stil des aktiven Layers speichern", self.run_save_style)
|
|
||||||
)
|
|
||||||
for key, dataset in DATASETS.items():
|
|
||||||
self.dataset_actions.append(
|
|
||||||
self._add_action(
|
|
||||||
"%s laden" % dataset["label"],
|
|
||||||
partial(self.run_load_dataset, key),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.dataset_actions.append(
|
|
||||||
self._add_action("Aktiven Layer hochladen", self.run_upload_active_layer)
|
|
||||||
)
|
)
|
||||||
|
self.iface.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, self.panel)
|
||||||
|
|
||||||
|
# Menüeintrag zum Wiedereinblenden, falls die Leiste geschlossen wurde.
|
||||||
|
self.panel_action = QAction("Seitenleiste anzeigen", self.iface.mainWindow())
|
||||||
|
self.panel_action.setCheckable(True)
|
||||||
|
self.panel_action.setChecked(True)
|
||||||
|
self.panel_action.toggled.connect(self.panel.setVisible)
|
||||||
|
self.panel.visibilityChanged.connect(self.panel_action.setChecked)
|
||||||
|
self.iface.addPluginToWebMenu(MENU_TITLE, self.panel_action)
|
||||||
|
self.actions.append(self.panel_action)
|
||||||
|
|
||||||
self.iface.projectRead.connect(self.restore_project_selection)
|
self.iface.projectRead.connect(self.restore_project_selection)
|
||||||
self.iface.newProjectCreated.connect(self.restore_project_selection)
|
self.iface.newProjectCreated.connect(self.restore_project_selection)
|
||||||
@@ -141,18 +132,10 @@ class VlnKartenPlugin:
|
|||||||
for action in self.actions:
|
for action in self.actions:
|
||||||
self.iface.removePluginWebMenu(MENU_TITLE, action)
|
self.iface.removePluginWebMenu(MENU_TITLE, action)
|
||||||
self.actions = []
|
self.actions = []
|
||||||
if self.toolbar is not None:
|
if self.panel is not None:
|
||||||
self.toolbar.deleteLater()
|
self.iface.removeDockWidget(self.panel)
|
||||||
self.toolbar = None
|
self.panel.deleteLater()
|
||||||
self.combo = None
|
self.panel = None
|
||||||
|
|
||||||
def _add_action(self, text, callback):
|
|
||||||
action = QAction(text, self.iface.mainWindow())
|
|
||||||
action.triggered.connect(callback)
|
|
||||||
self.iface.addPluginToWebMenu(MENU_TITLE, action)
|
|
||||||
self.toolbar.addAction(action)
|
|
||||||
self.actions.append(action)
|
|
||||||
return action
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Anmeldung & Verfahrensliste
|
# Anmeldung & Verfahrensliste
|
||||||
@@ -205,20 +188,9 @@ class VlnKartenPlugin:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._restoring = True
|
self.panel.set_angemeldet(True, self.client.mail or "API-Key")
|
||||||
self.combo.clear()
|
self.panel.set_verfahren(verfahren)
|
||||||
self.combo.addItem("— Verfahren wählen —", None)
|
self.panel.set_status("%d Verfahren geladen." % len(verfahren))
|
||||||
for v in verfahren:
|
|
||||||
label = "%s — %s" % (v["vkz"], v["name"]) if v["name"] else v["vkz"]
|
|
||||||
self.combo.addItem(label, v["vkz"])
|
|
||||||
self.combo.setEnabled(True)
|
|
||||||
self._restoring = False
|
|
||||||
|
|
||||||
self.login_action.setText(
|
|
||||||
"Angemeldet: %s" % (self.client.mail or "API-Key")
|
|
||||||
)
|
|
||||||
for action in self.dataset_actions:
|
|
||||||
action.setEnabled(True)
|
|
||||||
self.restore_project_selection()
|
self.restore_project_selection()
|
||||||
|
|
||||||
def _handle_session_expired(self):
|
def _handle_session_expired(self):
|
||||||
@@ -236,41 +208,43 @@ class VlnKartenPlugin:
|
|||||||
self.run_login()
|
self.run_login()
|
||||||
|
|
||||||
def _set_logged_out_ui(self):
|
def _set_logged_out_ui(self):
|
||||||
self.login_action.setText("Anmelden …")
|
if self.panel is not None:
|
||||||
self._restoring = True
|
self.panel.set_angemeldet(False)
|
||||||
self.combo.clear()
|
self.panel.set_status("")
|
||||||
self.combo.addItem("— bitte anmelden —", None)
|
|
||||||
self.combo.setEnabled(False)
|
def run_logout(self):
|
||||||
self._restoring = False
|
"""Abmelden: gespeicherten API-Key verwerfen, Oberfläche zurücksetzen."""
|
||||||
for action in self.dataset_actions:
|
settings = QSettings()
|
||||||
action.setEnabled(False)
|
settings.beginGroup(SETTINGS_GROUP)
|
||||||
|
settings.remove("api_key")
|
||||||
|
settings.endGroup()
|
||||||
|
self.client.logout()
|
||||||
|
self.client = KartenApiClient()
|
||||||
|
self._set_logged_out_ui()
|
||||||
|
self.iface.messageBar().pushMessage(
|
||||||
|
"VLN Karten", "Abgemeldet — der gespeicherte API-Key wurde entfernt.",
|
||||||
|
level=Qgis.Info, duration=5)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Verfahrens-Auswahl <-> Projektdatei
|
# Verfahrens-Auswahl <-> Projektdatei
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def on_verfahren_changed(self, index):
|
def on_verfahren_changed(self, vkz):
|
||||||
if self._restoring or self.combo is None:
|
|
||||||
return
|
|
||||||
vkz = self.combo.currentData()
|
|
||||||
if vkz:
|
if vkz:
|
||||||
QgsProject.instance().writeEntry(
|
QgsProject.instance().writeEntry(
|
||||||
PROJECT_SCOPE, PROJECT_KEY_VKZ, vkz
|
PROJECT_SCOPE, PROJECT_KEY_VKZ, vkz
|
||||||
)
|
)
|
||||||
|
|
||||||
def restore_project_selection(self):
|
def restore_project_selection(self):
|
||||||
if self.combo is None or self.combo.count() == 0:
|
if self.panel is None or self.panel.anzahl_eintraege() == 0:
|
||||||
return
|
return
|
||||||
vkz, _ok = QgsProject.instance().readEntry(
|
vkz, _ok = QgsProject.instance().readEntry(
|
||||||
PROJECT_SCOPE, PROJECT_KEY_VKZ, ""
|
PROJECT_SCOPE, PROJECT_KEY_VKZ, ""
|
||||||
)
|
)
|
||||||
self._restoring = True
|
self.panel.waehle_vkz(vkz)
|
||||||
index = self.combo.findData(vkz) if vkz else -1
|
|
||||||
self.combo.setCurrentIndex(index if index >= 0 else 0)
|
|
||||||
self._restoring = False
|
|
||||||
|
|
||||||
def _current_vkz(self):
|
def _current_vkz(self):
|
||||||
return self.combo.currentData() if self.combo is not None else None
|
return self.panel.aktuelles_vkz() if self.panel is not None else None
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Laden & Hochladen
|
# Laden & Hochladen
|
||||||
@@ -449,16 +423,18 @@ class VlnKartenPlugin:
|
|||||||
return
|
return
|
||||||
# Schrittweise: je Ereignisschleifen-Durchlauf ein Layer, Fortschritt in der
|
# Schrittweise: je Ereignisschleifen-Durchlauf ein Layer, Fortschritt in der
|
||||||
# Meldungsleiste. Fehler kommen dort am Ende gesammelt, nicht als Modal-Dialog.
|
# Meldungsleiste. Fehler kommen dort am Ende gesammelt, nicht als Modal-Dialog.
|
||||||
|
self.panel.set_status("Lade %d Sammlungen …" % len(collections))
|
||||||
ogc_layers.layer_hinzufuegen(
|
ogc_layers.layer_hinzufuegen(
|
||||||
self.iface, self.client.base_url, vkz, self.combo.currentText(), collections, authcfg,
|
self.iface, self.client.base_url, vkz, self.panel.aktueller_text(), collections,
|
||||||
api_key=None if authcfg else self.client.api_key)
|
authcfg, api_key=None if authcfg else self.client.api_key)
|
||||||
|
|
||||||
def run_save_style(self):
|
def run_save_style(self):
|
||||||
"""Den Stil des aktiven OGC-Layers auf den Server zurückschreiben.
|
"""Den Stil des aktiven OGC-Layers auf den Server zurückschreiben.
|
||||||
|
|
||||||
Erlaubt für Verbandsmitarbeiter und die Gruppe MODUL_KARTENSTIL; der Server antwortet
|
Erlaubt für Verbandsmitarbeiter und die Gruppe MODUL_KARTENSTIL; der Server antwortet
|
||||||
sonst mit 403. Mit gedrückter Umschalttaste wird als **Vorlage der Art** gespeichert —
|
sonst mit 403. Ist in der Seitenleiste „als Vorlage für alle Verfahren“ angehakt (oder
|
||||||
die gilt dann für alle Verfahren (z. B. ein Stil für jede „Karte alter Stand").
|
die Umschalttaste gedrückt), gilt der Stil für die **Art** statt nur für dieses
|
||||||
|
Verfahren — z. B. ein Stil für jede „Karte alter Stand“.
|
||||||
"""
|
"""
|
||||||
from qgis.PyQt.QtWidgets import QApplication
|
from qgis.PyQt.QtWidgets import QApplication
|
||||||
from qgis.PyQt.QtCore import Qt as _Qt
|
from qgis.PyQt.QtCore import Qt as _Qt
|
||||||
@@ -473,7 +449,8 @@ class VlnKartenPlugin:
|
|||||||
if not cid:
|
if not cid:
|
||||||
self._show_error("Kein OGC-Layer", "Die Datenquelle nennt keine Sammlung (typename).")
|
self._show_error("Kein OGC-Layer", "Die Datenquelle nennt keine Sammlung (typename).")
|
||||||
return
|
return
|
||||||
vorlage = bool(QApplication.keyboardModifiers() & _Qt.KeyboardModifier.ShiftModifier)
|
vorlage = (self.panel is not None and self.panel.als_vorlage()) or bool(
|
||||||
|
QApplication.keyboardModifiers() & _Qt.KeyboardModifier.ShiftModifier)
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
settings.beginGroup(SETTINGS_GROUP)
|
settings.beginGroup(SETTINGS_GROUP)
|
||||||
authcfg = settings.value(ogc_layers.SETTINGS_AUTHCFG, "")
|
authcfg = settings.value(ogc_layers.SETTINGS_AUTHCFG, "")
|
||||||
@@ -489,6 +466,9 @@ class VlnKartenPlugin:
|
|||||||
self.iface.messageBar().pushMessage(
|
self.iface.messageBar().pushMessage(
|
||||||
"VLN Karten", "Stil für %s gespeichert — %s" % (cid, text),
|
"VLN Karten", "Stil für %s gespeichert — %s" % (cid, text),
|
||||||
level=Qgis.Success, duration=8)
|
level=Qgis.Success, duration=8)
|
||||||
|
if self.panel is not None:
|
||||||
|
self.panel.set_status("Stil gespeichert: %s%s"
|
||||||
|
% (cid, " (Vorlage)" if vorlage else ""))
|
||||||
else:
|
else:
|
||||||
self._show_error("Stil nicht gespeichert", text)
|
self._show_error("Stil nicht gespeichert", text)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user