Merge pull request 'Navigation.remove_all() räumt Toolbar/Menü beim Unload tatsächlich auf (#72)' (#73) from bugfix/72-navigation-cleanup-deletelater into unstable
Reviewed-on: #73
This commit was merged in pull request #73.
This commit is contained in:
@@ -1,4 +1,8 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
|
- Bugfix: `Navigation.remove_all()` ruft jetzt `deleteLater()` auf Menü und Toolbar auf, bevor die Referenzen verworfen werden – vorher blieb die Toolbar beim Unload als Kind von main_window bestehen und der Plugin Reloader meldete beim Neuladen "removing duplicated widget(s) not cleaned up by the plugin during unload: LnoSachsenToolbar" (#72)
|
||||||
|
=======
|
||||||
- Restliche „ungeklärt"-Funktionen aus dem Prüfstand entschieden: Tests für Dateipruefer (`_ist_leer`, Priorität in `_handle_leere_eingabe`), Datenschreiber (`_store_style_in_gpkg` – Tabelle anlegen, Default-Stil zurücksetzen, Geometriespalte übernehmen) und Pruefmanager (`_handle_verfahrensgebiet_layer_existiert` „entfernen"/„ersetzen"); Linkpruefer (`_ist_url`, `_pruefe_url`, `_pruefe_dateipfad`), `Pruefmanager._handle_datei_existiert` und `Pruefmanager._ask_use_or_replace_pufferlayer` (toter Code, Bug #60) bekamen den Vermerk „von Test abgesehen" – sn_basis steht jetzt bei 0 ungeklärten Funktionen (#70)
|
- Restliche „ungeklärt"-Funktionen aus dem Prüfstand entschieden: Tests für Dateipruefer (`_ist_leer`, Priorität in `_handle_leere_eingabe`), Datenschreiber (`_store_style_in_gpkg` – Tabelle anlegen, Default-Stil zurücksetzen, Geometriespalte übernehmen) und Pruefmanager (`_handle_verfahrensgebiet_layer_existiert` „entfernen"/„ersetzen"); Linkpruefer (`_ist_url`, `_pruefe_url`, `_pruefe_dateipfad`), `Pruefmanager._handle_datei_existiert` und `Pruefmanager._ask_use_or_replace_pufferlayer` (toter Code, Bug #60) bekamen den Vermerk „von Test abgesehen" – sn_basis steht jetzt bei 0 ungeklärten Funktionen (#70)
|
||||||
|
>>>>>>> unstable
|
||||||
- Kategorie „vielleicht testen" abgeschlossen: 30 weitere Funktionen bekamen Tests (os_wrapper, qgiscore_wrapper, qgisui_wrapper, DataGrabber, pruef_ergebnis, Navigation, SettingsTab, PrintTab – Zustandslogik, Signal-Handling, Atlas-/Theme-Verwaltung), `PrintTab._build_ui` bekam den Vermerk „Test ausgesetzt, zunächst Refactor" (#67)
|
- Kategorie „vielleicht testen" abgeschlossen: 30 weitere Funktionen bekamen Tests (os_wrapper, qgiscore_wrapper, qgisui_wrapper, DataGrabber, pruef_ergebnis, Navigation, SettingsTab, PrintTab – Zustandslogik, Signal-Handling, Atlas-/Theme-Verwaltung), `PrintTab._build_ui` bekam den Vermerk „Test ausgesetzt, zunächst Refactor" (#67)
|
||||||
- Testkatalog-Klassifizierung in Docstrings festgehalten: 43 als „vernachlässigbar" eingestufte Funktionen tragen jetzt den Vermerk „von Test abgesehen, weil vernachlässigbar", 41 Print-Bereich-Funktionen (`print_logic.py`, `print_layout.py`, `print_layout_baufreigabe.py`) den Vermerk „Test ausgesetzt, zunächst Refactor" – keine Verhaltensänderung
|
- Testkatalog-Klassifizierung in Docstrings festgehalten: 43 als „vernachlässigbar" eingestufte Funktionen tragen jetzt den Vermerk „von Test abgesehen, weil vernachlässigbar", 41 Print-Bereich-Funktionen (`print_logic.py`, `print_layout.py`, `print_layout_baufreigabe.py`) den Vermerk „Test ausgesetzt, zunächst Refactor" – keine Verhaltensänderung
|
||||||
- Tests für weitere zentrale Funktionen ergänzt: Verfahrensgebiet-Zustandsverwaltung, Dock-Verwaltung, Pruefmanager-/Dateipruefer-Entscheidungswege, Fundament-Wrapper (Pfade, Landkreise, Nachrichten, Layer-Existenz/-Stil, GPKG-Layer-Prüfung, QGIS-3/4-Geometriekompatibilität) und Projekteigenschaften-Tab (#65)
|
- Tests für weitere zentrale Funktionen ergänzt: Verfahrensgebiet-Zustandsverwaltung, Dock-Verwaltung, Pruefmanager-/Dateipruefer-Entscheidungswege, Fundament-Wrapper (Pfade, Landkreise, Nachrichten, Layer-Existenz/-Stil, GPKG-Layer-Prüfung, QGIS-3/4-Geometriekompatibilität) und Projekteigenschaften-Tab (#65)
|
||||||
|
|||||||
@@ -495,6 +495,7 @@ except (ImportError, AttributeError):
|
|||||||
if action in self._actions: self._actions.remove(action)
|
if action in self._actions: self._actions.remove(action)
|
||||||
def clear(self): self._actions.clear()
|
def clear(self): self._actions.clear()
|
||||||
def menuAction(self): return self
|
def menuAction(self): return self
|
||||||
|
def deleteLater(self) -> None: pass
|
||||||
|
|
||||||
class _MockToolBar:
|
class _MockToolBar:
|
||||||
def __init__(self, *args, **kwargs): self._actions = []
|
def __init__(self, *args, **kwargs): self._actions = []
|
||||||
@@ -503,6 +504,7 @@ except (ImportError, AttributeError):
|
|||||||
def removeAction(self, action):
|
def removeAction(self, action):
|
||||||
if action in self._actions: self._actions.remove(action)
|
if action in self._actions: self._actions.remove(action)
|
||||||
def clear(self): self._actions.clear()
|
def clear(self): self._actions.clear()
|
||||||
|
def deleteLater(self) -> None: pass
|
||||||
|
|
||||||
class _MockActionGroup:
|
class _MockActionGroup:
|
||||||
def __init__(self, *args, **kwargs): self._actions = []
|
def __init__(self, *args, **kwargs): self._actions = []
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class RemoveAllTests(unittest.TestCase):
|
|||||||
def test_entfernt_menu_toolbar_und_setzt_zustand_zurueck(self, mock_remove_menu, mock_remove_toolbar):
|
def test_entfernt_menu_toolbar_und_setzt_zustand_zurueck(self, mock_remove_menu, mock_remove_toolbar):
|
||||||
"""Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt."""
|
"""Prüft, dass remove_all Menü und Toolbar über die Wrapper entfernt und den Navigationszustand zurücksetzt."""
|
||||||
nav = Navigation()
|
nav = Navigation()
|
||||||
fake_menu, fake_toolbar = object(), object()
|
fake_menu, fake_toolbar = Mock(), Mock()
|
||||||
nav.menu = fake_menu
|
nav.menu = fake_menu
|
||||||
nav.toolbar = fake_toolbar
|
nav.toolbar = fake_toolbar
|
||||||
nav.actions = [(1, "a1")]
|
nav.actions = [(1, "a1")]
|
||||||
@@ -178,6 +178,20 @@ class RemoveAllTests(unittest.TestCase):
|
|||||||
self.assertEqual(nav.actions, [])
|
self.assertEqual(nav.actions, [])
|
||||||
self.assertIsNone(nav.plugin_group)
|
self.assertIsNone(nav.plugin_group)
|
||||||
|
|
||||||
|
@patch("sn_basis.ui.navigation.remove_toolbar")
|
||||||
|
@patch("sn_basis.ui.navigation.remove_menu")
|
||||||
|
def test_loescht_menu_und_toolbar_objekt_damit_kein_duplikat_uebrig_bleibt(self, mock_remove_menu, mock_remove_toolbar):
|
||||||
|
"""Prüft, dass remove_all deleteLater() auf Menü und Toolbar aufruft, damit sie den Qt-Objektbaum tatsächlich verlassen (Issue #72)."""
|
||||||
|
nav = Navigation()
|
||||||
|
fake_menu, fake_toolbar = Mock(), Mock()
|
||||||
|
nav.menu = fake_menu
|
||||||
|
nav.toolbar = fake_toolbar
|
||||||
|
|
||||||
|
nav.remove_all()
|
||||||
|
|
||||||
|
fake_menu.deleteLater.assert_called_once()
|
||||||
|
fake_toolbar.deleteLater.assert_called_once()
|
||||||
|
|
||||||
@patch("sn_basis.ui.navigation.remove_toolbar")
|
@patch("sn_basis.ui.navigation.remove_toolbar")
|
||||||
@patch("sn_basis.ui.navigation.remove_menu")
|
@patch("sn_basis.ui.navigation.remove_menu")
|
||||||
def test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf(self, mock_remove_menu, mock_remove_toolbar):
|
def test_ohne_menu_und_toolbar_ruft_wrapper_nicht_auf(self, mock_remove_menu, mock_remove_toolbar):
|
||||||
|
|||||||
@@ -114,12 +114,18 @@ class Navigation:
|
|||||||
self.actions = [(p, a) for p, a in self.actions if a != action]
|
self.actions = [(p, a) for p, a in self.actions if a != action]
|
||||||
|
|
||||||
def remove_all(self):
|
def remove_all(self):
|
||||||
|
# remove_menu()/remove_toolbar() nehmen Menü/Toolbar nur aus der sichtbaren Leiste –
|
||||||
|
# das Qt-Objekt bleibt sonst als Kind von main_window bestehen (Issue #72, vom Plugin
|
||||||
|
# Reloader als "duplicated widget(s) not cleaned up" gemeldet). deleteLater() räumt es
|
||||||
|
# tatsächlich auf, analog zum bestehenden Workaround in init_ui().
|
||||||
if self.menu:
|
if self.menu:
|
||||||
remove_menu(self.menu)
|
remove_menu(self.menu)
|
||||||
|
self.menu.deleteLater()
|
||||||
self.menu = None
|
self.menu = None
|
||||||
|
|
||||||
if self.toolbar:
|
if self.toolbar:
|
||||||
remove_toolbar(self.toolbar)
|
remove_toolbar(self.toolbar)
|
||||||
|
self.toolbar.deleteLater()
|
||||||
self.toolbar = None
|
self.toolbar = None
|
||||||
|
|
||||||
self.actions.clear()
|
self.actions.clear()
|
||||||
|
|||||||
Reference in New Issue
Block a user