From 0116c1834d6885f9c5f915f858bc9ee7c4000dcd Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Tue, 21 Jan 2025 17:00:00 +0200 Subject: [PATCH 1/3] fix unloading the plugin --- arho_feature_template/core/plan_manager.py | 38 ++++++++++++++++++---- arho_feature_template/plugin.py | 24 +++++++++++--- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index 51f0908..426da3d 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -91,8 +91,8 @@ def __init__(self): self.new_feature_dock.hide() # Initialize digitize tools - self.digitize_map_tool = PlanDigitizeMapTool(iface.mapCanvas(), iface.cadDockWidget()) - self.digitize_map_tool.digitizingCompleted.connect(self._plan_geom_digitized) + self.plan_digitize_map_tool = PlanDigitizeMapTool(iface.mapCanvas(), iface.cadDockWidget()) + self.plan_digitize_map_tool.digitizingCompleted.connect(self._plan_geom_digitized) self.feature_digitize_map_tool = None self.initialize_feature_digitize_map_tool() @@ -103,6 +103,10 @@ def __init__(self): ) self.inspect_plan_feature_tool.edit_feature_requested.connect(self.edit_plan_feature) + # Initialize lambda service + self.lambda_service = LambdaService() + self.lambda_service.jsons_received.connect(self.save_plan_jsons) + def toggle_identify_plan_features(self, activate: bool): # noqa: FBT001 if activate: self.previous_map_tool = iface.mapCanvas().mapTool() @@ -152,8 +156,8 @@ def add_new_plan(self): self.set_active_plan(None) plan_layer.startEditing() - self.digitize_map_tool.setLayer(plan_layer) - iface.mapCanvas().setMapTool(self.digitize_map_tool) + self.plan_digitize_map_tool.setLayer(plan_layer) + iface.mapCanvas().setMapTool(self.plan_digitize_map_tool) def edit_plan(self): plan_layer = PlanLayer.get_from_project() @@ -306,8 +310,6 @@ def get_plan_json(self): QMessageBox.critical(None, "Virhe", "Ei aktiivista kaavaa.") return - self.lambda_service = LambdaService() - self.lambda_service.jsons_received.connect(self.save_plan_jsons) self.lambda_service.serialize_plan(plan_id) def save_plan_jsons(self, plan_json, outline_json): @@ -342,6 +344,30 @@ def create_new_regulation_group(self): else: save_regulation_group(new_regulation_group_form.model) + def unload(self): + # Lambda service + self.lambda_service.jsons_received.disconnect(self.save_plan_jsons) + self.lambda_service.deleteLater() + + # Feature digitize tool + if self.feature_digitize_map_tool: + self.feature_digitize_map_tool.digitizingCompleted.disconnect() + self.feature_digitize_map_tool.digitizingFinished.disconnect() + self.feature_digitize_map_tool.deleteLater() + + # Plan digitize tool + self.plan_digitize_map_tool.digitizingCompleted.disconnect(self._plan_geom_digitized) + self.plan_digitize_map_tool.deleteLater() + + # Inspect plan feature tool + self.inspect_plan_feature_tool.edit_feature_requested.disconnect(self.edit_plan_feature) + self.inspect_plan_feature_tool.deleteLater() + + # New feature dock + self.new_feature_dock.tool_activated.disconnect(self.add_new_plan_feature) + iface.removeDockWidget(self.new_feature_dock) + self.new_feature_dock.deleteLater() + def regulation_group_library_from_active_plan() -> RegulationGroupLibrary: category_features = list(PlanRegulationGroupTypeLayer.get_features()) diff --git a/arho_feature_template/plugin.py b/arho_feature_template/plugin.py index 1291d88..be5790a 100644 --- a/arho_feature_template/plugin.py +++ b/arho_feature_template/plugin.py @@ -134,7 +134,7 @@ def initGui(self) -> None: # noqa N802 self.plan_manager.new_feature_dock.visibilityChanged.connect(self.dock_visibility_changed) - iface.mapCanvas().mapToolSet.connect(self.plan_manager.digitize_map_tool.deactivate) + iface.mapCanvas().mapToolSet.connect(self.plan_manager.plan_digitize_map_tool.deactivate) self.validation_dock = ValidationDock() iface.addDockWidget(Qt.RightDockWidgetArea, self.validation_dock) @@ -248,14 +248,30 @@ def open_settings(self): def unload(self) -> None: """Removes the plugin menu item and icon from QGIS GUI.""" + # Handle signals + self.plan_manager.new_feature_dock.visibilityChanged.disconnect() + iface.mapCanvas().mapToolSet.disconnect() + + # Handle actions for action in self.actions: iface.removePluginMenu(Plugin.name, action) iface.removeToolBarIcon(action) + action.deleteLater() + self.actions.clear() + + # Handle toolbar iface.mainWindow().removeToolBar(self.toolbar) - teardown_logger(Plugin.name) + self.toolbar = None - self.plan_manager.new_feature_dock.close() - self.validation_dock.close() + # Handle plan manager + self.plan_manager.unload() + + # Handle validation dock + iface.removeDockWidget(self.validation_dock) + self.validation_dock.deleteLater() + + # Handle logger + teardown_logger(Plugin.name) def dock_visibility_changed(self, visible: bool) -> None: # noqa: FBT001 self.new_feature_dock_action.setChecked(visible) From 640a822e5365a05146de2b3e6b92378b6ff11a0f Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Tue, 21 Jan 2025 17:23:25 +0200 Subject: [PATCH 2/3] fix removing spatial index building from InspectPlanFeatures tool --- .../gui/tools/inspect_plan_features_tool.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/arho_feature_template/gui/tools/inspect_plan_features_tool.py b/arho_feature_template/gui/tools/inspect_plan_features_tool.py index b9e8b2c..32627ee 100644 --- a/arho_feature_template/gui/tools/inspect_plan_features_tool.py +++ b/arho_feature_template/gui/tools/inspect_plan_features_tool.py @@ -3,7 +3,7 @@ from functools import partial from typing import TYPE_CHECKING -from qgis.core import QgsFeature, QgsFeatureRequest, QgsPointXY, QgsRectangle, QgsSpatialIndex, QgsVectorLayer +from qgis.core import QgsFeature, QgsFeatureRequest, QgsPointXY, QgsRectangle, QgsVectorLayer from qgis.gui import QgsMapCanvas, QgsMapMouseEvent, QgsMapTool, QgsRubberBand from qgis.PyQt.QtCore import QPoint, Qt, QTimer, pyqtSignal from qgis.PyQt.QtGui import QColor @@ -24,18 +24,12 @@ def __init__(self, canvas: QgsMapCanvas, layer_classes: list[PlanFeatureLayer]): self.layer_classes = layer_classes self.layers = [cls.get_from_project() for cls in layer_classes] - self.spatial_indexes: dict[str, QgsSpatialIndex] = {} self.highlighted: tuple[QgsFeature, str] | None = None self.highlight_rubber_band: QgsRubberBand | None = None def activate(self): super().activate() - self.rebuild_spatial_indexes() - - def rebuild_spatial_indexes(self): - for layer in self.layers: - self.spatial_indexes[layer.id()] = QgsSpatialIndex(layer.getFeatures()) def create_highlight_rubberband(self, feature: QgsFeature, layer: QgsVectorLayer): self.highlight_rubber_band = QgsRubberBand(self.canvas, layer.geometryType()) From 009efe3f36138007eaf3564eff47f9758ff72065 Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Tue, 21 Jan 2025 17:29:02 +0200 Subject: [PATCH 3/3] fix reading project layers too early The fix is not very scalable, might need to address this more thoroughly in the future --- arho_feature_template/core/plan_manager.py | 20 ++++++++++++------- .../gui/tools/inspect_plan_features_tool.py | 7 ++++++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index 426da3d..60b102d 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -81,9 +81,7 @@ def __init__(self): self.feature_template_libraries = [ FeatureTemplateLibrary.from_config_file(file) for file in feature_template_library_config_files() ] - self.regulation_group_libraries = [ - RegulationGroupLibrary.from_config_file(file) for file in regulation_group_library_config_files() - ] + self.regulation_group_libraries = None # Initialize new feature dock self.new_feature_dock = NewFeatureDock(self.feature_template_libraries) @@ -107,6 +105,14 @@ def __init__(self): self.lambda_service = LambdaService() self.lambda_service.jsons_received.connect(self.save_plan_jsons) + def get_regulation_group_libraries(self): + # Lazy initialization of regulation_group_libraries to avoid reading regulation code layer too early + if self.regulation_group_libraries is None: + self.regulation_group_libraries = [ + RegulationGroupLibrary.from_config_file(file) for file in regulation_group_library_config_files() + ] + return self.regulation_group_libraries + def toggle_identify_plan_features(self, activate: bool): # noqa: FBT001 if activate: self.previous_map_tool = iface.mapCanvas().mapTool() @@ -171,7 +177,7 @@ def edit_plan(self): return plan_model = PlanLayer.model_from_feature(feature) - attribute_form = PlanAttributeForm(plan_model, self.regulation_group_libraries) + attribute_form = PlanAttributeForm(plan_model, self.get_regulation_group_libraries()) if attribute_form.exec_(): feature = save_plan(attribute_form.model) @@ -202,7 +208,7 @@ def _plan_geom_digitized(self, feature: QgsFeature): return plan_model = Plan(geom=feature.geometry()) - attribute_form = PlanAttributeForm(plan_model, self.regulation_group_libraries) + attribute_form = PlanAttributeForm(plan_model, self.get_regulation_group_libraries()) if attribute_form.exec_(): feature = save_plan(attribute_form.model) plan_to_be_activated = feature["id"] @@ -229,7 +235,7 @@ def _plan_feature_geom_digitized(self, feature: QgsFeature): plan_feature.geom = feature.geometry() attribute_form = PlanFeatureForm( - plan_feature, title, [*self.regulation_group_libraries, regulation_group_library_from_active_plan()] + plan_feature, title, [*self.get_regulation_group_libraries(), regulation_group_library_from_active_plan()] ) if attribute_form.exec_(): save_plan_feature(attribute_form.model) @@ -241,7 +247,7 @@ def edit_plan_feature(self, feature: QgsFeature, layer_name: str): # Geom editing handled with basic QGIS vertex editing? title = plan_feature.name if plan_feature.name else layer_name attribute_form = PlanFeatureForm( - plan_feature, title, [*self.regulation_group_libraries, regulation_group_library_from_active_plan()] + plan_feature, title, [*self.get_regulation_group_libraries(), regulation_group_library_from_active_plan()] ) if attribute_form.exec_(): save_plan_feature(attribute_form.model) diff --git a/arho_feature_template/gui/tools/inspect_plan_features_tool.py b/arho_feature_template/gui/tools/inspect_plan_features_tool.py index 32627ee..5569709 100644 --- a/arho_feature_template/gui/tools/inspect_plan_features_tool.py +++ b/arho_feature_template/gui/tools/inspect_plan_features_tool.py @@ -23,7 +23,7 @@ def __init__(self, canvas: QgsMapCanvas, layer_classes: list[PlanFeatureLayer]): self.canvas = canvas self.layer_classes = layer_classes - self.layers = [cls.get_from_project() for cls in layer_classes] + self.layers: list[QgsVectorLayer] | None = None self.highlighted: tuple[QgsFeature, str] | None = None self.highlight_rubber_band: QgsRubberBand | None = None @@ -70,6 +70,11 @@ def query_nearby_features(self, point: QgsPointXY) -> dict[QgsVectorLayer, list[ request = QgsFeatureRequest() request.setFilterRect(tolerance_geom) request.setFlags(QgsFeatureRequest.Flag.ExactIntersect) + + # Lazy reading of feature layers to avoid reading them too early + if self.layers is None: + self.layers = [cls.get_from_project() for cls in self.layer_classes] + with OverrideCursor(Qt.WaitCursor): for layer in self.layers: features = list(layer.getFeatures(request))