From 4330ae4a969981bc7b741bf1e1f9ba8359f80a43 Mon Sep 17 00:00:00 2001 From: Miikka Kallio Date: Thu, 7 Nov 2024 12:44:50 +0200 Subject: [PATCH] Refactored new_plan, and load_existing_land_use_plan Added handling of unsaved changes to creating new plan. --- arho_feature_template/core/new_plan.py | 63 ------------ arho_feature_template/core/plan_manager.py | 107 +++++++++++++++++++++ arho_feature_template/plugin.py | 44 ++------- 3 files changed, 113 insertions(+), 101 deletions(-) delete mode 100644 arho_feature_template/core/new_plan.py create mode 100644 arho_feature_template/core/plan_manager.py diff --git a/arho_feature_template/core/new_plan.py b/arho_feature_template/core/new_plan.py deleted file mode 100644 index 97dd5ee..0000000 --- a/arho_feature_template/core/new_plan.py +++ /dev/null @@ -1,63 +0,0 @@ -from qgis.core import QgsProject, QgsVectorLayer -from qgis.utils import iface - -from arho_feature_template.core.update_plan import LandUsePlan, update_selected_plan - - -class NewPlan: - def add_new_plan(self): - # Filtered layers are not editable, so clear filters first. - self.clear_all_filters() - - layers = QgsProject.instance().mapLayersByName("Kaava") - if not layers: - iface.messageBar().pushMessage("Error", "Layer 'Kaava' not found", level=3) - return - - kaava_layer = layers[0] - - if not kaava_layer.isEditable(): - kaava_layer.startEditing() - - iface.setActiveLayer(kaava_layer) - - iface.actionAddFeature().trigger() - - # Connect the featureAdded signal - kaava_layer.featureAdded.connect(self.feature_added) - - def feature_added(self): - kaava_layer = iface.activeLayer() - kaava_layer.featureAdded.disconnect() - feature_ids_before_commit = kaava_layer.allFeatureIds() - if kaava_layer.isEditable(): - if not kaava_layer.commitChanges(): - iface.messageBar().pushMessage("Error", "Failed to commit changes to the layer.", level=3) - return - else: - iface.messageBar().pushMessage("Error", "Layer is not editable.", level=3) - return - - feature_ids_after_commit = kaava_layer.allFeatureIds() - - # Find the new plan.id by comparing fids before and after commit. - new_feature_id = next((fid for fid in feature_ids_after_commit if fid not in feature_ids_before_commit), None) - - if new_feature_id is not None: - new_feature = kaava_layer.getFeature(new_feature_id) - - if new_feature.isValid(): - feature_id_value = new_feature["id"] - update_selected_plan(LandUsePlan(feature_id_value)) - else: - iface.messageBar().pushMessage("Error", "Invalid feature retrieved.", level=3) - else: - iface.messageBar().pushMessage("Error", "No new feature was added.", level=3) - - def clear_all_filters(self): - """Clear filters for all vector layers in the project.""" - layers = QgsProject.instance().mapLayers().values() - - for layer in layers: - if isinstance(layer, QgsVectorLayer): - layer.setSubsetString("") diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py new file mode 100644 index 0000000..a4f911b --- /dev/null +++ b/arho_feature_template/core/plan_manager.py @@ -0,0 +1,107 @@ +from qgis.core import QgsProject, QgsVectorLayer +from qgis.PyQt.QtWidgets import QDialog, QMessageBox +from qgis.utils import iface + +from arho_feature_template.core.update_plan import LandUsePlan, update_selected_plan +from arho_feature_template.gui.load_plan_dialog import LoadPlanDialog +from arho_feature_template.utils.db_utils import get_existing_database_connection_names +from arho_feature_template.utils.misc_utils import handle_unsaved_changes + + +class PlanManager: + def __init__(self): + self.kaava_layer = self.get_layer_by_name("Kaava") + + def get_layer_by_name(self, layer_name): + """Retrieve a layer by name from the project.""" + layers = QgsProject.instance().mapLayersByName(layer_name) + if layers: + return layers[0] + iface.messageBar().pushMessage("Error", f"Layer '{layer_name}' not found", level=3) + return None + + def add_new_plan(self): + """Initiate the process to add a new plan to the Kaava layer.""" + if not handle_unsaved_changes(): + return + + self.clear_all_filters() + + if not self.kaava_layer: + return + + if not self.kaava_layer.isEditable(): + self.kaava_layer.startEditing() + + iface.setActiveLayer(self.kaava_layer) + iface.actionAddFeature().trigger() + + # Connect the featureAdded signal to a callback method + self.kaava_layer.featureAdded.connect(self.feature_added) + + def feature_added(self): + """Callback for when a new feature is added to the Kaava layer.""" + if not self.kaava_layer: + return + + # Disconnect the signal to avoid repeated triggers + self.kaava_layer.featureAdded.disconnect() + + feature_ids_before_commit = self.kaava_layer.allFeatureIds() + + if self.kaava_layer.isEditable(): + if not self.kaava_layer.commitChanges(): + iface.messageBar().pushMessage("Error", "Failed to commit changes to the layer.", level=3) + return + else: + iface.messageBar().pushMessage("Error", "Layer is not editable.", level=3) + return + + feature_ids_after_commit = self.kaava_layer.allFeatureIds() + new_feature_id = next((fid for fid in feature_ids_after_commit if fid not in feature_ids_before_commit), None) + + if new_feature_id is not None: + new_feature = self.kaava_layer.getFeature(new_feature_id) + if new_feature.isValid(): + feature_id_value = new_feature["id"] + update_selected_plan(LandUsePlan(feature_id_value)) + else: + iface.messageBar().pushMessage("Error", "Invalid feature retrieved.", level=3) + else: + iface.messageBar().pushMessage("Error", "No new feature was added.", level=3) + + def load_land_use_plan(self): + """Load an existing land use plan using a dialog selection.""" + connections = get_existing_database_connection_names() + + if not connections: + QMessageBox.critical(None, "Error", "No database connections found.") + return + + if not handle_unsaved_changes(): + return + + dialog = LoadPlanDialog(None, connections) + + if dialog.exec_() == QDialog.Accepted: + selected_plan_id = dialog.get_selected_plan_id() + self.commit_all_editable_layers() + + if not selected_plan_id: + QMessageBox.critical(None, "Error", "No plan was selected.") + return + + plan = LandUsePlan(selected_plan_id) + update_selected_plan(plan) + + def clear_all_filters(self): + """Clear filters for all vector layers in the project.""" + for layer in QgsProject.instance().mapLayers().values(): + if isinstance(layer, QgsVectorLayer): + layer.setSubsetString("") + + def commit_all_editable_layers(self): + """Commit all changes in any editable layers.""" + for layer in QgsProject.instance().mapLayers().values(): + if isinstance(layer, QgsVectorLayer) and layer.isEditable(): + layer.commitChanges() diff --git a/arho_feature_template/plugin.py b/arho_feature_template/plugin.py index 551d4b9..7079fd9 100644 --- a/arho_feature_template/plugin.py +++ b/arho_feature_template/plugin.py @@ -2,21 +2,16 @@ from typing import TYPE_CHECKING, Callable, cast -from qgis.core import QgsProject, QgsVectorLayer from qgis.PyQt.QtCore import QCoreApplication, Qt, QTranslator from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtWidgets import QAction, QDialog, QMessageBox, QWidget +from qgis.PyQt.QtWidgets import QAction, QWidget from qgis.utils import iface from arho_feature_template.core.feature_template_library import FeatureTemplater, TemplateGeometryDigitizeMapTool -from arho_feature_template.core.new_plan import NewPlan -from arho_feature_template.core.update_plan import LandUsePlan, update_selected_plan -from arho_feature_template.gui.load_plan_dialog import LoadPlanDialog +from arho_feature_template.core.plan_manager import PlanManager from arho_feature_template.qgis_plugin_tools.tools.custom_logging import setup_logger, teardown_logger from arho_feature_template.qgis_plugin_tools.tools.i18n import setup_translation from arho_feature_template.qgis_plugin_tools.tools.resources import plugin_name -from arho_feature_template.utils.db_utils import get_existing_database_connection_names -from arho_feature_template.utils.misc_utils import handle_unsaved_changes if TYPE_CHECKING: from qgis.gui import QgisInterface, QgsMapTool @@ -32,6 +27,7 @@ class Plugin: def __init__(self) -> None: setup_logger(Plugin.name) self.digitizing_tool = None + self.plan_manager = PlanManager() # initialize locale locale, file_path = setup_translation() @@ -127,7 +123,6 @@ def add_action( def initGui(self) -> None: # noqa N802 self.templater = FeatureTemplater() - self.new_plan = NewPlan() # plan_icon_path = os.path.join(PLUGIN_PATH, "resources/icons/city.png") # A placeholder icon # load_icon_path = os.path.join(PLUGIN_PATH, "resources/icons/folder.png") # A placeholder icon @@ -172,37 +167,10 @@ def on_map_tool_changed(self, new_tool: QgsMapTool, old_tool: QgsMapTool) -> Non self.template_dock_action.setChecked(False) def add_new_plan(self): - self.new_plan.add_new_plan() + self.plan_manager.add_new_plan() - def load_existing_land_use_plan(self) -> None: - """Open existing land use plan.""" - - connections = get_existing_database_connection_names() - - if not connections: - QMessageBox.critical(None, "Error", "No database connections found.") - return - - if not handle_unsaved_changes(): - return - - dialog = LoadPlanDialog(None, connections) - - if dialog.exec_() == QDialog.Accepted: - selected_plan_id = dialog.get_selected_plan_id() - - project = QgsProject.instance() - for layer in project.mapLayers().values(): - if isinstance(layer, QgsVectorLayer) and layer.isEditable(): - layer.commitChanges() - - if not selected_plan_id: - QMessageBox.critical(None, "Error", "No plan was selected.") - return - - plan = LandUsePlan(selected_plan_id) - - update_selected_plan(plan) + def load_existing_land_use_plan(self): + self.plan_manager.load_land_use_plan() def unload(self) -> None: """Removes the plugin menu item and icon from QGIS GUI."""