# Plan: QGIS 3.40 Exit-Crash absichern Der wahrscheinlichste Auslöser ist fehlendes Signal-Cleanup beim Plugin-Unload: globale Projekt-Listener und PrintTab-Listener bleiben aktiv, während QgsProject beim QGIS-Exit bereits zerstört wird. Die empfohlene Lösung ist ein minimaler, versionsneutraler Cleanup-Pfad (disconnect + defensive Guards), ohne Fachlogik zu ändern. ## Steps 1. Teardown-Funktion für globale Verfahrensgebiet-Listener ergänzen, inkl. idempotentem Disconnect und Rücksetzen des Installationsflags. - Datei: `sn_basis/functions/verfahrensgebiet_manager.py` (nach `setup_verfahrensgebiet_listener`, ca. Zeile 132) - Neue Funktion `teardown_verfahrensgebiet_listener()` disconnect alle 5 QgsProject-Signale (`layersAdded`, `layerRemoved`, `readProject`, `newProjectCreated`, `cleared`) und setzt `_listener_installed = False` zurück. - RuntimeError abfangen (Projekt wird gerade zerstört → normal). 2. Teardown in Unload-Pfad vor UI-Abbau aufrufen. (depends on step 1) - Datei: `sn_basis/main.py` (in `BasisPlugin.unload`, ca. Zeile 52) - Import und Aufruf von `teardown_verfahrensgebiet_listener()` als erste Zeile in `unload()`, bevor `self.ui.remove_all()` ausgeführt wird. 3. PrintTab um kleinen Projekt-Signal-Cleanup ergänzen, symmetrisch zu bestehendem Theme-Cleanup. (parallel mit step 1) - Datei: `sn_basis/ui/tabs/print_tab.py` - Neue Methode `cleanup()` nach `_disconnect_theme_collection_signals` (ca. Zeile 485). - Disconnectet QgsProject-Signale (`readProject`, `newProjectCreated`, `cleared`) von `self._on_project_changed` (analog zu bestehendem Theme-Disconnect-Muster). - Ruft danach `self._disconnect_theme_collection_signals()` auf. 4. BaseDockWidget.closeEvent so erweitern, dass Tab-Cleanup-Hooks vor Schließen aufgerufen werden. (depends on step 3) - Datei: `sn_basis/ui/base_dockwidget.py` (in `closeEvent`, ca. Zeile 97) - Vor dem bestehenden `self.action.setChecked(False)` Block: über alle Tabs iterieren, auf `cleanup`-Attribut prüfen, aufrufen; Exception abfangen. 5. Optional nur bei Restinstabilität: defensive Guards bei Projektzugriffen in kritischen Handlern ergänzen. - Datei: `sn_basis/functions/variable_wrapper.py` (in `get_variable`/`set_variable`, ca. Zeile 60 und 89) - Datei: `sn_basis/functions/verfahrensgebiet_manager.py` (in `_on_layer_removed`, ca. Zeile 114) - `QgsProject.instance()` auf None/RuntimeError prüfen und bei Zerstörung früh zurückkehren. ## Relevant files - `sn_basis/functions/verfahrensgebiet_manager.py` — Listener-Setup/Teardown, Handler-Guards - `sn_basis/main.py` — Unload-Reihenfolge - `sn_basis/ui/tabs/print_tab.py` — Projekt-Signal-Cleanup - `sn_basis/ui/base_dockwidget.py` — zentraler Cleanup-Hook - `sn_basis/ui/dockmanager.py` — asynchrones deleteLater als Lifecycle-Kontext - `sn_basis/functions/variable_wrapper.py` — optional defensive Guards ## Decisions - Include: minimaler Lifecycle-Fix über disconnect + Guards. - Exclude: Refactoring der gesamten Signal-Architektur, größere UI-Umbauten, versionsspezifische If-Branches. - Begründung: geringstes Risiko für Regressionen bei maximaler Wirkung gegen Shutdown-Crashs. - Keine Änderung an Business-Logik, keine Änderung an Variablennamen, keine QGIS-versionsspezifischen Sonderpfade — rein sichere Disconnect/Guard-Muster. ## Verification 1. Plugin laden, Dock öffnen/schließen, Projekt neu laden, Layer „Verfahrensgebiet" add/remove, dann QGIS 3.40.7 beenden → kein Access Violation. 2. Dasselbe unter QGIS 4 → unverändertes Verhalten, keine Regression. 3. Mehrfacher Plugin-Reload in einer Sitzung → keine doppelten Signal-Reaktionen, keine Exceptions beim Unload. 4. Optionaler Stresstest: Unit-Test für idempotentes Teardown (mehrfaches unload ohne Exception). ## Further considerations - Step 5 (defensive Guards in variable_wrapper) nur umsetzen, wenn nach Steps 1–4 noch reproduzierbare Exit-Crashs auftreten. - Ein kleiner Unit-Test für idempotentes Teardown (mehrfaches `unload()` ohne Exception) wäre empfehlenswert, falls die Testsuite erweitert wird.