diff --git a/docs/architektur/modulkarte.md b/docs/architektur/modulkarte.md index 7b1f6d1..534de21 100644 --- a/docs/architektur/modulkarte.md +++ b/docs/architektur/modulkarte.md @@ -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 | | `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/base_dockwidget.py` | 126 | `BaseDockWidget`: Titel „LNO Sachsen \| “, 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 \| “, 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/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 | @@ -25,7 +25,7 @@ Legende: ⚠ = über 600 Zeilen, nie komplett laden, per `grep -n "def "` navigi | Datei | Zeilen | Zweck | |---|---:|---| | `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/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) | @@ -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/ui/tabs/working_tab.py` | 873 | | `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 | diff --git a/docs/architektur/schnittstellen.md b/docs/architektur/schnittstellen.md index 5c33dde..5b83751 100644 --- a/docs/architektur/schnittstellen.md +++ b/docs/architektur/schnittstellen.md @@ -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!). - 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. +- **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) diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md index 2c86681..01f4aa9 100644 --- a/docs/status/STATUS.md +++ b/docs/status/STATUS.md @@ -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 (#96: Textumbruch 60 mm, ungleiche Spaltenbreiten, zweizeiliger Basemap-Titel, Mindestbreite 180 mm wie das Kartenschild) auf - `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, PR steht aus. -17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Umgesetzt auf - `Plugin_SN_Basis:feature/100-basemap-themen` (Doku: `Projektwissen:docs/100-basemap-themen`), PR steht aus. + `Plugin_SN_Basis:bugfix/96-legenden-spaltenbreite`, gemergt (PR #97). +17. **Basemap hinzufügen und Thema wechseln (Basis #100).** Gemergt (PR #103, Doku PR #25). 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 (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. Manueller QGIS-Test am 05.10.2026 bestanden. **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`. diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html index bbacea9..d241bb9 100644 --- a/docs/tests/testkatalog.html +++ b/docs/tests/testkatalog.html @@ -119,20 +119,20 @@ Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
-
711Testfälle
-
668grün
+
716Testfälle
+
673grün
35rot (Baseline)
80%mit Docstring
59Testdateien
Verteilung je Paket
-
sn_basis42 Dateien · 459 Testfälle
+
sn_basis42 Dateien · 464 Testfälle
sn_verfahrensgebiet12 Dateien · 208 Testfälle
sn_plan415 Dateien · 44 Testfälle
@@ -147,13 +147,13 @@

sn_basis

-

Plattform-Plugin · Ordner Plugin_SN_Basis · 42 Testdateien · 459 Testfälle

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 42 Testdateien · 464 Testfälle

- -

Tests für sn_basis/ui/base_dockwidget.py::closeEvent (Issue #65).

CloseEventTests 5
flowchart TD
+  

Tests für sn_basis/ui/base_dockwidget.py: closeEvent (Issue #65) und Scroll-Bereich je Tab (Basis #104).

CloseEventTests 5ScrollbereichTests 5
flowchart TD
   root(["test_base_dockwidget.py"])
   class root rootnode
   root --> g1["CloseEventTests (5)"]
@@ -163,16 +163,28 @@
   g1 --> t3("fehler_in_cleanup_wird_abgefangen_und_andere_tabs_laufen_weiter")
   g1 --> t4("setzt_toolbar_action_zurueck")
   g1 --> t5("ohne_action_wird_nichts_zurueckgesetzt")
+  root --> g2["ScrollbereichTests (5)"]
+  class g2 groupnode
+  g2 --> t6("jeder_tab_liegt_in_einem_scrollbereich")
+  g2 --> t7("fachplugin_mit_eigenem_tabaufbau_bekommt_scrollbereich")
+  g2 --> t8("scrollbereich_wird_nicht_doppelt_angelegt")
+  g2 --> t9("setwidget_none_und_andere_widgets_bleiben_unveraendert")
+  g2 --> t10("cleanup_erreicht_tabs_im_scrollbereich")
   class t1 ok
   class t2 ok
   class t3 ok
   class t4 ok
   class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
   classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
   classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
   classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
   classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
-  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.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_ohne_action_wird_nichts_zurueckgesetzt
Prüft, dass closeEvent ohne verknüpfte Toolbar-Action keine Ausnahme wirft.grün

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 10
flowchart TD
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
CloseEventTests
test_ruft_cleanup_aller_tabs_auf
Prüft, dass closeEvent cleanup() auf allen Tabs des Dock-Widgets aufruft.grün
CloseEventTests
test_ignoriert_tabs_ohne_cleanup
Prüft, dass ein Tab ohne cleanup()-Methode nicht zu einem Fehler führt.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_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

Tests für die Anbindung der Basemap-Legende an Druckablauf, Dialog und Druck-Tab (Basis #88):

TestMehrfachauswahl 1TestDruckanbindung 4TestBasemapBereich 10
flowchart TD
   root(["test_basemap_anbindung.py"])
   class root rootnode
   root --> g1["TestMehrfachauswahl (1)"]
diff --git a/docs/tests/testkatalog.md b/docs/tests/testkatalog.md
index 914c237..6af84ed 100644
--- a/docs/tests/testkatalog.md
+++ b/docs/tests/testkatalog.md
@@ -9,17 +9,17 @@ selbst (Docstrings) und aus der Testbaseline.
 
 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.
-Mit Beschreibung (Docstring): 566 von 711 (80 %).
+Gesamt: 3 Pakete, 59 Testdateien, 716 Testfälle – 673 grün, 35 rot (Baseline), 8 übersprungen.
+Mit Beschreibung (Docstring): 571 von 716 (80 %).
 
 ## Zusammenfassung
 
 | 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_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
 
@@ -31,15 +31,16 @@ Mit Beschreibung (Docstring): 566 von 711 (80 %).
 
 ## 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`
 
-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:
 
 - `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 |
 | --- | --- | --- |
@@ -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_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 |
+| `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`
 
@@ -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,
 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.
 
diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md
index 159b27d..09f5841 100644
--- a/docs/wissen/fallstricke.md
+++ b/docs/wissen/fallstricke.md
@@ -92,6 +92,11 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik
 
 ## 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
   Element einmal gezeichnet wurde – beim Anlegen per Code ändert es nichts. Platzbedarf vorher über
   `QgsLegendRenderer(legende.model(), einstellungen).minimumSize(QgsLayoutUtils.createRenderContextForLayout(layout, None))`