Scroll-Bereich der LNO-Docks (Basis #104) dokumentiert

Schnittstellen Abschnitt 2, Fallstricke, Modulkarte, Testkatalog und
STATUS (Punkt 18; #96 und #100 als gemergt vermerkt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 21:53:50 +02:00
co-authored by Claude Opus 5.5
parent c1c628ac97
commit e5c7d17831
6 changed files with 51 additions and 21 deletions
+3 -3
View File
@@ -15,7 +15,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
| `main.py` | 64 | `BasisPlugin`: baut `Navigation`, startet Verfahrensgebiet-Listener, ruft `initGui()` aller `sn_*`-Plugins nach, räumt beim Unload auf | | `main.py` | 64 | `BasisPlugin`: baut `Navigation`, startet Verfahrensgebiet-Listener, ruft `initGui()` aller `sn_*`-Plugins nach, räumt beim Unload auf |
| `ui/navigation.py` | 127 | Menü und Toolbar „LNO Sachsen“; `add_action(text, callback, tooltip, priority)`, `finalize_menu_and_toolbar()`, `set_active_plugin()`. Enthält noch Debug-`print` und eine `TEST ACTION` | | `ui/navigation.py` | 127 | Menü und Toolbar „LNO Sachsen“; `add_action(text, callback, tooltip, priority)`, `finalize_menu_and_toolbar()`, `set_active_plugin()`. Enthält noch Debug-`print` und eine `TEST ACTION` |
| `ui/dockmanager.py` | 86 | `DockManager.show(dock)`: entfernt alle anderen `sn_dock_*`-Docks, zeigt das neue rechts an | | `ui/dockmanager.py` | 86 | `DockManager.show(dock)`: entfernt alle anderen `sn_dock_*`-Docks, zeigt das neue rechts an |
| `ui/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| <Untertitel>“, Tabs aus Klassenattribut `tabs`, ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück | | `ui/base_dockwidget.py` | 172 | `BaseDockWidget`: Titel „LNO Sachsen \| <Untertitel>“, Tabs aus Klassenattribut `tabs`, jeder Tab in einem Scroll-Bereich (`setWidget`, #104), ruft `cleanup()` der Tabs beim Schließen, setzt Toolbar-Action zurück |
| `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) | | `ui/tabs/settings_tab.py` | 158 | Tab „Projekteigenschaften“: Benutzer- und Projektfelder → Projektvariablen (`SettingsLogic`) |
| `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` | | `ui/tabs/print_tab.py` ⚠ | 730 | Tab „Druck“: Druckbereich, Maßstab, Format, Themen, Legende, Atlas, Basemap-Bereich; nutzt `print_logic` |
| `ui/widgets/basemap_bereich.py` | 433 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen | | `ui/widgets/basemap_bereich.py` | 433 | Einklappbarer Bereich „Basemap“ im Druck-Tab: Basemap hinzufügen und Thema wechseln (#100), Auswertebereich, Legende aktualisieren, Stilvorlagen |
@@ -25,7 +25,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi
| Datei | Zeilen | Zweck | | Datei | Zeilen | Zweck |
|---|---:|---| |---|---:|---|
| `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) | | `functions/__init__.py` | 50 | Re-Export vieler Wrapper (`from sn_basis.functions import QgsProject, info, ...`) |
| `functions/qt_wrapper.py` ⚠ | 832 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` | | `functions/qt_wrapper.py` ⚠ | 873 | Qt-Symbole: PyQt6 primär, PyQt5-Fallback, sonst Mock. `QT_VERSION` (0/5/6), Enum-Aliase (`DockAreaRight`, …), `exec_dialog()` |
| `functions/qgiscore_wrapper.py` ⚠ | 1 428 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` | | `functions/qgiscore_wrapper.py` ⚠ | 1 428 | `qgis.core`-Symbole und Konstanten; ab Zeile ~255 Mock-Klassen; eigener Block für Vektorkacheln/Ausdrücke/Legende (#88); Helfer `network_head`, `get_layer_extent`, `create_buffer_layer`, `layer_exists_in_gpkg`, `polygon_zu_linie` |
| `functions/qgisui_wrapper.py` | 271 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, aktiver Layer (`get_active_layer`); Mock-`iface` | | `functions/qgisui_wrapper.py` | 271 | `iface`-Zugriffe: Hauptfenster, Docks, Menüs, Toolbars, Layoutdesigner, aktiver Layer (`get_active_layer`); Mock-`iface` |
| `functions/dialog_wrapper.py` | 570 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ask_auswahl` (frei benannte Knöpfe), `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) | | `functions/dialog_wrapper.py` | 570 | Dialoge: `ask_yes_no`, `show_info_dialog`, `ask_text`, Überschreiben/Anhängen/Abbrechen, `ask_auswahl` (frei benannte Knöpfe), `ProgressDialog`/`create_progress_dialog`, `ask_mehrfachauswahl` (Liste mit Häkchen) |
@@ -146,5 +146,5 @@ Dient als **minimales Muster** für ein neues Fachplugin (Skill `sn-neues-fachpl
| `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 | | `sn_verfahrensgebiet/functions/verfahrensgebiet_shape_komplett.py` | 978 |
| `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 | | `sn_verfahrensgebiet/ui/tabs/working_tab.py` | 873 |
| `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 | | `sn_verfahrensgebiet/functions/knickpunkt_pruefung.py` | 810 |
| `sn_basis/functions/qt_wrapper.py` | 832 | | `sn_basis/functions/qt_wrapper.py` | 873 |
| `sn_basis/ui/tabs/print_tab.py` | 730 | | `sn_basis/ui/tabs/print_tab.py` | 730 |
+5
View File
@@ -40,6 +40,11 @@ Vertrag für ein Fachplugin (Muster: `Plugin_SN_Widmung/main.py`):
Optional `set_services(pruefmanager=..., data_grabber=...)` (wird von Fachplugin-Docks aufgerufen) und `cleanup()` (wird beim Schließen des Docks aufgerufen – Signale trennen!). Optional `set_services(pruefmanager=..., data_grabber=...)` (wird von Fachplugin-Docks aufgerufen) und `cleanup()` (wird beim Schließen des Docks aufgerufen – Signale trennen!).
- Gemeinsame Tabs aus `sn_basis`: `ui.tabs.print_tab.PrintTab` (Druck), `ui.tabs.settings_tab.SettingsTab` (Projekteigenschaften). - Gemeinsame Tabs aus `sn_basis`: `ui.tabs.print_tab.PrintTab` (Druck), `ui.tabs.settings_tab.SettingsTab` (Projekteigenschaften).
- `dock.action` = Referenz auf die Toolbar-Action; `BaseDockWidget.closeEvent` setzt sie zurück. - `dock.action` = Referenz auf die Toolbar-Action; `BaseDockWidget.closeEvent` setzt sie zurück.
- **Scroll-Bereich je Tab (Basis #104):** `BaseDockWidget.setWidget(widget)` packt jeden Tab eines `QTabWidget` in einen
`QScrollArea` (`widgetResizable`, ohne Rahmen; Funktion `base_dockwidget.tabs_mit_scrollbereich`). Das gilt auch für
Fachplugins, die ihre Tabs in `_init_tabs_with_services()` selbst aufbauen und per `setWidget` einsetzen. Folge:
`dock.widget().widget(i)` liefert den Scroll-Bereich, die Tab-Instanz ist `….widget(i).widget()`; das Eltern-Widget
eines Tabs ist der Viewport des Scroll-Bereichs, nicht mehr das `QTabWidget`. Tabs vor `setWidget` vollständig hinzufügen.
## 3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt) ## 3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt)
+5 -3
View File
@@ -70,12 +70,14 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek
16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Gemergt (PR #95). Mehrspaltige Legenden schmaler 16. **Kartenrand-Überlappung bei kleinen Verfahren (Basis #93).** Gemergt (PR #95). Mehrspaltige Legenden schmaler
(#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das
Kartenschild) auf Kartenschild) auf
`Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus. `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, gemergt (PR #97).
17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Umgesetzt auf 17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Gemergt (PR #103, Doku PR #25).
`Plugin_SN_Basis:feature/100-basemap-themen` (Doku: `Projektwissen:docs/100-basemap-themen`), PR steht aus.
Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test Themen Farbe/Grau, Erkennung über Kachel-URL, Prüfwert für ungespeicherte Änderungen. Manueller QGIS-Test
am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101 am 05.10.2026 bestanden (Laden über `styleUrl`, Themenwechsel per `setDataSource`, Prüfwert stabil). Folge-Issues: #101
(Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief. (Import/Export der Stile), P10 Vektor (eigenes Issue, Entwurf liegt vor), Relief.
18. **Langer Tab verlängert das QGIS-Fenster (Basis #104).** Behoben auf `Plugin_SN_Basis:bugfix/104-dock-scrollbereich`
(Doku: `Projektwissen:docs/104-dock-scrollbereich`), PR steht aus: jeder Tab im Scroll-Bereich über
`BaseDockWidget.setWidget`, wirkt auch für Plan41/Verfahrensgebiet. Offen: QGIS-Test (3.44 und 4).
**Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45) **Hinweis zum lokalen Stand (04.09.2026):** Die Klone `Plugin_SN_Verfahrensgebiet` (4 Commits: PR #43, PR #45)
und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`. und `Repository` (Release 26.8.4-unstable) liegen hinter `origin`. Aktualisieren mit `python scripts/hallo.py`.
File diff suppressed because one or more lines are too long
+13 -7
View File
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`) Stand der Baseline: `docs/status/testbaseline.json` (SHA-256 `aeabc946a7f3`)
Gesamt: 3 Pakete, 59 Testdateien, 711 Testfälle – 668 grün, 35 rot (Baseline), 8 übersprungen. Gesamt: 3 Pakete, 59 Testdateien, 716 Testfälle – 673 grün, 35 rot (Baseline), 8 übersprungen.
Mit Beschreibung (Docstring): 566 von 711 (80 %). Mit Beschreibung (Docstring): 571 von 716 (80 %).
## Zusammenfassung ## Zusammenfassung
| Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung | | Paket | Testdateien | Testfälle | grün | rot (Baseline) | übersprungen | mit Beschreibung |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | | --- | ---: | ---: | ---: | ---: | ---: | ---: |
| `sn_basis` | 42 | 459 | 459 | 0 | 0 | 459 von 459 (100 %) | | `sn_basis` | 42 | 464 | 464 | 0 | 0 | 464 von 464 (100 %) |
| `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) | | `sn_verfahrensgebiet` | 12 | 208 | 166 | 34 | 8 | 106 von 208 (51 %) |
| `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) | | `sn_plan41` | 5 | 44 | 43 | 1 | 0 | 1 von 44 (2 %) |
| **Summe** | **59** | **711** | **668** | **35** | **8** | **566 von 711 (80 %)** | | **Summe** | **59** | **716** | **673** | **35** | **8** | **571 von 716 (80 %)** |
### Legende ### Legende
@@ -31,15 +31,16 @@ Mit Beschreibung (Docstring): 566 von 711 (80 %).
## Paket `sn_basis` ## Paket `sn_basis`
Plattform-Plugin, Ordner `Plugin_SN_Basis` – 42 Testdateien mit 459 Testfällen (459 grün, 0 rot, 0 übersprungen). Plattform-Plugin, Ordner `Plugin_SN_Basis` – 42 Testdateien mit 464 Testfällen (464 grün, 0 rot, 0 übersprungen).
### `Plugin_SN_Basis/tests/test_base_dockwidget.py` ### `Plugin_SN_Basis/tests/test_base_dockwidget.py`
Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65). Worum es in der Datei geht: Tests für sn_basis/ui/base_dockwidget.py: closeEvent (Issue #65) und Scroll-Bereich je Tab (Basis #104).
Testgruppen: Testgruppen:
- `CloseEventTests` (5 Testfälle): — (Docstring fehlt) - `CloseEventTests` (5 Testfälle): — (Docstring fehlt)
- `ScrollbereichTests` (5 Testfälle): Lange Tabs dürfen das QGIS-Fenster nicht über den Bildschirmrand schieben (Basis #104).
| Testfall | prüft | Status | | Testfall | prüft | Status |
| --- | --- | --- | | --- | --- | --- |
@@ -48,6 +49,11 @@ Testgruppen:
| `CloseEventTests.test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter` | Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert. | grün | | `CloseEventTests.test_fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter` | Prüft, dass ein Fehler in cleanup() eines Tabs nicht die übrigen Tabs verhindert. | grün |
| `CloseEventTests.test_setzt_toolbar_action_zurueck` | Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt. | grün | | `CloseEventTests.test_setzt_toolbar_action_zurueck` | Prüft, dass closeEvent die verknüpfte Toolbar-Action auf 'nicht gedrückt' zurücksetzt. | grün |
| `CloseEventTests.test_ohne_action_wird_nichts_zurueckgesetzt` | Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft. | grün | | `CloseEventTests.test_ohne_action_wird_nichts_zurueckgesetzt` | Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft. | grün |
| `ScrollbereichTests.test_jeder_tab_liegt_in_einem_scrollbereich` | Prüft, dass BaseDockWidget jeden Tab in einen mitwachsenden Scroll-Bereich packt, damit ein langer Tab scrollt, statt das Fenster zu verlängern. | grün |
| `ScrollbereichTests.test_fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich` | Prüft, dass auch ein Fachplugin, das seine Tabs selbst aufbaut und per setWidget einsetzt (wie Plan41, Verfahrensgebiet), Scroll-Bereiche bekommt. | grün |
| `ScrollbereichTests.test_scrollbereich_wird_nicht_doppelt_angelegt` | Prüft, dass ein Tab, der schon in einem Scroll-Bereich liegt, beim erneuten Einsetzen nicht ein zweites Mal verpackt wird. | grün |
| `ScrollbereichTests.test_setwidget_none_und_andere_widgets_bleiben_unveraendert` | Prüft, dass setWidget(None) und Widgets ohne Tabs ohne Fehler und unverändert eingesetzt werden. | grün |
| `ScrollbereichTests.test_cleanup_erreicht_tabs_im_scrollbereich` | Prüft, dass closeEvent cleanup() auch für Tabs aufruft, die in einem Scroll-Bereich liegen. | grün |
### `Plugin_SN_Basis/tests/test_basemap_anbindung.py` ### `Plugin_SN_Basis/tests/test_basemap_anbindung.py`
@@ -1507,7 +1513,7 @@ Arbeitsliste: Diese Testfälle haben keinen Docstring; was sie fachlich prüfen,
aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall, aus dem Quelltext erschließen. Jeder Eintrag braucht eine einzeilige Beschreibung im Testfall,
dann verschwindet er hier von selbst. dann verschwindet er hier von selbst.
### `sn_basis` – 0 von 459 Testfällen ohne Beschreibung ### `sn_basis` – 0 von 464 Testfällen ohne Beschreibung
Keine Lücken. Keine Lücken.
+5
View File
@@ -92,6 +92,11 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
## Code-Konventionen, die man kennen muss ## Code-Konventionen, die man kennen muss
- **Dock-Inhalt ohne Scroll-Bereich verlängert das QGIS-Fenster.** Qt verlangt für ein Dock mindestens die Höhe seines
Inhalts (beim `QTabWidget`: des höchsten Tabs). Ist sie größer als der Bildschirm, wächst das Hauptfenster nach unten
aus dem Bild, statt dass ein Scrollbalken erscheint. Abhilfe: Inhalt in `QScrollArea` mit `setWidgetResizable(True)`;
in den LNO-Docks erledigt das `BaseDockWidget.setWidget` (Basis #104). Die VLN-Plugins („VLN Karte“ 2.0, „VLN Karten“
0.5) haben keine eigene Lösung, ihr Inhalt ist nur kurz genug. (05.10.2026)
- **Legendenelemente im Layout (QGIS 3.44):** `QgsLayoutItemLegend.adjustBoxSize()` wirkt erst, nachdem das - **Legendenelemente im Layout (QGIS 3.44):** `QgsLayoutItemLegend.adjustBoxSize()` wirkt erst, nachdem das
Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über
`QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))` `QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))`