3.40exit-crash-fix als plan in assets

This commit is contained in:
2026-04-17 14:16:50 +02:00
parent 9306028df8
commit 5564c06d5c
+57
View File
@@ -0,0 +1,57 @@
# 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 14 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.