Files
API_Karte_QGISDemo/vln_karten/panel.py
T
KAIandClaude Opus 5.5 c3386dc454 Linkliste: Kartenwahl hakt die passenden Dienste vor
Erik, 06.10.2026: neben dem Thema eine zweite Gliederung — für welche Karten ein Dienst gedacht
ist (Wege- und Gewässerplan, Neugestaltungsgrundsätze, Wertermittlung; mehrfach). Der Manager
liefert dafür je Eintrag `karten` und den Katalog unter GET /linkliste/karten.

Über der Dienstliste steht jetzt je Karte ein Häkchen (Katalog vom Server, Rückfall auf die drei
Karten bei älterem Server). Eine oder mehrere Karten gewählt: angehakt sind die Dienste mit
mindestens einer passenden Karte (linkliste.vorhaken), ohne Wahl alle wie bisher; von Hand
nachjustieren bleibt möglich, eine neu geholte Liste übernimmt die Wahl. Karten im Tooltip.
Fassung bleibt 0.7.0 (noch nicht ausgeliefert). Tests ohne Netz erweitert, beide grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 23:19:47 +02:00

489 lines
23 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- 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. Layer — Auswahl der Gruppen, laden, Änderungen speichern
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 Inhalt sitzt in einer Rollfläche: ist die Leiste höher als der Bildschirm, rollt sie,
statt das QGIS-Fenster zu strecken (Issue #1).
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
die OGC API schreibt QGIS jede Änderung einzeln zurück; „Änderungen speichern" schließt
nur die offenen Bearbeitungssitzungen.
"""
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtWidgets import (QCheckBox, QComboBox, QDockWidget, QFrame, QGroupBox,
QHBoxLayout, QLabel, QLineEdit, QListWidget,
QListWidgetItem, QPushButton, QScrollArea, QSizePolicy,
QVBoxLayout, QWidget)
PANEL_TITEL = "VLN Karten"
PANEL_OBJEKT = "VlnKartenPanel"
def ist_landkreis(vkz):
"""Steht ``vkz`` für einen Landkreis (oFB, 10–100) statt für ein Verfahren?
Landkreise stehen seit 0.7.0 oben in der Auswahl — gewählt lädt man dort nur die
Linkliste des Landkreises, keine Verfahrenslayer.
"""
try:
return 10 <= int(vkz) <= 100
except (TypeError, ValueError):
return False
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):
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_layer())
aussen.addWidget(self._bau_zeichen())
aussen.addWidget(self._bau_dienste())
aussen.addWidget(self._bau_darstellung())
aussen.addStretch(1)
aussen.addWidget(self._bau_status())
inhalt.setMinimumWidth(260)
# Die Seitenleiste haengt in einer Rollflaeche, nicht direkt im Dock (Daniel Helbig,
# LK Meissen, 14.09.2026 — Issue #1 „UI-Ueberlauf"): ein QDockWidget gibt die
# Mindesthoehe seines Inhalts an das Hauptfenster weiter. Ist der Inhalt hoeher als
# der Bildschirm, waechst das QGIS-Fenster mit und sein unterer Rand rutscht ins
# Unsichtbare — samt Statusleiste. Die Rollflaeche kappt diese Weitergabe und blendet
# stattdessen einen senkrechten Rollbalken ein.
rolle = QScrollArea()
rolle.setWidget(inhalt)
rolle.setWidgetResizable(True) # Inhalt folgt der Breite des Docks
rolle.setFrameShape(QFrame.Shape.NoFrame)
rolle.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
rolle.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
# Ohne das bleibt die Mindesthoehe des Inhalts am Dock haengen und der Rollbalken
# aendert nichts an der gemeldeten Groesse. Uebrig bleibt die eigene Mindesthoehe der
# Rollflaeche (gemessen 88 px) — klein genug fuer jeden Bildschirm, gross genug,
# dass sich das Dock nicht auf einen Strich zusammenziehen laesst.
rolle.setMinimumHeight(0)
# Breite des Rollbalkens draufschlagen, sonst schneidet er die Auswahllisten an.
balken = rolle.verticalScrollBar().sizeHint().width() or 16
rolle.setMinimumWidth(260 + balken)
self.setWidget(rolle)
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_layer(self):
kasten = QGroupBox("Layer")
lage = QVBoxLayout(kasten)
lage.setSpacing(6)
# Jede Sammlung kostet beim Anlegen rund eine halbe Sekunde (der Provider fragt
# Sammlung, Schema, Stil, OPTIONS und die erste Seite ab). Wer nur einen Teil braucht,
# ist mit der Auswahl deutlich schneller: 27 Layer ~14 s, die Vorerhebung allein ~2 s.
lage.addWidget(_klein(QLabel("Was geladen wird:")))
self.gruppen_haken = {}
for schluessel, beschriftung in (("Verfahren", "Verfahren (Umring, Plan 41, KAS …)"),
("Vorerhebung", "Vorerhebung"),
("Maßnahmenblätter", "Maßnahmenblätter (mit Zeichen am Weg)"),
("Widmungen & Verkehrszeichen",
"Widmungen && Verkehrszeichen")):
haken = QCheckBox(beschriftung)
haken.setChecked(True)
lage.addWidget(haken)
self.gruppen_haken[schluessel] = haken
self.ogc_knopf = QPushButton("Layer laden")
self.ogc_knopf.setToolTip("Die angehakten Sammlungen als editierbare Layer der OGC API")
self.ogc_knopf.clicked.connect(lambda: self._ruf("load_ogc"))
linie = QFrame()
linie.setFrameShape(QFrame.Shape.HLine)
linie.setFrameShadow(QFrame.Shadow.Sunken)
self.speichern_knopf = QPushButton("Änderungen speichern")
self.speichern_knopf.setToolTip("Schließt die offenen Bearbeitungssitzungen aller "
"geladenen VLN-Layer; jede Änderung geht einzeln an "
"die API")
self.speichern_knopf.clicked.connect(lambda: self._ruf("save_edits"))
lage.addWidget(self.ogc_knopf)
lage.addWidget(linie)
lage.addWidget(self.speichern_knopf)
lage.addWidget(_klein(QLabel("Bearbeiten wie gewohnt über den Stift; Speichern schreibt "
"jede Änderung sofort zurück.")))
return kasten
def _bau_zeichen(self):
"""Verkehrszeichen setzen — Palette, Blattwahl, zwei Werkzeuge.
Die Bedienung ist dieselbe wie im Browser: erst ein Schild waehlen, dann in der Karte
zwei Klicks (Standort, Blickrichtung). Das Blatt braucht nur, wer Zeichen am Weg eines
Massnahmenblatts setzt — in der Verfahrenskarte bleibt die Auswahl leer.
"""
from .zeichen_werkzeug import ZeichenPalette
kasten = QGroupBox("Verkehrszeichen setzen")
lage = QVBoxLayout(kasten)
lage.setSpacing(6)
lage.addWidget(_klein(QLabel("Schild waehlen, dann in der Karte: erster Klick Standort, "
"zweiter Klick Blickrichtung.")))
self.palette = ZeichenPalette()
lage.addWidget(self.palette)
lage.addWidget(_klein(QLabel("Blatt (nur fuer Zeichen am Weg):")))
self.blatt_combo = QComboBox()
self.blatt_combo.setToolTip("An welches Massnahmenblatt das Zeichen gehoert")
self.blatt_combo.addItem("— kein Blatt (Verfahrenskarte) —", None)
lage.addWidget(self.blatt_combo)
zeile = QHBoxLayout()
self.schild_knopf = QPushButton("Schild setzen")
self.schild_knopf.setCheckable(True)
self.schild_knopf.setToolTip("Zwei Klicks in der Karte: Standort, dann Blickrichtung")
self.schild_knopf.clicked.connect(lambda: self._ruf("zeichen_setzen"))
self.zusatz_knopf = QPushButton("Zusatzzeichen")
self.zusatz_knopf.setCheckable(True)
self.zusatz_knopf.setToolTip("Gewaehltes Zusatzzeichen an ein vorhandenes Schild haengen")
self.zusatz_knopf.clicked.connect(lambda: self._ruf("zusatz_anhaengen"))
zeile.addWidget(self.schild_knopf)
zeile.addWidget(self.zusatz_knopf)
lage.addLayout(zeile)
return kasten
def setze_blaetter(self, blaetter):
"""Die Auswahl der Blaetter fuellen — ``[(Beschriftung, uuid)]``."""
vorher = self.blatt_combo.currentData()
self.blatt_combo.blockSignals(True)
self.blatt_combo.clear()
self.blatt_combo.addItem("— kein Blatt (Verfahrenskarte) —", None)
for text, uuid_ in blaetter or []:
self.blatt_combo.addItem(text, uuid_)
if vorher:
i = self.blatt_combo.findData(vorher)
if i >= 0:
self.blatt_combo.setCurrentIndex(i)
self.blatt_combo.blockSignals(False)
def aktuelles_blatt(self):
return self.blatt_combo.currentData()
def aktueller_zeichencode(self):
return str((self.palette.aktueller_eintrag() or {}).get("code") or "")
def werkzeug_zuruecksetzen(self):
"""Beide Werkzeugknoepfe ausrasten (wenn QGIS das Werkzeug wechselt)."""
for knopf in (self.schild_knopf, self.zusatz_knopf):
knopf.blockSignals(True)
knopf.setChecked(False)
knopf.blockSignals(False)
def _bau_dienste(self):
"""Die Linkliste des gewählten Verfahrens oder Landkreises — gepflegt landesweit und vom
Landkreis, hier nur geladen.
Die Liste kommt fertig aufgelöst vom Server; je Zeile stehen Gruppe, Untergruppe und
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 "
"oder den gewählten Landkreis.")))
# Kartenwahl (Erik, 06.10.2026): hakt vor, was für die gewählte Karte gedacht ist.
# Die Häkchen entstehen erst mit dem Katalog vom Server (set_karten).
self.karten_haken = {}
self._karten_namen = {}
self.karten_zeile = QHBoxLayout()
self.karten_zeile.setSpacing(10)
self.karten_beschriftung = _klein(QLabel("Für Karte:"))
self.karten_beschriftung.setVisible(False)
self.karten_zeile.addWidget(self.karten_beschriftung)
lage.addLayout(self.karten_zeile)
self.dienste_liste = QListWidget()
# Bewusst niedrig: die Mindesthoehe aller Kaesten zusammen ist die Mindesthoehe des
# Docks (s. Rollflaeche im Konstruktor). 140 px hier machten die Seitenleiste auf
# einem 13-Zoll-Bildschirm hoeher als das Fenster.
self.dienste_liste.setMinimumHeight(90)
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_karten(self, katalog):
"""Die Kartenwahl aufbauen: je Karte ein Häkchen; eine Wahl hakt die passenden Dienste vor.
:param katalog: ``[{schluessel, name}]`` vom Server (``GET /linkliste/karten``).
"""
for haken in self.karten_haken.values():
self.karten_zeile.removeWidget(haken)
haken.deleteLater()
self.karten_haken = {}
self._karten_namen = {}
for k in katalog or []:
haken = QCheckBox(str(k.get("name") or k.get("schluessel")))
haken.setToolTip("Dienste für diese Karte vorhaken")
haken.stateChanged.connect(lambda _zustand: self._karten_anwenden())
self.karten_zeile.addWidget(haken)
self.karten_haken[str(k.get("schluessel"))] = haken
self._karten_namen[str(k.get("schluessel"))] = str(k.get("name") or k.get("schluessel"))
self.karten_beschriftung.setVisible(bool(self.karten_haken))
def gewaehlte_karten(self):
"""Schlüssel der angehakten Karten."""
return [k for k, haken in self.karten_haken.items() if haken.isChecked()]
def _karten_anwenden(self):
"""Die Dienste nach der Kartenwahl vorhaken (ohne Wahl: alle)."""
from .linkliste import vorhaken
eintraege = [self.dienste_liste.item(i) for i in range(self.dienste_liste.count())]
daten = [e.data(Qt.ItemDataRole.UserRole) or {} for e in eintraege]
for eintrag, an in zip(eintraege, vorhaken(daten, self.gewaehlte_karten())):
eintrag.setCheckState(Qt.CheckState.Checked if an else Qt.CheckState.Unchecked)
def set_dienste(self, dienste):
"""Die geholte Liste anzeigen: Name, Gruppe › Untergruppe und Herkunft, alle zunächst
angehakt."""
self.dienste_liste.clear()
for d in dienste or []:
gruppe = d.get("gruppe_name") or ""
if d.get("untergruppe"):
gruppe = "%s › %s" % (gruppe, d.get("untergruppe"))
text = "%s · %s · %s" % (d.get("name"), gruppe, 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)
karten = ", ".join(self._karten_namen.get(k, k) for k in (d.get("karten") or []))
eintrag.setToolTip("%s\n%s%s" % (d.get("url"), d.get("beschreibung") or "",
("\nKarten: " + karten) if karten else ""))
self.dienste_liste.addItem(eintrag)
if self.gewaehlte_karten():
self._karten_anwenden()
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)
lage.setSpacing(6)
self.stil_knopf = QPushButton("Stil des aktiven Layers speichern")
self.stil_knopf.setToolTip("Den Stil dieses Verfahrens darf speichern, wer im Verfahren "
"schreiben darf (nicht bei „nur lesen“)")
self.stil_knopf.clicked.connect(lambda: self._ruf("save_style"))
self.vorlage_haken = QCheckBox("als Vorlage für alle Verfahren")
self.vorlage_haken.setToolTip("Gilt dann für diese Art in jedem Verfahren, z. B. für "
"jede Karte alter Stand — nur für die Gruppe "
"MODUL_KARTENSTIL")
lage.addWidget(self.stil_knopf)
lage.addWidget(self.vorlage_haken)
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.speichern_knopf,
self.schild_knopf, self.zusatz_knopf, self.palette, self.blatt_combo,
self.stil_knopf, self.vorlage_haken, self.dienste_holen_knopf,
self.dienste_laden_knopf, self.dienste_alle, self.dienste_liste,
*self.gruppen_haken.values(), *self.karten_haken.values()):
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.dienste_liste.clear()
self._stumm = False
def set_verfahren(self, verfahren, aemter=()):
"""Die Auswahl füllen.
:param verfahren: ``[{"vkz", "name", "zugriff"}]`` aus der API; ``zugriff == "lesen"``
bekommt den Zusatz „nur lesen“ (oFB: Verfahren des eigenen Landkreises).
:param aemter: ``[{"vkz", "name"}]`` — die eigenen Landkreise stehen oben; gewählt lädt
man dort nur die Linkliste des Landkreises.
"""
eintraege = [("%s — Linkliste des Landkreises" % (a.get("name") or a["vkz"]), a["vkz"])
for a in aemter]
for v in verfahren:
text = "%s — %s" % (v["vkz"], v["name"]) if v.get("name") else str(v["vkz"])
if v.get("zugriff") == "lesen":
text += " (nur lesen)"
eintraege.append((text, v["vkz"]))
self._verfahren = eintraege
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 gewaehlte_gruppen(self):
"""Namen der angehakten Layer-Gruppen (wie in ``ogc_layers.GRUPPEN``)."""
return {name for name, haken in self.gruppen_haken.items() if 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)