Fix: beim Plugin-Reload werden neue Toolbars hinzugefügt aber keine gelöscht

Fix: Settings-Tab ist leer
Dokumentation begonnen
Pluginkonzept.md überarbeitet
This commit is contained in:
2026-01-09 15:19:25 +01:00
parent b805f78f02
commit 039c614592
6 changed files with 443 additions and 207 deletions

View File

@@ -1,122 +1,188 @@
+ PluginController # WrapperArchitektur Übersicht
└─ GUIManager Die WrapperArchitektur von sn_basis bildet das Fundament für eine robuste, testbare und zukunftssichere QGISPluginEntwicklung.
└─ PrüfManager (koordiniert alle Prüfer) Sie kapselt sämtliche QGIS und QtAbhängigkeiten hinter klar definierten Schnittstellen und ermöglicht dadurch:
├─ Dateiprüfer
├─ Linklistenprüfer
│ └─ Zeilenprüfer[n]
│ ├─ Linkprüfer
│ └─ Stilprüfer
└─ LayerLoader
└─ Logger
Plan41_plugin/ Mockfähige UnitTests ohne QGIS
├── plugin/ # Plugin-Code
│ ├── main_plugin.py # PluginController
│ ├── dock_widget.py # GUIManager
│ ├── pruefer/
│ │ ├── dateipruefer.py
│ │ ├── linklistenpruefer.py
│ │ ├── zeilenpruefer.py
│ │ ├── linkpruefer.py
│ │ └── stilpruefer.py
│ ├── loader.py
│ └── logger.py
├── tests/ # Unit-Tests
│ ├── __init__.py
│ ├── test_dateipruefer.py
│ ├── test_linklistenpruefer.py
│ ├── test_zeilenpruefer.py
│ ├── test_linkpruefer.py
│ ├── test_stilpruefer.py
│ ├── test_logger.py
│ └── run_tests.py # zentraler Test-Runner
├── requirements.txt
└── README.md
PyQt5/6Kompatibilität ohne CodeÄnderungen
+------------------------------------+ saubere Trennung von UI, Logik und Infrastruktur
| PluginController |
+------------------------------------+
| - Dock_widget: GUIManager |
| - pruef_manager: PruefManager |
| - loader: LayerLoader |
| - logger: Logger |
+------------------------------------+
| + start(): void |
+------------------------------------+
+------------------------------------+ stabile APIs, die unabhängig von QGISVersionen bleiben
| GUIManager |
+------------------------------------+
| - dialog: QWidget |
+------------------------------------+
| + getParameter(): dict |
+------------------------------------+
+------------------------------------+ klare Erweiterbarkeit für zukünftige Module und Plugins
| PruefManager |
+------------------------------------+
| - dateipruefer: Dateipruefer |
| - linklistenpruefer: Linklisten... |
+------------------------------------+
| + pruefe_alle(parameter): list |
+------------------------------------+
+------------------------------------+ Die WrapperSchicht ist das zentrale Bindeglied zwischen der PluginLogik und der QGIS/QtUmgebung.
| Dateipruefer |
+------------------------------------+
| + pruefe(pfad: str): PruefErgebnis |
+------------------------------------+
+------------------------------------+ ## Ziele der WrapperArchitektur
| Linklistenpruefer | 🎯 1. Entkopplung von QGIS und Qt
+------------------------------------+ Alle direkten Importe wie from qgis.core import ... oder from qgis.PyQt.QtWidgets import ... verschwinden aus der PluginLogik.
| + pruefe(pfad: str): list[Zeile] | Stattdessen werden sie über WrapperModule abstrahiert.
+------------------------------------+
+------------------------------------+ 🎯 2. Testbarkeit ohne QGIS
| Zeilenpruefer | Im MockModus liefern die Wrapper:
+------------------------------------+
| - linkpruefer: Linkpruefer |
| - stilpruefer: Stilpruefer |
+------------------------------------+
| + pruefe(zeile: str): LayerAuftrag |
+------------------------------------+
+------------------------------------+ DummyObjekte
| Linkpruefer |
+------------------------------------+
| + pruefe(link: str): PruefErgebnis |
+------------------------------------+
+------------------------------------+ simulierte Rückgabewerte
| Stilpruefer |
+------------------------------------+
| + pruefe(stilname: str): Pruef... |
+------------------------------------+
+------------------------------------+ speicherbare Zustände (z.B. Variablen, Layer, Nachrichten)
| LayerLoader |
+------------------------------------+
| + lade(layer_auftrag): void |
+------------------------------------+
+------------------------------------+ Damit laufen Tests in jeder CIUmgebung.
| Logger |
+------------------------------------+
| + schreibe(msg: str): void |
| + exportiere(): file |
+------------------------------------+
+------------------------------------+ 🎯 3. Einheitliche API für alle Plugins
| PruefErgebnis | Plugins greifen nicht mehr direkt auf QGIS zu, sondern nutzen:
+------------------------------------+
| - erfolgreich: bool |
| - daten: dict |
| - fehler: list[str] |
| - warnungen: list[str] |
+------------------------------------+
Code
sn_basis.functions.qgiscore_wrapper
sn_basis.functions.qgisui_wrapper
sn_basis.functions.qt_wrapper
sn_basis.functions.variable_wrapper
sn_basis.functions.message_wrapper
sn_basis.functions.dialog_wrapper
🎯 4. Zukunftssicherheit
Ändert sich die QGIS oder QtAPI, wird nur der Wrapper angepasst, nicht jedes Plugin.
## Architekturüberblick
Die WrapperSchicht besteht aus mehreren Modulen, die jeweils einen klar abgegrenzten Verantwortungsbereich haben.
### 1. qt_wrapper QtAbstraktion
Kapselt alle QtWidgets, Dialoge und Konstanten:
QWidget, QDialog, QMessageBox, QToolBar, QMenu, …
Layouts, Buttons, Labels, LineEdits
QtKonstanten wie YES, NO, DockAreas
MockModus:
Stellt DummyWidgets bereit, die keine UI öffnen.
### 2. qgiscore_wrapper QGISCoreAbstraktion
Abstraktion für:
QgsProject
LayerZugriff
ProjektMetadaten
Pfade, CRS, FeatureZugriff
MockModus:
Simuliert ein Projekt und LayerContainer.
### 3. qgisui_wrapper QGISUIAbstraktion
Kapselt UIbezogene QGISFunktionen:
Zugriff auf iface
DockManagement
Menü und ToolbarIntegration
HauptfensterZugriff
MockModus:
Stellt ein DummyInterface bereit.
### 4. variable_wrapper QGISVariablen
Abstraktion für:
Projektvariablen (projectScope)
globale Variablen (globalScope)
MockSpeicher für Tests
Vorteile:
keine QGISAbhängigkeit in der Logik
testbare Variablenverwaltung
einheitliches API
### 5. message_wrapper Meldungen & Logging
Einheitliche Schnittstelle für:
Fehlermeldungen
Warnungen
InfoMeldungen
Logging
MockModus:
Speichert Nachrichten statt sie an QGIS zu senden.
### 6. dialog_wrapper BenutzerDialoge
Abstraktion für:
Ja/NeinDialoge
spätere Erweiterungen (Eingabedialoge, Dateidialoge, etc.)
MockModus:
Gibt DefaultWerte zurück, öffnet keine UI.
### 7. DockManager & Navigation
Diese Module nutzen die WrapperSchicht, um:
DockWidgets sicher zu verwalten
Toolbars und Menüs zu erzeugen
Reloadsichere UIStrukturen aufzubauen
Sie sind keine Wrapper, sondern WrapperKonsumenten.
## Designprinzipien
🧱 1. Single Source of Truth
Jede QGIS oder QtFunktionalität wird nur an einer Stelle implementiert.
🔄 2. Austauschbarkeit
MockModus und Echtmodus sind vollständig austauschbar.
🧪 3. Testbarkeit
Jede Funktion kann ohne QGIS getestet werden.
🧼 4. Saubere Trennung
UI → qt_wrapper
QGISCore → qgiscore_wrapper
QGISUI → qgisui_wrapper
Logik → settings_logic, layer_logic, prüfmanager, …
🔌 5. Erweiterbarkeit
Neue Wrapper können jederzeit ergänzt werden, ohne bestehende Plugins zu brechen.
## Vorteile für Entwickler
Keine QGISAbhängigkeiten in der Logik
IDEfreundlich (Pylance, Autocomplete, Typing)
CIfähig (Tests ohne QGIS)
saubere Architektur
leichte Wartbarkeit
klare Dokumentation
## Fazit
Die WrapperArchitektur ist das Herzstück von sn_basis.
Sie ermöglicht eine moderne, modulare und testbare QGISPluginEntwicklung, die unabhängig von QGISVersionen, QtVersionen und Entwicklungsumgebungen funktioniert.
Sie bildet die Grundlage für:
stabile APIs
saubere UIAbstraktion
automatisierte Tests
nachhaltige Weiterentwicklung

View File

@@ -1,22 +1,144 @@
**Pluginkonzept** # WrapperArchitektur Übersicht
Das Plugin ist grundsätzlich als modulares System gedacht. Komponenten sollen sowohl im Plugin selbst, aber auch in anderen Anwendungen verbaut werden können. Die WrapperArchitektur von sn_basis bildet das Fundament für eine robuste, testbare und zukunftssichere QGISPluginEntwicklung.
Die Module sind als Python-Objekte angelegt. Sie kapselt sämtliche QGIS und QtAbhängigkeiten hinter klar definierten Schnittstellen und ermöglicht dadurch:
Alle Fallunterscheidungen, Exception-Management und Fehlerbehandlung sind in die "Prüfer" ausgelagert.
Der "Prüfmanager" übernimmt dabei die Interaktion mit dem Anwender, um Abfragen oder Fallunterscheidungen durchzuführen, die nicht anhand des Codes erfolgen können.
Alle Prüfer geben ein Objekt "Prüfergebnis" zurück, das das Ergebnis der Fallunterscheidung, Exceptions und Fehlermeldungen enthält. Die Prüfer haben selbst keine UI-Elemente.
| Modul | Aufgabe | Beschreibung | - Mockfähige UnitTests ohne QGIS
|-------------------|---------------------------------------|--------------| - PyQt5/6Kompatibilität ohne CodeÄnderungen
|PruefManager | Nutzerabfragen, Ergebnisanpassung | Der Pruefmanager wertet das Ergebnis vom Typ "PruefErgebnis" aus. Sind Entscheidungen erforderlich, fragt er den Anwender und passt das PruefErgebnis entsprechend an, bzw gibt Fehler aus| - saubere Trennung von UI, Logik und Infrastruktur
|Dateipruefer | Auswertung der Eingaben in Dateiauswahlfeldern | Der Dateipruefer prüft die Eingaben in Dateifeldern. Dabei kann bei jeder Prüfung vorgegeben werden, ob leere Eingabefelder zulässig sind, und ob sie, wenn sie leer sind, eine Standarddatei aufrufen oder temporäre Layer erzeugen. In jedem Fall wird der Nutzer zur Entscheidung aufgefordert, ob das leere Feld beabsichtigt ist, oder ein Bedienfehler| - stabile APIs, die unabhängig von QGISVersionen bleiben
|Linklistenpruefer | Spezialprüfer für die Linkliste aus dem Plan41-Plugin | Damit die beiden Objekte Stilpruefer und Linkpruefer auch unabhängig voneinander verwendet werden können, fasst der Linklistenpruefer die Ergebnisse zusammen und ergänzt eine Prüfung zur Kartenreihenfolge/Layerreihenfolge| - klare Erweiterbarkeit für zukünftige Module und Plugins
|Linkpruefer | prüft die Quelle eines angegebenen Links technisch und entscheidet die technischen Parameter nach Typ und Quellort | Enthält eine Fallunterscheidung für lokale und remote-Quellen, sowie für unterschiedliche Datenanbieter. Der Linkpruefer gibt Fehler und Exceptions zurück, wenn die Quelle fehlerhaft oder nicht erreichbar ist.|
|Stilpruefer | Prüft verschiedene Stilquellen | Der Stilpruefer prüft .qml und eingebettete Stile und gibt Warnungen zurück, bzw. Exceptions, um Nutzerentscheidungen auszulösen|
Jedes Modul hat seinen eigenen Unittest. Die Tests werden im Unterordner "Test" zusammengefasst und können gesammelt über die "run_tests.py" aufgerufen werden. Die WrapperSchicht ist das zentrale Bindeglied zwischen der PluginLogik und der QGIS/QtUmgebung.
## Ziele der WrapperArchitektur
1. Entkopplung von QGIS und Qt
Alle direkten Importe wie from qgis.core import ... oder from qgis.PyQt.QtWidgets import ... verschwinden aus der PluginLogik.
Stattdessen werden sie über WrapperModule abstrahiert.
2. Testbarkeit ohne QGIS
Im MockModus liefern die Wrapper:
- DummyObjekte
- simulierte Rückgabewerte
- speicherbare Zustände (z.B. Variablen, Layer, Nachrichten)
Damit laufen Tests in jeder CIUmgebung.
3. Einheitliche API für alle Plugins
Plugins greifen nicht mehr direkt auf QGIS zu, sondern nutzen:
- sn_basis.functions.qgiscore_wrapper
- sn_basis.functions.qgisui_wrapper
- sn_basis.functions.qt_wrapper
- sn_basis.functions.variable_wrapper
- sn_basis.functions.message_wrapper
- sn_basis.functions.dialog_wrapper
Aufgrund des Umfangs ist der Wrapper für die Layerbehandlung aufgeteilt:
- ly_existence_wrapper
- ly_geometry_wrapper
- ly_Metadata_wrapper
- ly_style_wrapper
- ly_visibility_wrapper
4. Zukunftssicherheit
Ändert sich die QGIS oder QtAPI, wird nur der Wrapper angepasst, nicht jedes Plugin.
## Architekturüberblick
Die WrapperSchicht besteht aus mehreren Modulen, die jeweils einen klar abgegrenzten Verantwortungsbereich haben.
### 1. qt_wrapper QtAbstraktion
Kapselt alle QtWidgets, Dialoge und Konstanten:
- QWidget, QDialog, QMessageBox, QToolBar, QMenu, …
- Layouts, Buttons, Labels, LineEdits
- QtKonstanten wie YES, NO, DockAreas
MockModus:
Stellt DummyWidgets bereit, die keine UI öffnen.
### 2. qgiscore_wrapper QGISCoreAbstraktion
Abstraktion für:
- QgsProject
- LayerZugriff
- ProjektMetadaten
- Pfade, CRS, FeatureZugriff
MockModus:
Simuliert ein Projekt und LayerContainer.
### 3. qgisui_wrapper QGISUIAbstraktion
Kapselt UIbezogene QGISFunktionen:
- Zugriff auf iface
- DockManagement
- Menü und ToolbarIntegration
- HauptfensterZugriff
MockModus:
Stellt ein DummyInterface bereit.
### 4. variable_wrapper QGISVariablen
Abstraktion für:
- Projektvariablen (projectScope)
- globale Variablen (globalScope)
- MockSpeicher für Tests
Vorteile:
- keine QGISAbhängigkeit in der Logik
- testbare Variablenverwaltung
- einheitliches API
### 5. message_wrapper Meldungen & Logging
Einheitliche Schnittstelle für:
- Fehlermeldungen
- Warnungen
- InfoMeldungen
- Logging
MockModus:
Speichert Nachrichten statt sie an QGIS zu senden.
### 6. dialog_wrapper BenutzerDialoge
Abstraktion für:
- Ja/NeinDialoge
- spätere Erweiterungen (Eingabedialoge, Dateidialoge, etc.)
MockModus:
Gibt DefaultWerte zurück, öffnet keine UI.
### 7. DockManager & Navigation
Diese Module nutzen die WrapperSchicht, um:
- DockWidgets sicher zu verwalten
- Toolbars und Menüs zu erzeugen
- Reloadsichere UIStrukturen aufzubauen
Sie sind keine Wrapper, sondern WrapperKonsumenten. Alle Fach-Plugins nutzen den Dockmanager des Basisplugins.
## Designprinzipien
1. Single Source of Truth
Jede QGIS oder QtFunktionalität wird nur an einer Stelle implementiert.
2. Austauschbarkeit
MockModus und Echtmodus sind vollständig austauschbar.
3. Testbarkeit
Jede Funktion kann ohne QGIS getestet werden.
4. Saubere Trennung
- UI → qt_wrapper
- QGISCore → qgiscore_wrapper
- QGISUI → qgisui_wrapper
- Logik → settings_logic, layer_logic, prüfmanager, …
5. Erweiterbarkeit
Neue Wrapper können jederzeit ergänzt werden, ohne bestehende Plugins zu brechen.
Jedes Modul wird durch ein Mermaid-ClassDiagram beschrieben. Die Entscheidungen und Exceptions, sowie die behandelten Fehler werden visuell aufbereitet.
Zur Verarbeitung werden alle Nutzerinteraktionen und Angaben zunächst in den zuständigen Prüfer übergeben. Wenn vorhanden, mit den erforderlichen Parametern. Das Ergebnis wird zur Auswertung an den Pruefmanager übergeben. Dieser bereitet das Ergebnis auf, behandelt alle Exceptions und Anwenderentscheidungen und gibt die Daten mit den richtigen Parametern zur Weiterverarbeitung an die eigentliche Funktion.
Der Prüfmanager, die Stile und weitere, universelle Bausteine sind im Plugin sn_basis abgelegt und werden von dort in anderen Modulen verwendet.

View File

@@ -1,9 +1,16 @@
""" """
sn_basis/functions/dialog_wrapper.py Benutzer-Dialoge sn_basis/functions/dialog_wrapper.py Benutzer-Dialoge
Dieser Wrapper kapselt alle Benutzer-Dialoge (z. B. Ja/Nein-Abfragen)
und sorgt dafür, dass sie sowohl in QGIS als auch im Mock-/Testmodus
einheitlich funktionieren.
""" """
from typing import Any from typing import Any
# Import der abstrahierten Qt-Klassen aus dem qt_wrapper.
# QMessageBox, YES und NO sind bereits kompatibel zu Qt5/Qt6
# und im Mock-Modus durch Dummy-Objekte ersetzt.
from sn_basis.functions.qt_wrapper import ( from sn_basis.functions.qt_wrapper import (
QMessageBox, QMessageBox,
YES, YES,
@@ -22,20 +29,34 @@ def ask_yes_no(
parent: Any = None, parent: Any = None,
) -> bool: ) -> bool:
""" """
Fragt den Benutzer eine Ja/Nein-Frage. Stellt dem Benutzer eine Ja/Nein-Frage.
- In Qt: zeigt einen QMessageBox-Dialog - In einer echten QGIS-Umgebung wird ein QMessageBox-Dialog angezeigt.
- Im Mock-Modus: gibt den Default-Wert zurück - Im Mock-/Testmodus wird kein Dialog geöffnet, sondern der Default-Wert
zurückgegeben, damit Tests ohne UI laufen können.
:param title: Titel des Dialogs
:param message: Nachrichtentext
:param default: Rückgabewert im Fehler- oder Mock-Fall
:param parent: Optionales Parent-Widget
:return: True bei "Ja", False bei "Nein"
""" """
try: try:
# Definiert die beiden Buttons, die angezeigt werden sollen.
buttons = QMessageBox.Yes | QMessageBox.No buttons = QMessageBox.Yes | QMessageBox.No
# Öffnet den Dialog (oder im Mock-Modus: simuliert ihn).
result = QMessageBox.question( result = QMessageBox.question(
parent, parent,
title, title,
message, message,
buttons, buttons,
YES if default else NO, YES if default else NO, # Vorauswahl abhängig vom Default
) )
# Gibt True zurück, wenn der Benutzer "Ja" gewählt hat.
return result == YES return result == YES
except Exception: except Exception:
# Falls Qt nicht verfügbar ist (Mock/CI), wird der Default-Wert genutzt.
return default return default

View File

@@ -1,3 +0,0 @@
{
"extraPaths": ["."]
}

View File

@@ -36,7 +36,17 @@ class Navigation:
main_window = get_main_window() main_window = get_main_window()
if not main_window: if not main_window:
return return
# -----------------------------------------
# Vorherige Toolbars entfernen
# -----------------------------------------
for tb in main_window.findChildren(QToolBar):
if tb.objectName() == "LnoSachsenToolbar":
remove_toolbar(tb)
tb.deleteLater()
# -----------------------------------------
# Menü und Toolbar neu erzeugen
# -----------------------------------------
self.menu = QMenu("LNO Sachsen", main_window) self.menu = QMenu("LNO Sachsen", main_window)
add_menu(self.menu) add_menu(self.menu)

View File

@@ -1,87 +1,107 @@
""" #sn_basis/ui/tabs/settings_tab.py
sn_basis/ui/base_dockwidget.py from sn_basis.functions.qt_wrapper import (
QWidget,
Basis-Dockwidget für alle LNO-Module. QGridLayout,
""" QLabel,
QLineEdit,
from sn_basis.functions.qt_wrapper import QDockWidget, QTabWidget QGroupBox,
from sn_basis.functions.message_wrapper import warning, error QVBoxLayout,
QPushButton,
)
from sn_basis.functions.settings_logic import SettingsLogic
class SettingsTab(QDockWidget): class SettingsTab(QWidget):
""" tab_title = "Projekteigenschaften"
Basis-Dockwidget für alle LNO-Module.
- Titel wird automatisch aus base_title + subtitle erzeugt def __init__(self, parent=None):
- Tabs werden dynamisch aus der Klassenvariable 'tabs' erzeugt
- Die zugehörige Toolbar-Action wird beim Schließen zurückgesetzt
"""
base_title = "LNO Sachsen"
tabs = [] # Liste von Tab-Klassen
action = None # Referenz auf die Toolbar-Action
def __init__(self, parent=None, subtitle=""):
super().__init__(parent) super().__init__(parent)
self.logic = SettingsLogic()
# ----------------------------------------------------- main_layout = QVBoxLayout()
# Titel setzen
# -----------------------------------------------------
try:
title = (
self.base_title
if not subtitle
else f"{self.base_title} | {subtitle}"
)
self.setWindowTitle(title)
except Exception as e:
warning("Titel konnte nicht gesetzt werden", str(e))
# ----------------------------------------------------- # -----------------------------
# Tabs erzeugen # Definition der Felder
# ----------------------------------------------------- # -----------------------------
try: self.user_fields = {
tab_widget = QTabWidget() "amt": "Amt:",
"behoerde": "Behörde:",
"landkreis_user": "Landkreis:",
"sachgebiet": "Sachgebiet:",
}
for tab_class in self.tabs: self.project_fields = {
try: "bezeichnung": "Bezeichnung:",
tab_instance = tab_class() "verfahrensnummer": "Verfahrensnummer:",
tab_title = getattr( "gemeinden": "Gemeinde(n):",
tab_class, "landkreise_proj": "Landkreis(e):",
"tab_title", }
tab_class.__name__,
)
tab_widget.addTab(tab_instance, tab_title)
except Exception as e:
error(
"Tab konnte nicht geladen werden",
f"{tab_class}: {e}",
)
self.setWidget(tab_widget) # -----------------------------
# Benutzerspezifische Festlegungen
# -----------------------------
user_group = QGroupBox("Benutzerspezifische Festlegungen")
user_layout = QGridLayout()
self.user_inputs = {}
except Exception as e: for row, (key, label) in enumerate(self.user_fields.items()):
error( input_widget = QLineEdit()
"Tab-Widget konnte nicht initialisiert werden", self.user_inputs[key] = input_widget
str(e),
) user_layout.addWidget(QLabel(label), row, 0)
user_layout.addWidget(input_widget, row, 1)
user_group.setLayout(user_layout)
# -----------------------------
# Projektspezifische Festlegungen
# -----------------------------
project_group = QGroupBox("Projektspezifische Festlegungen")
project_layout = QGridLayout()
self.project_inputs = {}
for row, (key, label) in enumerate(self.project_fields.items()):
input_widget = QLineEdit()
self.project_inputs[key] = input_widget
project_layout.addWidget(QLabel(label), row, 0)
project_layout.addWidget(input_widget, row, 1)
project_group.setLayout(project_layout)
# -----------------------------
# Speichern-Button
# -----------------------------
save_button = QPushButton("Speichern")
save_button.clicked.connect(self.save_data)
# -----------------------------
# Layout zusammenfügen
# -----------------------------
main_layout.addWidget(user_group)
main_layout.addWidget(project_group)
main_layout.addStretch()
main_layout.addWidget(save_button)
self.setLayout(main_layout)
# Daten laden
self.load_data()
# --------------------------------------------------------- # ---------------------------------------------------------
# Dock schließen # Speichern
# --------------------------------------------------------- # ---------------------------------------------------------
def save_data(self):
fields = {
key: widget.text()
for key, widget in {**self.user_inputs, **self.project_inputs}.items()
}
self.logic.save(fields)
def closeEvent(self, event): # ---------------------------------------------------------
""" # Laden
Wird aufgerufen, wenn das Dock geschlossen wird. # ---------------------------------------------------------
Setzt die zugehörige Toolbar-Action zurück. def load_data(self):
""" data = self.logic.load()
try: for key, widget in {**self.user_inputs, **self.project_inputs}.items():
if self.action: widget.setText(data.get(key, ""))
self.action.setChecked(False)
except Exception as e:
warning(
"Toolbar-Status konnte nicht zurückgesetzt werden",
str(e),
)
super().closeEvent(event)