From 039c6145922a27f94812303a4bbae5f6351a76b7 Mon Sep 17 00:00:00 2001 From: daniel Date: Fri, 9 Jan 2026 15:19:25 +0100 Subject: [PATCH] =?UTF-8?q?Fix:=20beim=20Plugin-Reload=20werden=20neue=20T?= =?UTF-8?q?oolbars=20hinzugef=C3=BCgt=20aber=20keine=20gel=C3=B6scht=20Fix?= =?UTF-8?q?:=20Settings-Tab=20ist=20leer=20Dokumentation=20begonnen=20Plug?= =?UTF-8?q?inkonzept.md=20=C3=BCberarbeitet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/Objektstruktur.txt | 282 ++++++++++++++++++++++-------------- assets/Pluginkonzept.md | 156 +++++++++++++++++--- functions/dialog_wrapper.py | 29 +++- pyrightconfig.json | 3 - ui/navigation.py | 12 +- ui/tabs/settings_tab.py | 168 +++++++++++---------- 6 files changed, 443 insertions(+), 207 deletions(-) delete mode 100644 pyrightconfig.json diff --git a/assets/Objektstruktur.txt b/assets/Objektstruktur.txt index df3e392..0cf5e72 100644 --- a/assets/Objektstruktur.txt +++ b/assets/Objektstruktur.txt @@ -1,122 +1,188 @@ -+ PluginController - └─ GUIManager - └─ PrüfManager (koordiniert alle Prüfer) - ├─ Dateiprüfer - ├─ Linklistenprüfer - │ └─ Zeilenprüfer[n] - │ ├─ Linkprüfer - │ └─ Stilprüfer - └─ LayerLoader - └─ Logger +# Wrapper‑Architektur – Übersicht +Die Wrapper‑Architektur von sn_basis bildet das Fundament für eine robuste, testbare und zukunftssichere QGIS‑Plugin‑Entwicklung. +Sie kapselt sämtliche QGIS‑ und Qt‑Abhängigkeiten hinter klar definierten Schnittstellen und ermöglicht dadurch: -Plan41_plugin/ -│ -├── 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 +Mock‑fähige Unit‑Tests ohne QGIS +PyQt5/6‑Kompatibilität ohne Code‑Änderungen -+------------------------------------+ -| PluginController | -+------------------------------------+ -| - Dock_widget: GUIManager | -| - pruef_manager: PruefManager | -| - loader: LayerLoader | -| - logger: Logger | -+------------------------------------+ -| + start(): void | -+------------------------------------+ +saubere Trennung von UI, Logik und Infrastruktur -+------------------------------------+ -| GUIManager | -+------------------------------------+ -| - dialog: QWidget | -+------------------------------------+ -| + getParameter(): dict | -+------------------------------------+ +stabile APIs, die unabhängig von QGIS‑Versionen bleiben -+------------------------------------+ -| PruefManager | -+------------------------------------+ -| - dateipruefer: Dateipruefer | -| - linklistenpruefer: Linklisten... | -+------------------------------------+ -| + pruefe_alle(parameter): list | -+------------------------------------+ +klare Erweiterbarkeit für zukünftige Module und Plugins -+------------------------------------+ -| Dateipruefer | -+------------------------------------+ -| + pruefe(pfad: str): PruefErgebnis | -+------------------------------------+ +Die Wrapper‑Schicht ist das zentrale Bindeglied zwischen der Plugin‑Logik und der QGIS‑/Qt‑Umgebung. -+------------------------------------+ -| Linklistenpruefer | -+------------------------------------+ -| + pruefe(pfad: str): list[Zeile] | -+------------------------------------+ +## Ziele der Wrapper‑Architektur +🎯 1. Entkopplung von QGIS und Qt +Alle direkten Importe wie from qgis.core import ... oder from qgis.PyQt.QtWidgets import ... verschwinden aus der Plugin‑Logik. +Stattdessen werden sie über Wrapper‑Module abstrahiert. -+------------------------------------+ -| Zeilenpruefer | -+------------------------------------+ -| - linkpruefer: Linkpruefer | -| - stilpruefer: Stilpruefer | -+------------------------------------+ -| + pruefe(zeile: str): LayerAuftrag | -+------------------------------------+ +🎯 2. Testbarkeit ohne QGIS +Im Mock‑Modus liefern die Wrapper: -+------------------------------------+ -| Linkpruefer | -+------------------------------------+ -| + pruefe(link: str): PruefErgebnis | -+------------------------------------+ +Dummy‑Objekte -+------------------------------------+ -| Stilpruefer | -+------------------------------------+ -| + pruefe(stilname: str): Pruef... | -+------------------------------------+ +simulierte Rückgabewerte -+------------------------------------+ -| LayerLoader | -+------------------------------------+ -| + lade(layer_auftrag): void | -+------------------------------------+ +speicherbare Zustände (z. B. Variablen, Layer, Nachrichten) -+------------------------------------+ -| Logger | -+------------------------------------+ -| + schreibe(msg: str): void | -| + exportiere(): file | -+------------------------------------+ +Damit laufen Tests in jeder CI‑Umgebung. -+------------------------------------+ -| PruefErgebnis | -+------------------------------------+ -| - erfolgreich: bool | -| - daten: dict | -| - fehler: list[str] | -| - warnungen: list[str] | -+------------------------------------+ +🎯 3. Einheitliche API für alle Plugins +Plugins greifen nicht mehr direkt auf QGIS zu, sondern nutzen: +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 Qt‑API, wird nur der Wrapper angepasst, nicht jedes Plugin. + +## Architekturüberblick +Die Wrapper‑Schicht besteht aus mehreren Modulen, die jeweils einen klar abgegrenzten Verantwortungsbereich haben. + +### 1. qt_wrapper – Qt‑Abstraktion +Kapselt alle Qt‑Widgets, Dialoge und Konstanten: + +QWidget, QDialog, QMessageBox, QToolBar, QMenu, … + +Layouts, Buttons, Labels, LineEdits + +Qt‑Konstanten wie YES, NO, Dock‑Areas + +Mock‑Modus: +Stellt Dummy‑Widgets bereit, die keine UI öffnen. + +### 2. qgiscore_wrapper – QGIS‑Core‑Abstraktion +Abstraktion für: + +QgsProject + +Layer‑Zugriff + +Projekt‑Metadaten + +Pfade, CRS, Feature‑Zugriff + +Mock‑Modus: +Simuliert ein Projekt und Layer‑Container. + +### 3. qgisui_wrapper – QGIS‑UI‑Abstraktion +Kapselt UI‑bezogene QGIS‑Funktionen: + +Zugriff auf iface + +Dock‑Management + +Menü‑ und Toolbar‑Integration + +Hauptfenster‑Zugriff + +Mock‑Modus: +Stellt ein Dummy‑Interface bereit. + +### 4. variable_wrapper – QGIS‑Variablen +Abstraktion für: + +Projektvariablen (projectScope) + +globale Variablen (globalScope) + +Mock‑Speicher für Tests + +Vorteile: + +keine QGIS‑Abhängigkeit in der Logik + +testbare Variablenverwaltung + +einheitliches API + +### 5. message_wrapper – Meldungen & Logging +Einheitliche Schnittstelle für: + +Fehlermeldungen + +Warnungen + +Info‑Meldungen + +Logging + +Mock‑Modus: +Speichert Nachrichten statt sie an QGIS zu senden. + +### 6. dialog_wrapper – Benutzer‑Dialoge +Abstraktion für: + +Ja/Nein‑Dialoge + +spätere Erweiterungen (Eingabedialoge, Dateidialoge, etc.) + +Mock‑Modus: +Gibt Default‑Werte zurück, öffnet keine UI. + +### 7. DockManager & Navigation +Diese Module nutzen die Wrapper‑Schicht, um: + +DockWidgets sicher zu verwalten + +Toolbars und Menüs zu erzeugen + +Reload‑sichere UI‑Strukturen aufzubauen + +Sie sind keine Wrapper, sondern Wrapper‑Konsumenten. + +## Designprinzipien +🧱 1. Single Source of Truth +Jede QGIS‑ oder Qt‑Funktionalität wird nur an einer Stelle implementiert. + +🔄 2. Austauschbarkeit +Mock‑Modus und Echtmodus sind vollständig austauschbar. + +🧪 3. Testbarkeit +Jede Funktion kann ohne QGIS getestet werden. + +🧼 4. Saubere Trennung +UI → qt_wrapper + +QGIS‑Core → qgiscore_wrapper + +QGIS‑UI → 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 QGIS‑Abhängigkeiten in der Logik + +IDE‑freundlich (Pylance, Autocomplete, Typing) + +CI‑fähig (Tests ohne QGIS) + +saubere Architektur + +leichte Wartbarkeit + +klare Dokumentation + +## Fazit +Die Wrapper‑Architektur ist das Herzstück von sn_basis. +Sie ermöglicht eine moderne, modulare und testbare QGIS‑Plugin‑Entwicklung, die unabhängig von QGIS‑Versionen, Qt‑Versionen und Entwicklungsumgebungen funktioniert. + +Sie bildet die Grundlage für: + +stabile APIs + +saubere UI‑Abstraktion + +automatisierte Tests + +nachhaltige Weiterentwicklung \ No newline at end of file diff --git a/assets/Pluginkonzept.md b/assets/Pluginkonzept.md index 14f41dc..b00f2d4 100644 --- a/assets/Pluginkonzept.md +++ b/assets/Pluginkonzept.md @@ -1,22 +1,144 @@ -**Pluginkonzept** -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 Module sind als Python-Objekte angelegt. -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. +# Wrapper‑Architektur – Übersicht +Die Wrapper‑Architektur von sn_basis bildet das Fundament für eine robuste, testbare und zukunftssichere QGIS‑Plugin‑Entwicklung. +Sie kapselt sämtliche QGIS‑ und Qt‑Abhängigkeiten hinter klar definierten Schnittstellen und ermöglicht dadurch: -| Modul | Aufgabe | Beschreibung | -|-------------------|---------------------------------------|--------------| -|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| -|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| -|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| -|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| +- Mock‑fähige Unit‑Tests ohne QGIS +- PyQt5/6‑Kompatibilität ohne Code‑Änderungen +- saubere Trennung von UI, Logik und Infrastruktur +- stabile APIs, die unabhängig von QGIS‑Versionen bleiben +- klare Erweiterbarkeit für zukünftige Module und Plugins -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 Wrapper‑Schicht ist das zentrale Bindeglied zwischen der Plugin‑Logik und der QGIS‑/Qt‑Umgebung. + +## Ziele der Wrapper‑Architektur +1. Entkopplung von QGIS und Qt +Alle direkten Importe wie from qgis.core import ... oder from qgis.PyQt.QtWidgets import ... verschwinden aus der Plugin‑Logik. +Stattdessen werden sie über Wrapper‑Module abstrahiert. + +2. Testbarkeit ohne QGIS +Im Mock‑Modus liefern die Wrapper: + +- Dummy‑Objekte +- simulierte Rückgabewerte +- speicherbare Zustände (z. B. Variablen, Layer, Nachrichten) + +Damit laufen Tests in jeder CI‑Umgebung. + +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 Qt‑API, wird nur der Wrapper angepasst, nicht jedes Plugin. + +## Architekturüberblick +Die Wrapper‑Schicht besteht aus mehreren Modulen, die jeweils einen klar abgegrenzten Verantwortungsbereich haben. + +### 1. qt_wrapper – Qt‑Abstraktion +Kapselt alle Qt‑Widgets, Dialoge und Konstanten: + +- QWidget, QDialog, QMessageBox, QToolBar, QMenu, … +- Layouts, Buttons, Labels, LineEdits +- Qt‑Konstanten wie YES, NO, Dock‑Areas + +Mock‑Modus: +Stellt Dummy‑Widgets bereit, die keine UI öffnen. + +### 2. qgiscore_wrapper – QGIS‑Core‑Abstraktion +Abstraktion für: + +- QgsProject +- Layer‑Zugriff +- Projekt‑Metadaten +- Pfade, CRS, Feature‑Zugriff + +Mock‑Modus: +Simuliert ein Projekt und Layer‑Container. + +### 3. qgisui_wrapper – QGIS‑UI‑Abstraktion +Kapselt UI‑bezogene QGIS‑Funktionen: + +- Zugriff auf iface +- Dock‑Management +- Menü‑ und Toolbar‑Integration +- Hauptfenster‑Zugriff + +Mock‑Modus: +Stellt ein Dummy‑Interface bereit. + +### 4. variable_wrapper – QGIS‑Variablen +Abstraktion für: + +- Projektvariablen (projectScope) +- globale Variablen (globalScope) +- Mock‑Speicher für Tests + +Vorteile: + +- keine QGIS‑Abhängigkeit in der Logik +- testbare Variablenverwaltung +- einheitliches API + +### 5. message_wrapper – Meldungen & Logging +Einheitliche Schnittstelle für: + +- Fehlermeldungen +- Warnungen +- Info‑Meldungen +- Logging + +Mock‑Modus: +Speichert Nachrichten statt sie an QGIS zu senden. + +### 6. dialog_wrapper – Benutzer‑Dialoge +Abstraktion für: + +- Ja/Nein‑Dialoge +- spätere Erweiterungen (Eingabedialoge, Dateidialoge, etc.) + +Mock‑Modus: +Gibt Default‑Werte zurück, öffnet keine UI. + +### 7. DockManager & Navigation +Diese Module nutzen die Wrapper‑Schicht, um: + +- DockWidgets sicher zu verwalten +- Toolbars und Menüs zu erzeugen +- Reload‑sichere UI‑Strukturen aufzubauen + +Sie sind keine Wrapper, sondern Wrapper‑Konsumenten. Alle Fach-Plugins nutzen den Dockmanager des Basisplugins. + +## Designprinzipien +1. Single Source of Truth +Jede QGIS‑ oder Qt‑Funktionalität wird nur an einer Stelle implementiert. + +2. Austauschbarkeit +Mock‑Modus und Echtmodus sind vollständig austauschbar. + +3. Testbarkeit +Jede Funktion kann ohne QGIS getestet werden. + +4. Saubere Trennung +- UI → qt_wrapper +- QGIS‑Core → qgiscore_wrapper +- QGIS‑UI → 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. diff --git a/functions/dialog_wrapper.py b/functions/dialog_wrapper.py index 43e7654..3ed9c41 100644 --- a/functions/dialog_wrapper.py +++ b/functions/dialog_wrapper.py @@ -1,9 +1,16 @@ """ 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 +# 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 ( QMessageBox, YES, @@ -22,20 +29,34 @@ def ask_yes_no( parent: Any = None, ) -> bool: """ - Fragt den Benutzer eine Ja/Nein-Frage. + Stellt dem Benutzer eine Ja/Nein-Frage. - - In Qt: zeigt einen QMessageBox-Dialog - - Im Mock-Modus: gibt den Default-Wert zurück + - In einer echten QGIS-Umgebung wird ein QMessageBox-Dialog angezeigt. + - 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: + # Definiert die beiden Buttons, die angezeigt werden sollen. buttons = QMessageBox.Yes | QMessageBox.No + + # Öffnet den Dialog (oder im Mock-Modus: simuliert ihn). result = QMessageBox.question( parent, title, message, 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 + except Exception: + # Falls Qt nicht verfügbar ist (Mock/CI), wird der Default-Wert genutzt. return default diff --git a/pyrightconfig.json b/pyrightconfig.json deleted file mode 100644 index 17b70df..0000000 --- a/pyrightconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extraPaths": ["."] -} diff --git a/ui/navigation.py b/ui/navigation.py index d1d9a05..35bbf45 100644 --- a/ui/navigation.py +++ b/ui/navigation.py @@ -36,7 +36,17 @@ class Navigation: main_window = get_main_window() if not main_window: 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) add_menu(self.menu) diff --git a/ui/tabs/settings_tab.py b/ui/tabs/settings_tab.py index 83a1571..fdacd5e 100644 --- a/ui/tabs/settings_tab.py +++ b/ui/tabs/settings_tab.py @@ -1,87 +1,107 @@ -""" -sn_basis/ui/base_dockwidget.py - -Basis-Dockwidget für alle LNO-Module. -""" - -from sn_basis.functions.qt_wrapper import QDockWidget, QTabWidget -from sn_basis.functions.message_wrapper import warning, error +#sn_basis/ui/tabs/settings_tab.py +from sn_basis.functions.qt_wrapper import ( + QWidget, + QGridLayout, + QLabel, + QLineEdit, + QGroupBox, + QVBoxLayout, + QPushButton, +) +from sn_basis.functions.settings_logic import SettingsLogic -class SettingsTab(QDockWidget): - """ - Basis-Dockwidget für alle LNO-Module. +class SettingsTab(QWidget): + tab_title = "Projekteigenschaften" - - Titel wird automatisch aus base_title + subtitle erzeugt - - 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=""): + def __init__(self, parent=None): super().__init__(parent) + self.logic = SettingsLogic() - # ----------------------------------------------------- - # 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)) + main_layout = QVBoxLayout() - # ----------------------------------------------------- - # Tabs erzeugen - # ----------------------------------------------------- - try: - tab_widget = QTabWidget() + # ----------------------------- + # Definition der Felder + # ----------------------------- + self.user_fields = { + "amt": "Amt:", + "behoerde": "Behörde:", + "landkreis_user": "Landkreis:", + "sachgebiet": "Sachgebiet:", + } - for tab_class in self.tabs: - try: - tab_instance = tab_class() - tab_title = getattr( - tab_class, - "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.project_fields = { + "bezeichnung": "Bezeichnung:", + "verfahrensnummer": "Verfahrensnummer:", + "gemeinden": "Gemeinde(n):", + "landkreise_proj": "Landkreis(e):", + } - self.setWidget(tab_widget) + # ----------------------------- + # Benutzerspezifische Festlegungen + # ----------------------------- + user_group = QGroupBox("Benutzerspezifische Festlegungen") + user_layout = QGridLayout() + self.user_inputs = {} - except Exception as e: - error( - "Tab-Widget konnte nicht initialisiert werden", - str(e), - ) + for row, (key, label) in enumerate(self.user_fields.items()): + input_widget = QLineEdit() + self.user_inputs[key] = input_widget + + 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): - """ - Wird aufgerufen, wenn das Dock geschlossen wird. - Setzt die zugehörige Toolbar-Action zurück. - """ - try: - if self.action: - self.action.setChecked(False) - except Exception as e: - warning( - "Toolbar-Status konnte nicht zurückgesetzt werden", - str(e), - ) - - super().closeEvent(event) + # --------------------------------------------------------- + # Laden + # --------------------------------------------------------- + def load_data(self): + data = self.logic.load() + for key, widget in {**self.user_inputs, **self.project_inputs}.items(): + widget.setText(data.get(key, ""))