From 6e1f10bf914770787a4e54c46e88058df1f2fdf6 Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Wed, 22 Jan 2025 16:13:12 +0200 Subject: [PATCH 1/2] improve LoadPlanDialog - UI adjustments: enable dialog resizing to smaller, disable table cell editing, set table columns to resize to content (and ID column to stretch), reorder columns, change QLineEdit to QgsFilterLineEdit, change UI labeling and spacing - automatically load the plans of the first found connection - show active plan as selected if present in the selected connection - refactor code --- arho_feature_template/core/plan_manager.py | 6 +- .../gui/dialogs/load_plan_dialog.py | 135 +++++++++++----- .../gui/dialogs/load_plan_dialog.ui | 145 ++++++++---------- 3 files changed, 160 insertions(+), 126 deletions(-) diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index 43605ce..3947e19 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -279,16 +279,16 @@ def set_active_plan(self, plan_id: str | None): def load_land_use_plan(self): """Load an existing land use plan using a dialog selection.""" - connections = get_existing_database_connection_names() + connection_names = get_existing_database_connection_names() - if not connections: + if not connection_names: QMessageBox.critical(None, "Error", "No database connections found.") return if not handle_unsaved_changes(): return - dialog = LoadPlanDialog(None, connections) + dialog = LoadPlanDialog(None, connection_names) if dialog.exec_() == QDialog.Accepted: selected_plan_id = dialog.get_selected_plan_id() diff --git a/arho_feature_template/gui/dialogs/load_plan_dialog.py b/arho_feature_template/gui/dialogs/load_plan_dialog.py index a881d2a..6670aeb 100644 --- a/arho_feature_template/gui/dialogs/load_plan_dialog.py +++ b/arho_feature_template/gui/dialogs/load_plan_dialog.py @@ -1,12 +1,25 @@ +from __future__ import annotations + from importlib import resources +from typing import Sequence from qgis.core import QgsProviderRegistry from qgis.PyQt import uic from qgis.PyQt.QtCore import QRegularExpression, QSortFilterProxyModel, Qt from qgis.PyQt.QtGui import QStandardItem, QStandardItemModel -from qgis.PyQt.QtWidgets import QComboBox, QDialog, QDialogButtonBox, QLineEdit, QMessageBox, QPushButton, QTableView +from qgis.PyQt.QtWidgets import ( + QComboBox, + QDialog, + QDialogButtonBox, + QHeaderView, + QLineEdit, + QMessageBox, + QPushButton, + QTableView, +) from arho_feature_template.core.exceptions import UnexpectedNoneError +from arho_feature_template.utils.misc_utils import get_active_plan_id ui_path = resources.files(__package__) / "load_plan_dialog.ui" @@ -14,6 +27,11 @@ class PlanFilterProxyModel(QSortFilterProxyModel): + def __init__(self, model: QStandardItemModel): + super().__init__() + self.setSourceModel(model) + self.setFilterCaseSensitivity(Qt.CaseInsensitive) + def filterAcceptsRow(self, source_row, source_parent): # noqa: N802 model = self.sourceModel() if not model: @@ -33,57 +51,97 @@ def filterAcceptsRow(self, source_row, source_parent): # noqa: N802 class LoadPlanDialog(QDialog, LoadPlanDialogBase): # type: ignore - connectionComboBox: QComboBox # noqa: N815 - push_button_load: QPushButton - planTableView: QTableView # noqa: N815 - searchLineEdit: QLineEdit # noqa: N815 - buttonBox: QDialogButtonBox # noqa: N815 + connections_selection: QComboBox + load_btn: QPushButton + plan_table_view: QTableView + search_line_edit: QLineEdit + button_box: QDialogButtonBox - def __init__(self, parent, connections): + ID_COLUMN = 4 + + def __init__(self, parent, connection_names: list[str]): super().__init__(parent) self.setupUi(self) self._selected_plan_id = None - self.buttonBox.rejected.connect(self.reject) - self.buttonBox.accepted.connect(self.accept) - self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) + self.button_box.rejected.connect(self.reject) + self.button_box.accepted.connect(self.accept) + self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) - self.push_button_load.clicked.connect(self.load_plans) - self.searchLineEdit.textChanged.connect(self.filter_plans) + self.load_btn.clicked.connect(self.load_plans) + self.search_line_edit.textChanged.connect(self.filter_plans) - self.connectionComboBox.addItems(connections) + self.connections_selection.addItems(connection_names) - self.planTableView.setSelectionMode(QTableView.SingleSelection) - self.planTableView.setSelectionBehavior(QTableView.SelectRows) + self.plan_table_view: QTableView + self.plan_table_view.setSelectionMode(QTableView.SingleSelection) + self.plan_table_view.setSelectionBehavior(QTableView.SelectRows) self.model = QStandardItemModel() self.model.setColumnCount(5) self.model.setHorizontalHeaderLabels( [ - "ID", - "Tuottajan kaavatunnus", "Nimi", - "Kaavan elinkaaren tila", + "Tuottajan kaavatunnus", "Kaavalaji", + "Kaavan elinkaaren tila", + "ID", ] ) - self.filterProxyModel = PlanFilterProxyModel() - self.filterProxyModel.setSourceModel(self.model) - self.filterProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) + self.filterProxyModel = PlanFilterProxyModel(self.model) - self.planTableView.setModel(self.filterProxyModel) - self.planTableView.selectionModel().selectionChanged.connect(self.on_selection_changed) + self.plan_table_view.setModel(self.filterProxyModel) + self.plan_table_view.selectionModel().selectionChanged.connect(self.on_selection_changed) + # self.plan_table_view.setSortingEnabled(True) - def load_plans(self): + header = self.plan_table_view.horizontalHeader() + for i in range(4): + header.setSectionResizeMode(i, QHeaderView.ResizeToContents) + header.setSectionResizeMode(4, QHeaderView.Stretch) + + # Show plans for the first connections by default + # NOTE: Could be changed to the previously used connection if/when plugin can remember it + if len(connection_names) > 0: + self.load_plans() + + def clear_table(self): self.model.removeRows(0, self.model.rowCount()) - selected_connection = self.connectionComboBox.currentText() + def load_plans(self): + self.clear_table() + + selected_connection = self.connections_selection.currentText() if not selected_connection: - self.planTableView.setModel(QStandardItemModel()) return + active_plan_id = get_active_plan_id() + row_to_select = None + plans = self.get_plans_from_db(selected_connection) + for i, plan in enumerate(plans): + id_, producers_plan_identifier, name, lifecycle_status, plan_type = plan + self.model.appendRow( + [ + QStandardItem(name or ""), + QStandardItem(producers_plan_identifier or ""), + QStandardItem(plan_type or ""), + QStandardItem(lifecycle_status or ""), + QStandardItem(id_ or ""), + ] + ) + if active_plan_id == id_: + row_to_select = i + + if row_to_select is not None: + self.plan_table_view.selectRow(row_to_select) + + def get_plans_from_db(self, selected_connection: str) -> Sequence[str]: + """ + Loads plans from the selected DB connection. + + Returns plan information in the format [ID, producers_plan_identifier, name, lifecycle_status, plan_type]. + """ provider_registry = QgsProviderRegistry.instance() if provider_registry is None: raise UnexpectedNoneError @@ -111,15 +169,13 @@ def load_plans(self): ON p.plan_type_id = pt.id; """) - for plan in plans: - self.model.appendRow([QStandardItem(column or "") for column in plan]) - except Exception as e: # noqa: BLE001 QMessageBox.critical(self, "Error", f"Failed to load plans: {e}") - self.model.removeRows(0, self.model.rowCount()) + self.clear_table() + return plans def filter_plans(self): - search_text = self.searchLineEdit.text() + search_text = self.search_line_edit.text() if search_text: search_regex = QRegularExpression(search_text) self.filterProxyModel.setFilterRegularExpression(search_regex) @@ -127,19 +183,22 @@ def filter_plans(self): self.filterProxyModel.setFilterRegularExpression("") def on_selection_changed(self): - # Enable the OK button only if a row is selected - selection = self.planTableView.selectionModel().selectedRows() - ok_button = self.buttonBox.button(QDialogButtonBox.Ok) + """ + Check active selection in `plan_table_view`. + + Enable the OK button only if a row is selected. + """ + selection = self.plan_table_view.selectionModel().selectedRows() if selection: selected_row = selection[0].row() - self._selected_plan_id = self.planTableView.model().index(selected_row, 0).data() - ok_button.setEnabled(True) + self._selected_plan_id = self.plan_table_view.model().index(selected_row, self.ID_COLUMN).data() + self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) else: self._selected_plan_id = None - ok_button.setEnabled(False) + self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) def get_selected_connection(self): - return self.connectionComboBox.currentText() + return self.connections_selection.currentText() def get_selected_plan_id(self): return self._selected_plan_id diff --git a/arho_feature_template/gui/dialogs/load_plan_dialog.ui b/arho_feature_template/gui/dialogs/load_plan_dialog.ui index 0a0c82d..dd5c261 100644 --- a/arho_feature_template/gui/dialogs/load_plan_dialog.ui +++ b/arho_feature_template/gui/dialogs/load_plan_dialog.ui @@ -1,19 +1,25 @@ - LoadPlanDialog - + dialog + 0 0 - 800 - 600 + 642 + 414 - 800 - 600 + 440 + 360 + + + + + 650 + 440 @@ -21,26 +27,44 @@ - - - Valitse tietokantayhteys: - - - - - + - + - + 0 0 + + Tietokanta + - + + + + 0 + 0 + + + + + 200 + 0 + + + + + + + + + 0 + 0 + + Lataa kaavat @@ -49,92 +73,36 @@ - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - - - Etsi kaavoja: + + + true - - - - - - - 0 - 0 - - - - Etsi kaavoja... + + - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - - - Kaavat: - - - - - + 1 1 - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - + 0 0 - + + QAbstractItemView::NoEditTriggers + + - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -142,6 +110,13 @@ + + + QgsFilterLineEdit + QLineEdit +
qgsfilterlineedit.h
+
+
From b5fbb2531eb626f9c493272a49843555abc9ea49 Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Fri, 24 Jan 2025 10:05:00 +0200 Subject: [PATCH 2/2] remove ID column from load plan dialog --- .../gui/dialogs/load_plan_dialog.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/arho_feature_template/gui/dialogs/load_plan_dialog.py b/arho_feature_template/gui/dialogs/load_plan_dialog.py index 6670aeb..de42c6e 100644 --- a/arho_feature_template/gui/dialogs/load_plan_dialog.py +++ b/arho_feature_template/gui/dialogs/load_plan_dialog.py @@ -57,7 +57,7 @@ class LoadPlanDialog(QDialog, LoadPlanDialogBase): # type: ignore search_line_edit: QLineEdit button_box: QDialogButtonBox - ID_COLUMN = 4 + # ID_COLUMN = 4 def __init__(self, parent, connection_names: list[str]): super().__init__(parent) @@ -79,14 +79,14 @@ def __init__(self, parent, connection_names: list[str]): self.plan_table_view.setSelectionBehavior(QTableView.SelectRows) self.model = QStandardItemModel() - self.model.setColumnCount(5) + self.model.setColumnCount(4) self.model.setHorizontalHeaderLabels( [ "Nimi", - "Tuottajan kaavatunnus", "Kaavalaji", "Kaavan elinkaaren tila", - "ID", + "Tuottajan kaavatunnus", + # "ID", ] ) @@ -97,9 +97,9 @@ def __init__(self, parent, connection_names: list[str]): # self.plan_table_view.setSortingEnabled(True) header = self.plan_table_view.horizontalHeader() - for i in range(4): + for i in range(3): header.setSectionResizeMode(i, QHeaderView.ResizeToContents) - header.setSectionResizeMode(4, QHeaderView.Stretch) + header.setSectionResizeMode(3, QHeaderView.Stretch) # Show plans for the first connections by default # NOTE: Could be changed to the previously used connection if/when plugin can remember it @@ -124,12 +124,13 @@ def load_plans(self): self.model.appendRow( [ QStandardItem(name or ""), - QStandardItem(producers_plan_identifier or ""), QStandardItem(plan_type or ""), QStandardItem(lifecycle_status or ""), - QStandardItem(id_ or ""), + QStandardItem(producers_plan_identifier or ""), + # QStandardItem(id_ or ""), ] ) + self.model.item(i, 0).setData(id_, Qt.UserRole) if active_plan_id == id_: row_to_select = i @@ -191,7 +192,7 @@ def on_selection_changed(self): selection = self.plan_table_view.selectionModel().selectedRows() if selection: selected_row = selection[0].row() - self._selected_plan_id = self.plan_table_view.model().index(selected_row, self.ID_COLUMN).data() + self._selected_plan_id = self.plan_table_view.model().index(selected_row, 0).data(Qt.UserRole) self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) else: self._selected_plan_id = None