4.0 KiB
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
-
Teardown-Funktion für globale Verfahrensgebiet-Listener ergänzen, inkl. idempotentem Disconnect und Rücksetzen des Installationsflags.
- Datei:
sn_basis/functions/verfahrensgebiet_manager.py(nachsetup_verfahrensgebiet_listener, ca. Zeile 132) - Neue Funktion
teardown_verfahrensgebiet_listener()disconnect alle 5 QgsProject-Signale (layersAdded,layerRemoved,readProject,newProjectCreated,cleared) und setzt_listener_installed = Falsezurück. - RuntimeError abfangen (Projekt wird gerade zerstört → normal).
- Datei:
-
Teardown in Unload-Pfad vor UI-Abbau aufrufen. (depends on step 1)
- Datei:
sn_basis/main.py(inBasisPlugin.unload, ca. Zeile 52) - Import und Aufruf von
teardown_verfahrensgebiet_listener()als erste Zeile inunload(), bevorself.ui.remove_all()ausgeführt wird.
- Datei:
-
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) vonself._on_project_changed(analog zu bestehendem Theme-Disconnect-Muster). - Ruft danach
self._disconnect_theme_collection_signals()auf.
- Datei:
-
BaseDockWidget.closeEvent so erweitern, dass Tab-Cleanup-Hooks vor Schließen aufgerufen werden. (depends on step 3)
- Datei:
sn_basis/ui/base_dockwidget.py(incloseEvent, ca. Zeile 97) - Vor dem bestehenden
self.action.setChecked(False)Block: über alle Tabs iterieren, aufcleanup-Attribut prüfen, aufrufen; Exception abfangen.
- Datei:
-
Optional nur bei Restinstabilität: defensive Guards bei Projektzugriffen in kritischen Handlern ergänzen.
- Datei:
sn_basis/functions/variable_wrapper.py(inget_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.
- Datei:
Relevant files
sn_basis/functions/verfahrensgebiet_manager.py— Listener-Setup/Teardown, Handler-Guardssn_basis/main.py— Unload-Reihenfolgesn_basis/ui/tabs/print_tab.py— Projekt-Signal-Cleanupsn_basis/ui/base_dockwidget.py— zentraler Cleanup-Hooksn_basis/ui/dockmanager.py— asynchrones deleteLater als Lifecycle-Kontextsn_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
- Plugin laden, Dock öffnen/schließen, Projekt neu laden, Layer „Verfahrensgebiet" add/remove, dann QGIS 3.40.7 beenden → kein Access Violation.
- Dasselbe unter QGIS 4 → unverändertes Verhalten, keine Regression.
- Mehrfacher Plugin-Reload in einer Sitzung → keine doppelten Signal-Reaktionen, keine Exceptions beim Unload.
- 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.