Geodienste aus der Linkliste laden — je Verfahren, aus der Seitenleiste

Erik, 11.09.2026: "als angemeldeter Nutzer TG-spezifisch die Dienste aus der
Linkliste einbinden".

- vln_karten/linkliste.py (neu): holt GET /v2/linkliste?vkz=…, baut je Anbieter
  den passenden Layer (wms, wfs, rest/ArcGIS, geojson/ogr), wendet den beim
  Eintrag hinterlegten QML-Stil an und haengt alles schrittweise in die Gruppe
  "Geodienste <VKZ> <Name>" mit Untergruppen je Listengruppe.
- Seitenleiste: neuer Block "Geodienste (Linkliste)" mit Liste holen, Haken je
  Dienst (Name, Gruppe, Herkunft, Stil-Hinweis), alle an/aus und Ausgewaehlte
  laden.
- Die Liste kommt fertig aufgeloest vom Server; das Plugin rechnet nichts nach.

Zwei Dinge, die der Test gezeigt hat (tools/headless_dienste_test.py, QGIS 4.2
ohne Fenster gegen die Live-Schnittstelle):

* Die Adressform muss schlicht "url=<Adresse>" sein, wie in
  Plugin_SN_Basis/modules/LayerLoader.py. Mit einem selbst gebauten
  QgsDataSourceUri (crs, format) kam der ArcGIS-Layer erst nach 60 s und
  ungueltig zurueck; jetzt 0,4 s und 4457 Objekte.
* Ein 404 auf style.qml erreicht QgsBlockingNetworkRequest als Transportfehler.
  Erst den Statuscode ansehen, sonst meldet das Plugin einen Fehler, wo schlicht
  kein Stil hinterlegt ist.

Testlauf 11.09.2026: 44 Dienste in 0,8 s, Layer gueltig, Stil angewendet,
"kein Stil hinterlegt" sauber gemeldet — 0 Fehler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
erik
2026-09-11 17:37:58 +02:00
co-authored by Claude Opus 5
parent 6b0ce65c61
commit c8b723b653
4 changed files with 417 additions and 4 deletions
+89
View File
@@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
"""Die Linkliste im Plugin: holen, Layer bauen, Stil anwenden — gegen die echte Schnittstelle.
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_dienste_test.py
"""
import os, sys, tempfile, time
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from qgis.core import QgsApplication, QgsNetworkAccessManager, QgsProject
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, QNetworkDiskCache
QNetworkProxyFactory.setUseSystemConfiguration(False)
c = QNetworkDiskCache(); c.setCacheDirectory(tempfile.mkdtemp())
QgsNetworkAccessManager.instance().setCache(c)
from vln_karten import linkliste as LL
from vln_karten.api_client import KartenApiClient
BASE, KEY, VKZ = "https://api.flurneuordnung-sachsen.de/v2", os.environ["VLN_TOKEN"], "27027"
FEHLER = []
def pruefe(b, t):
print((" OK " if b else " FEHL ") + t)
if not b:
FEHLER.append(t)
client = KartenApiClient(base_url=BASE, api_key=KEY)
print("== 1) Liste holen ==")
t = time.time()
dienste = LL.dienste_laden(client, VKZ)
pruefe(len(dienste) > 0, "%d Dienste in %.2f s" % (len(dienste), time.time() - t))
pruefe(all({"name", "provider", "url", "herkunft"} <= set(d) for d in dienste),
"alle Felder vorhanden, die der Layerbau braucht")
print("== 2) Layer bauen, je Anbieter einer ==")
gebaut = {}
for d in dienste:
p = d["provider"]
if p in gebaut:
continue
t = time.time()
lyr = LL.layer_bauen(d)
ok = lyr is not None and lyr.isValid()
gebaut[p] = ok
pruefe(ok, "%-5s %-34s %s (%.1f s)" % (p, d["name"][:34],
"gueltig" if ok else (lyr.error().message()[:60] if lyr else "kein Layer"),
time.time() - t))
if ok and hasattr(lyr, "featureCount"):
print(" Features:", lyr.featureCount())
print("== 3) Stil aus der Datenbank anwenden ==")
mit_stil = [d for d in dienste if d.get("stil")]
if mit_stil:
d = mit_stil[0]
lyr = LL.layer_bauen(d)
if lyr is not None and lyr.isValid():
vorher = lyr.renderer().type() if hasattr(lyr, "renderer") and lyr.renderer() else "-"
ok, meldung = LL.stil_anwenden(lyr, BASE, d["id"], KEY)
nachher = lyr.renderer().type() if hasattr(lyr, "renderer") and lyr.renderer() else "-"
pruefe(ok, "Stil fuer '%s': %s (Renderer %s -> %s)" % (d["name"], meldung or "angewendet",
vorher, nachher))
else:
print(" (Layer nicht gueltig, Stil nicht pruefbar)")
else:
print(" (kein Eintrag mit Stil)")
print("== 4) ohne Stil meldet sauber ==")
ohne = [d for d in dienste if not d.get("stil")]
if ohne:
lyr = LL.layer_bauen(ohne[0])
if lyr is not None and lyr.isValid():
ok, meldung = LL.stil_anwenden(lyr, BASE, ohne[0]["id"], KEY)
pruefe(not ok and "kein Stil" in meldung, "Meldung statt Fehler: %s" % meldung)
app.exitQgis()
print("== %d Fehler ==" % len(FEHLER))
for f in FEHLER:
print(" -", f)
sys.exit(1 if FEHLER else 0)
+205
View File
@@ -0,0 +1,205 @@
# -*- coding: utf-8 -*-
"""Linkliste der Geodienste — die Dienste eines Verfahrens als QGIS-Layer.
Die Liste kommt vom VLN-Manager (``GET /v2/linkliste?vkz=…``) und ist dort vierstufig gepflegt:
landesweit, Landkreis, Team und Verfahren erben aufeinander. Das Plugin bekommt bereits die
**aufgelöste** Liste — es muss nichts zusammenrechnen, sondern nur laden. Je Eintrag steht dabei,
woher er stammt (``herkunft``), damit man in QGIS sieht, ob ein Dienst landesweit gilt oder nur
für dieses eine Verfahren eingetragen wurde.
Vier Anbieter kommen vor, jeder braucht in QGIS eine andere Datenquelle:
========== ================================================================
``wms`` ``QgsRasterLayer`` mit ``crs``/``format``/``layers``/``url``
``wfs`` ``QgsVectorLayer`` mit ``typename`` (Anbieter ``WFS``)
``rest`` ``QgsVectorLayer`` auf den ArcGIS-FeatureServer
``geojson`` ``QgsVectorLayer`` über ``ogr`` (Datei oder Adresse)
========== ================================================================
Der Stil liegt beim Eintrag in der Datenbank und kommt über
``GET /v2/linkliste/{id}/style.qml``; ohne Stil bleibt die QGIS-Vorgabe.
"""
import os
import tempfile
from qgis.PyQt.QtCore import Qt, QTimer, QUrl
from qgis.PyQt.QtNetwork import QNetworkRequest
from qgis.PyQt.QtWidgets import QProgressBar
from qgis.core import (
Qgis,
QgsApplication,
QgsBlockingNetworkRequest,
QgsNetworkAccessManager,
QgsDataSourceUri,
QgsProject,
QgsRasterLayer,
QgsVectorLayer,
)
from .api_client import ApiError
#: Gruppenname im Ebenenbaum — alle Dienste eines Verfahrens landen darunter.
GRUPPE = "Geodienste"
#: Vorgaben für WMS-Anfragen; dieselben Werte nutzt das Web-GIS (karte/doku/hintergrund.md).
WMS_FORMAT = "image/png"
WMS_CRS = "EPSG:25833"
def dienste_laden(client, vkz):
"""``GET /linkliste?vkz=…`` -> Liste der aufgelösten Einträge.
:raises ApiError: wenn die Antwort nicht das erwartete Format hat.
"""
daten = client._request("GET", "/linkliste?vkz=%s" % vkz)
zeilen = daten.get("dienste") if isinstance(daten, dict) else None
if not isinstance(zeilen, list):
raise ApiError("Antwort von /linkliste hat ein unerwartetes Format.")
return zeilen
def stil_url(base_url, eintrag_id):
return "%s/linkliste/%s/style.qml" % (base_url.rstrip("/"), eintrag_id)
def stil_anwenden(layer, base_url, eintrag_id, api_key=None):
"""Den Stil des Eintrags holen und anwenden. Ohne Stil (404) bleibt alles, wie es ist.
:returns: ``(True, "")`` oder ``(False, Meldung)``.
"""
req = QNetworkRequest(QUrl(stil_url(base_url, eintrag_id)))
if api_key:
req.setRawHeader(b"X-API-Key", api_key.encode("utf-8"))
blk = QgsBlockingNetworkRequest()
fehler = blk.get(req)
antwort = blk.reply()
code = antwort.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute)
# Ein 404 kommt beim blockierenden Aufruf als Transportfehler an — erst den Statuscode
# ansehen, sonst meldet das Plugin einen Fehler, wo schlicht kein Stil hinterlegt ist.
if code == 404:
return False, "kein Stil hinterlegt"
if fehler != QgsBlockingNetworkRequest.NoError and not code:
return False, blk.errorMessage()
if code != 200:
return False, "HTTP %s" % code
pfad = os.path.join(tempfile.gettempdir(), "vln_dienst_%s.qml" % eintrag_id)
with open(pfad, "wb") as fh:
fh.write(bytes(antwort.content()))
meldung, ok = layer.loadNamedStyle(pfad)
if ok:
layer.triggerRepaint()
return bool(ok), "" if ok else meldung
def layer_bauen(eintrag, zeit_ms=20000):
"""Einen Eintrag der Linkliste in einen QGIS-Layer verwandeln.
Die Adressform ist die, die sich in ``Plugin_SN_Basis/modules/LayerLoader.py`` bewährt hat:
schlicht ``url=<Adresse>``, bei WFS zusätzlich ``typename=``. Ein selbst gebautes
``QgsDataSourceUri`` mit ``crs``/``format`` führte beim ArcGIS-Anbieter dazu, dass der
Layer nach einer Minute ungültig zurückkam (gemessen 11.09.2026).
:param zeit_ms: Netzwerk-Zeitlimit für die netzgebundenen Anbieter.
:returns: Layer oder ``None``, wenn der Anbieter unbekannt ist.
"""
name = str(eintrag.get("name") or "Dienst")
provider = str(eintrag.get("provider") or "").lower()
url = str(eintrag.get("url") or "").strip()
ebene = str(eintrag.get("layer") or "").strip()
if provider in ("wms", "wfs", "rest"):
try:
verwalter = QgsNetworkAccessManager.instance()
if hasattr(verwalter, "setTimeout"):
verwalter.setTimeout(zeit_ms)
except Exception: # noqa: BLE001
pass
if provider == "wms":
uri = url if url.lower().startswith("url=") else "url=" + url
if ebene and "layers=" not in uri:
uri = "layers=%s format=%s crs=%s %s" % (ebene, WMS_FORMAT, WMS_CRS, uri)
return QgsRasterLayer(uri, name, "wms")
if provider == "wfs":
basis = url[4:] if url.lower().startswith("url=") else url
uri = "url=" + basis
if ebene:
uri += " typename=" + ebene
return QgsVectorLayer(uri, name, "WFS")
if provider == "rest":
# Die Adresse zeigt meist schon auf den Layer (…/FeatureServer/3). Fehlt der Index,
# wird der aus der Liste angehängt, sonst 0 — der Anbieter braucht einen.
ziel = url.rstrip("/")
if ziel.lower().endswith("/featureserver") or ziel.lower().endswith("/mapserver"):
ziel = "%s/%s" % (ziel, ebene or "0")
return QgsVectorLayer("url=" + ziel, name, "arcgisfeatureserver")
if provider in ("geojson", "ogr"):
return QgsVectorLayer(url, name, "ogr")
return None
def dienste_hinzufuegen(iface, base_url, vkz, vkz_name, dienste, api_key=None, nur_ids=None):
"""Die Dienste als Layer in die Gruppe „Geodienste“ hängen — **schrittweise**.
Wie beim Laden der Verfahrenslayer wird je Ereignisschleifen-Durchlauf genau ein Layer
angelegt: WMS- und WFS-Anbieter fragen beim Anlegen ihre Capabilities ab, und bei zwanzig
Diensten friert die Oberfläche sonst sichtbar ein. Untergruppen entstehen je Gruppe der
Liste (Hintergrund, Wasser, Natur …), damit der Ebenenbaum lesbar bleibt.
:param nur_ids: nur diese Einträge laden (``None`` = alle).
:returns: die Meldung mit dem Fortschrittsbalken.
"""
auswahl = [d for d in dienste if nur_ids is None or int(d.get("id", 0)) in nur_ids]
projekt = QgsProject.instance()
wurzel = projekt.layerTreeRoot()
oben_name = ("%s %s %s" % (GRUPPE, vkz, vkz_name or "")).strip()
oben = wurzel.findGroup(oben_name) or wurzel.insertGroup(0, oben_name)
untergruppen = {}
balken = QProgressBar()
balken.setMaximum(max(1, len(auswahl)))
balken.setValue(0)
balken.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
meldung = iface.messageBar().createMessage("VLN Karten", "Geodienste werden geladen …")
meldung.layout().addWidget(balken)
iface.messageBar().pushWidget(meldung, Qgis.Info)
zustand = {"i": 0, "geladen": 0, "fehler": []}
def schritt():
if zustand["i"] >= len(auswahl):
iface.messageBar().popWidget(meldung)
text = "%d Geodienste geladen." % zustand["geladen"]
if zustand["fehler"]:
text += " %d nicht ladbar: %s" % (len(zustand["fehler"]),
", ".join(zustand["fehler"][:3]))
iface.messageBar().pushMessage(
"VLN Karten", text,
level=Qgis.Success if zustand["geladen"] else Qgis.Warning, duration=10)
return
eintrag = auswahl[zustand["i"]]
zustand["i"] += 1
gruppe_name = str(eintrag.get("gruppe_name") or "Sonstiges")
if gruppe_name not in untergruppen:
untergruppen[gruppe_name] = (oben.findGroup(gruppe_name)
or oben.addGroup(gruppe_name))
layer = layer_bauen(eintrag)
if layer is not None and layer.isValid():
if eintrag.get("stil"):
stil_anwenden(layer, base_url, eintrag.get("id"), api_key)
projekt.addMapLayer(layer, False)
untergruppen[gruppe_name].addLayer(layer)
zustand["geladen"] += 1
else:
zustand["fehler"].append(str(eintrag.get("name") or "?"))
balken.setValue(zustand["i"])
meldung.setText("Geodienste werden geladen … %d von %d (%s)"
% (zustand["i"], len(auswahl), eintrag.get("name")))
QTimer.singleShot(0, schritt)
QTimer.singleShot(0, schritt)
return meldung
+70 -4
View File
@@ -11,8 +11,9 @@ Aufbau von oben nach unten:
1. Anmeldung — Status + Knopf (Anmelden/Abmelden)
2. Verfahren — Suchfeld (filtert) + Auswahlliste
3. Layer — Auswahl der Gruppen, laden, Änderungen speichern
4. Darstellung — Stil speichern (mit Häkchen „für alle Verfahren")
5. Statuszeile
4. Geodienste — die Linkliste des Verfahrens: auswählen und als Layer laden
5. Darstellung — Stil speichern (mit Häkchen „für alle Verfahren")
6. Statuszeile
Der frühere GeoJSON-Weg (Kopie laden, später als Ganzes hochladen) ist am 08.09.2026
entfallen (Erik: „das Speichern via OGC einbauen, GeoJSON nicht mehr verwenden"). Über
@@ -22,7 +23,8 @@ nur die offenen Bearbeitungssitzungen.
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import (QCheckBox, QComboBox, QDockWidget, QFrame, QGroupBox,
QHBoxLayout, QLabel, QLineEdit, QPushButton, QSizePolicy,
QHBoxLayout, QLabel, QLineEdit, QListWidget,
QListWidgetItem, QPushButton, QSizePolicy,
QVBoxLayout, QWidget)
PANEL_TITEL = "VLN Karten"
@@ -64,6 +66,7 @@ class VlnPanel(QDockWidget):
aussen.addWidget(self._bau_anmeldung())
aussen.addWidget(self._bau_verfahren())
aussen.addWidget(self._bau_layer())
aussen.addWidget(self._bau_dienste())
aussen.addWidget(self._bau_darstellung())
aussen.addStretch(1)
aussen.addWidget(self._bau_status())
@@ -143,6 +146,66 @@ class VlnPanel(QDockWidget):
"jede Änderung sofort zurück.")))
return kasten
def _bau_dienste(self):
"""Die Linkliste des gewählten Verfahrens — vierstufig gepflegt, hier nur geladen.
Die Liste kommt fertig aufgelöst vom Server (landesweit, Landkreis, Team, Verfahren);
je Zeile steht die Herkunft dabei, damit man sieht, wo ein Dienst herkommt.
"""
kasten = QGroupBox("Geodienste (Linkliste)")
lage = QVBoxLayout(kasten)
lage.setSpacing(6)
lage.addWidget(_klein(QLabel("WMS-, WFS- und ArcGIS-Dienste für dieses Verfahren.")))
self.dienste_liste = QListWidget()
self.dienste_liste.setMinimumHeight(140)
self.dienste_liste.setToolTip("Angehakte Dienste werden geladen")
lage.addWidget(self.dienste_liste)
zeile = QHBoxLayout()
self.dienste_holen_knopf = QPushButton("Liste holen")
self.dienste_holen_knopf.clicked.connect(lambda: self._ruf("dienste_holen"))
self.dienste_laden_knopf = QPushButton("Ausgewählte laden")
self.dienste_laden_knopf.clicked.connect(lambda: self._ruf("dienste_laden"))
zeile.addWidget(self.dienste_holen_knopf)
zeile.addWidget(self.dienste_laden_knopf)
lage.addLayout(zeile)
self.dienste_alle = QPushButton("alle an- oder abwählen")
self.dienste_alle.setFlat(True)
self.dienste_alle.clicked.connect(self._dienste_umschalten)
lage.addWidget(self.dienste_alle)
return kasten
def _dienste_umschalten(self):
"""Alles an, wenn noch etwas aus ist; sonst alles aus."""
etwas_aus = any(self.dienste_liste.item(i).checkState() != Qt.CheckState.Checked
for i in range(self.dienste_liste.count()))
ziel = Qt.CheckState.Checked if etwas_aus else Qt.CheckState.Unchecked
for i in range(self.dienste_liste.count()):
self.dienste_liste.item(i).setCheckState(ziel)
def set_dienste(self, dienste):
"""Die geholte Liste anzeigen: Name, Gruppe und Herkunft, alle zunächst angehakt."""
self.dienste_liste.clear()
for d in dienste or []:
text = "%s · %s · %s" % (d.get("name"), d.get("gruppe_name") or "",
d.get("herkunft_text") or "")
if d.get("stil"):
text += " · mit Stil"
eintrag = QListWidgetItem(text)
eintrag.setFlags(eintrag.flags() | Qt.ItemFlag.ItemIsUserCheckable)
eintrag.setCheckState(Qt.CheckState.Checked)
eintrag.setData(Qt.ItemDataRole.UserRole, d)
eintrag.setToolTip("%s\n%s" % (d.get("url"), d.get("beschreibung") or ""))
self.dienste_liste.addItem(eintrag)
def gewaehlte_dienste(self):
"""Die angehakten Einträge als Liste der Server-Datensätze."""
aus = []
for i in range(self.dienste_liste.count()):
e = self.dienste_liste.item(i)
if e.checkState() == Qt.CheckState.Checked:
aus.append(e.data(Qt.ItemDataRole.UserRole))
return aus
def _bau_darstellung(self):
kasten = QGroupBox("Darstellung")
lage = QVBoxLayout(kasten)
@@ -174,7 +237,9 @@ class VlnPanel(QDockWidget):
self.login_knopf.setVisible(not angemeldet)
self.logout_knopf.setVisible(angemeldet)
for widget in (self.filter_feld, self.combo, self.ogc_knopf, self.speichern_knopf,
self.stil_knopf, self.vorlage_haken, *self.gruppen_haken.values()):
self.stil_knopf, self.vorlage_haken, self.dienste_holen_knopf,
self.dienste_laden_knopf, self.dienste_alle, self.dienste_liste,
*self.gruppen_haken.values()):
widget.setEnabled(angemeldet)
if not angemeldet:
self._stumm = True
@@ -182,6 +247,7 @@ class VlnPanel(QDockWidget):
self.combo.clear()
self.combo.addItem("— bitte anmelden —", None)
self.filter_feld.clear()
self.dienste_liste.clear()
self._stumm = False
def set_verfahren(self, verfahren):
+53
View File
@@ -19,6 +19,7 @@ from qgis.core import Qgis, QgsDataSourceUri, QgsProject
from .api_client import ApiError, AuthError, KartenApiClient
from .panel import VlnPanel
from .login_dialog import LoginDialog, SETTINGS_GROUP
from . import linkliste
from . import ogc_layers
MENU_TITLE = "&VLN Karten"
@@ -50,6 +51,8 @@ class VlnKartenPlugin:
"verfahren_gewaehlt": self.on_verfahren_changed,
"load_ogc": self.run_load_ogc,
"save_edits": self.run_save_edits,
"dienste_holen": self.run_dienste_holen,
"dienste_laden": self.run_dienste_laden,
"save_style": self.run_save_style,
},
)
@@ -288,6 +291,56 @@ class VlnKartenPlugin:
if self.panel is not None:
self.panel.set_status("%d von %d Layern gespeichert." % (len(gespeichert), len(offen)))
def run_dienste_holen(self):
"""Die Linkliste des gewählten Verfahrens holen und in der Seitenleiste anzeigen.
Der Server liefert sie bereits aufgelöst: landesweit, Landkreis, Team und Verfahren
sind zusammengeführt, geerbte Einträge tragen ihre Herkunft. Das Plugin rechnet nichts
nach, es zeigt und lädt.
"""
if not self._ensure_logged_in():
return
vkz = self._current_vkz()
if not vkz:
self._show_error("Kein Verfahren", "Bitte zuerst ein Verfahren auswählen.")
return
try:
dienste = linkliste.dienste_laden(self.client, vkz)
except AuthError:
self._handle_session_expired()
return
except ApiError as exc:
self._show_error("Linkliste", str(exc))
return
self.panel.set_dienste(dienste)
herkunft = {}
for d in dienste:
herkunft[d.get("herkunft_text") or "?"] = herkunft.get(d.get("herkunft_text") or "?", 0) + 1
text = ", ".join("%d %s" % (n, k) for k, n in herkunft.items()) or "nichts hinterlegt"
self.panel.set_status("%d Geodienste: %s" % (len(dienste), text))
self.iface.messageBar().pushMessage(
"VLN Karten", "%d Geodienste für dieses Verfahren (%s)." % (len(dienste), text),
level=Qgis.Info, duration=6)
def run_dienste_laden(self):
"""Die angehakten Dienste als Layer in die Gruppe „Geodienste" hängen."""
if not self._ensure_logged_in():
return
vkz = self._current_vkz()
if not vkz:
self._show_error("Kein Verfahren", "Bitte zuerst ein Verfahren auswählen.")
return
gewaehlt = self.panel.gewaehlte_dienste()
if not gewaehlt:
self._show_error("Nichts ausgewählt",
"Bitte zuerst die Liste holen und dann die gewünschten Dienste "
"anhaken.")
return
linkliste.dienste_hinzufuegen(
self.iface, self.client.base_url, vkz, self.panel.aktueller_text(), gewaehlt,
api_key=self.client.api_key)
self.panel.set_status("%d Geodienste werden geladen …" % len(gewaehlt))
def run_save_style(self):
"""Den Stil des aktiven OGC-Layers auf den Server zurückschreiben.