diff --git a/plugin/qgistim/widgets/dataset_widget.py b/plugin/qgistim/widgets/dataset_widget.py index 6852a2c..1c0c5f1 100644 --- a/plugin/qgistim/widgets/dataset_widget.py +++ b/plugin/qgistim/widgets/dataset_widget.py @@ -21,6 +21,7 @@ QCheckBox, QComboBox, QFileDialog, + QGroupBox, QHBoxLayout, QHeaderView, QLineEdit, @@ -272,14 +273,16 @@ def __init__(self, parent): self.new_geopackage_button = QPushButton("New") self.open_geopackage_button = QPushButton("Open") self.copy_geopackage_button = QPushButton("Copy") + self.restore_geopackage_button = QPushButton("Restore") self.transient_combo_box = QComboBox() self.transient_combo_box.addItems(["Steady-state", "Transient"]) self.transient_combo_box.currentTextChanged.connect(self.on_transient_changed) - self.remove_button = QPushButton("Remove from GeoPackage") + self.remove_button = QPushButton("Remove from Model") self.add_button = QPushButton("Add to QGIS") self.new_geopackage_button.clicked.connect(self.new_geopackage) self.open_geopackage_button.clicked.connect(self.open_geopackage) self.copy_geopackage_button.clicked.connect(self.copy_geopackage) + self.restore_geopackage_button.clicked.connect(self.restore_geopackage) self.suppress_popup_checkbox = QCheckBox("Suppress attribute form pop-up") self.suppress_popup_checkbox.stateChanged.connect(self.suppress_popup_changed) self.remove_button.clicked.connect(self.remove_geopackage_layer) @@ -288,18 +291,29 @@ def __init__(self, parent): self.convert_button.clicked.connect(self.convert_to_python) # Layout dataset_layout = QVBoxLayout() + + # Add geopackage management + geopackage_group = QGroupBox("GeoPackage") + geopackage_layout = QVBoxLayout() + geopackage_group.setLayout(geopackage_layout) + geopackage_layout.addWidget(self.dataset_line_edit) + geopackage_row = QHBoxLayout() + geopackage_row.addWidget(self.open_geopackage_button) + geopackage_row.addWidget(self.new_geopackage_button) + geopackage_row.addWidget(self.copy_geopackage_button) + geopackage_row.addWidget(self.restore_geopackage_button) + geopackage_layout.addLayout(geopackage_row) + dataset_layout.addWidget(geopackage_group) + + # Transient versus steady-state selector mode_row = QHBoxLayout() - dataset_row = QHBoxLayout() - layer_row = QHBoxLayout() - dataset_row.addWidget(self.dataset_line_edit) - dataset_row.addWidget(self.open_geopackage_button) - dataset_row.addWidget(self.new_geopackage_button) - dataset_row.addWidget(self.copy_geopackage_button) - dataset_layout.addLayout(dataset_row) mode_row.addWidget(self.transient_combo_box) dataset_layout.addLayout(mode_row) + # Dataset table and suppression checkbox dataset_layout.addWidget(self.dataset_tree) + # Assorted widgets dataset_layout.addWidget(self.suppress_popup_checkbox) + layer_row = QHBoxLayout() layer_row.addWidget(self.add_button) layer_row.addWidget(self.remove_button) dataset_layout.addLayout(layer_row) @@ -348,15 +362,17 @@ def add_selection_to_qgis(self) -> None: self.add_item_to_qgis(item) return - def load_geopackage(self) -> None: + def load_geopackage( + self, input_group: str = None, output_group: str = None + ) -> None: """ Load the layers of a GeoPackage into the Layers Panel """ self.dataset_tree.clear() name = str(Path(self.path).stem) - self.parent.create_input_group(name) - self.parent.create_output_group(name) + self.parent.create_input_group(name, input_group) + self.parent.create_output_group(name, output_group) elements = load_elements_from_geopackage(self.path) for element in elements: @@ -372,6 +388,7 @@ def load_geopackage(self) -> None: self.on_transient_changed() self.model_crs = self.domain_item().element.timml_layer.crs() + self.parent.qgs_project.writeEntry("qgistim", "geopackage_path", self.path) return def new_geopackage(self) -> None: @@ -420,6 +437,9 @@ def copy_geopackage(self) -> None: """ Copy a GeoPackage file, containing qgis-tim, and open it. """ + # Do nothing if there's nothing to copy. + if self.path == "": + return self.dataset_tree.clear() target_path, _ = QFileDialog.getSaveFileName(self, "Select file", "", "*.gpkg") if target_path != "": # Empty string in case of cancel button press @@ -437,6 +457,30 @@ def copy_geopackage(self) -> None: self.load_geopackage() return + def restore_geopackage(self) -> None: + qgs_project = self.parent.qgs_project + geopackage_path, success = qgs_project.readEntry("qgistim", "geopackage_path") + if not success: + self.parent.message_bar.pushMessage( + title="Error", + text="Could not find a QGIS-Tim GeoPackage in this QGS Project.", + level=Qgis.Critical, + ) + return + + if not Path(geopackage_path).exists(): + self.parent.message_bar.pushMessage( + title="Error", + text=f"QGIS-Tim Geopackage {geopackage_path} does not exist.", + level=Qgis.Critical, + ) + + input_group_name, _ = qgs_project.readEntry("qgistim", "input_group") + output_group_name, _ = qgs_project.readEntry("qgistim", "output_group") + self.dataset_line_edit.setText(geopackage_path) + self.load_geopackage(input_group_name, output_group_name) + return + def remove_geopackage_layer(self) -> None: """ Remove layers from: diff --git a/plugin/qgistim/widgets/tim_widget.py b/plugin/qgistim/widgets/tim_widget.py index 66cb002..84f411e 100644 --- a/plugin/qgistim/widgets/tim_widget.py +++ b/plugin/qgistim/widgets/tim_widget.py @@ -1,11 +1,11 @@ """ This module forms the high level DockWidget. -It ensures the underlying widgets can talk to each other. It also manages the +It ensures the underlying widgets can talk to each other. It also manages the connection to the QGIS Layers Panel, and ensures there is a group for the Tim layers there. """ -from typing import Any, Dict, Union +from typing import Any, Dict, Optional, Union from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( @@ -15,7 +15,13 @@ QVBoxLayout, QWidget, ) -from qgis.core import QgsApplication, QgsEditFormConfig, QgsMapLayer, QgsProject +from qgis.core import ( + QgsApplication, + QgsEditFormConfig, + QgsLayerTreeGroup, + QgsMapLayer, + QgsProject, +) from qgistim.core.server_handler import ServerHandler from qgistim.core.task import BaseServerTask from qgistim.widgets.compute_widget import ComputeWidget @@ -32,13 +38,19 @@ def __init__( self, root, name: str, + group_name: Optional[QgsLayerTreeGroup] = None, ): self.name = name self.root = root self.subgroups = {} - self.create_group() - - def create_group(self): + self.create_group(group_name) + + def _create_group(self, group_name: str): + if group_name is not None: + self.group = self.root.findGroup(group_name) + if self.group is not None: + self.group.removeAllChildren() + return self.group = self.root.addGroup(self.name) return @@ -147,6 +159,18 @@ def add_layer( return maplayer +class InputGroup(LayersPanelGroup): + def create_group(self, group=None): + QgsProject.instance().writeEntry("qgistim", "input_group", self.name) + self._create_group(group) + + +class OutputGroup(LayersPanelGroup): + def create_group(self, group=None): + QgsProject.instance().writeEntry("qgistim", "output_group", self.name) + self._create_group(group) + + class StartTask(BaseServerTask): @property def task_description(self): @@ -208,6 +232,10 @@ def __init__(self, parent, iface): # QGIS Layers Panel groups self.input_group = None self.output_group = None + + # Connect to the project saved signal. + # Note that the project is a singleton instance, so it's always up to date. + self.qgs_project = QgsProject.instance() return # Inter-widget communication @@ -293,16 +321,16 @@ def add_element(self, element: Any): # QGIS layers # ----------- - def create_input_group(self, name: str) -> None: - root = QgsProject.instance().layerTreeRoot() - self.input_group = LayersPanelGroup(root, f"{name} input") + def create_input_group(self, name: str, group: str = None) -> None: + root = self.qgs_project.layerTreeRoot() + self.input_group = InputGroup(root, f"{name} input", group) self.input_group.create_subgroup("timml") self.input_group.create_subgroup("ttim") return - def create_output_group(self, name: str) -> None: - root = QgsProject.instance().layerTreeRoot() - self.output_group = LayersPanelGroup(root, f"{name} output") + def create_output_group(self, name: str, group: str = None) -> None: + root = self.qgs_project.layerTreeRoot() + self.output_group = OutputGroup(root, f"{name} output", group) # Pre-create the groups here to make sure the vector group ends up on top. # Apparently moving it destroys the group? self.output_group.create_subgroup("vector")