From 6be94e1a030929a549e047e13273db6a5e0d070e Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 21 Aug 2024 10:27:56 +0200 Subject: [PATCH 1/3] untiny tiny icon --- .../images/LinkingRelationEditor-icon.svg | 184 +++++++++++++++--- 1 file changed, 162 insertions(+), 22 deletions(-) diff --git a/linking_relation_editor/images/LinkingRelationEditor-icon.svg b/linking_relation_editor/images/LinkingRelationEditor-icon.svg index 27527eb..e55e9c5 100644 --- a/linking_relation_editor/images/LinkingRelationEditor-icon.svg +++ b/linking_relation_editor/images/LinkingRelationEditor-icon.svg @@ -1,25 +1,165 @@ - - - - - + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + From 974bdd81c2fc706bb0b35035a134b8722298356b Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 21 Aug 2024 10:53:08 +0200 Subject: [PATCH 2/3] Default FilterExpression passed by the configuration. According to https://github.com/qgis/QGIS/pull/58448 --- .../gui/linking_child_manager_dialog.py | 8 +- .../gui/linking_relation_editor_widget.py | 3 + .../test_linking_child_manager_dialog.py | 93 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/linking_relation_editor/gui/linking_child_manager_dialog.py b/linking_relation_editor/gui/linking_child_manager_dialog.py index d521d43..70e9d61 100644 --- a/linking_relation_editor/gui/linking_child_manager_dialog.py +++ b/linking_relation_editor/gui/linking_child_manager_dialog.py @@ -23,6 +23,7 @@ QgsHighlight, QgsIdentifyMenu, QgsMessageBar, + QgsAttributeForm ) from qgis.PyQt.QtCore import QModelIndex, Qt, QTimer from qgis.PyQt.QtWidgets import QAction, QDialog, QMessageBox @@ -52,6 +53,7 @@ def __init__( nmRelation: QgsRelation, editorContext: QgsAttributeEditorContext, oneToOne: bool, + filterExpression: str, linkingChildManagerDialogConfig: dict, parent=None, ): @@ -64,6 +66,7 @@ def __init__( self._nmRelation = nmRelation self._editorContext = editorContext self._oneToOne = oneToOne + self._filterExpression = filterExpression self._linkingChildManagerDialogConfig = linkingChildManagerDialogConfig self._mapToolSelect = None @@ -185,7 +188,10 @@ def __init__( iface.messageBar(), QgsMessageBar.defaultMessageTimeout(), ) - self._feature_filter_widget.filterShowAll() + if self._filterExpression: + self._feature_filter_widget.setFilterExpression(self._filterExpression,QgsAttributeForm.ReplaceFilter, True) + else: + self._feature_filter_widget.filterShowAll() # Signal slots self.accepted.connect(self._accepting) diff --git a/linking_relation_editor/gui/linking_relation_editor_widget.py b/linking_relation_editor/gui/linking_relation_editor_widget.py index b5db2f6..d47e8e2 100644 --- a/linking_relation_editor/gui/linking_relation_editor_widget.py +++ b/linking_relation_editor/gui/linking_relation_editor_widget.py @@ -185,6 +185,7 @@ def config(self): return { "buttons": metaEnumFromValue(QgsRelationEditorWidget.Button.AllButtons).valueToKeys(self.visibleButtons()), "show_first_feature": self.mShowFirstFeature, + "filter_exression": self.mFilterExpression, CONFIG_ONE_TO_ONE: self.mOneToOne, CONFIG_LINKING_CHILD_MANAGER_DIALOG: self.mLinkingChildManagerDialogConfig, } @@ -195,6 +196,7 @@ def setConfig(self, config): config.get("buttons", metaEnumButtons.valueToKeys(QgsRelationEditorWidget.Button.AllButtons)) ) self.mShowFirstFeature = config.get("show_first_feature", True) + self.mFilterExpression = config.get("filter_expression") self.mOneToOne = config.get(CONFIG_ONE_TO_ONE) self.mLinkingChildManagerDialogConfig = config.get(CONFIG_LINKING_CHILD_MANAGER_DIALOG, {}) self.updateButtons() @@ -589,6 +591,7 @@ def _execLinkFeatureDialog(self): self.nmRelation(), self.editorContext(), self.mOneToOne, + self.mFilterExpression, self.mLinkingChildManagerDialogConfig, self, ) diff --git a/linking_relation_editor/tests/test_linking_child_manager_dialog.py b/linking_relation_editor/tests/test_linking_child_manager_dialog.py index 137dd9a..03b5094 100644 --- a/linking_relation_editor/tests/test_linking_child_manager_dialog.py +++ b/linking_relation_editor/tests/test_linking_child_manager_dialog.py @@ -138,6 +138,7 @@ def test_Instantiate(self): QgsRelation(), QgsAttributeEditorContext(), False, + None, {}, None, ) @@ -160,6 +161,7 @@ def test_InstantiateRelation1N(self): QgsRelation(), QgsAttributeEditorContext(), False, + None, {}, None, ) @@ -182,6 +184,7 @@ def test_InstantiateRelationNM(self): self.mRelationNM, QgsAttributeEditorContext(), False, + None, {}, None, ) @@ -206,6 +209,7 @@ def test_quickFilter(self): QgsRelation(), QgsAttributeEditorContext(), False, + None, {}, None, ) @@ -306,3 +310,92 @@ def test_quickFilter(self): dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(1, 0), Qt.DisplayRole), "Layer1-1: Martina formerly known as Prisca", ) + + def test_passed_filter_expression(self): + # get a parent with no childs + parentFeature = QgsFeature() + for feature in self.mLayer2.getFeatures(): + if feature.attribute("pk") == 12: + parentFeature = feature + break + + self.assertTrue(parentFeature.isValid()) + + dialog = LinkingChildManagerDialog( + self.mLayer1, + self.mLayer2, + parentFeature, + self.mRelation, + QgsRelation(), + QgsAttributeEditorContext(), + False, + None, + {}, + None, + ) + + self.assertEqual(dialog.mLayerNameLabel.text(), self.mLayer1.name()) + + # all entries + # "Layer1-0: The Artist formerly known as Prince" + # "Layer1-1: Martina formerly known as Prisca" + self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 2) + self.assertEqual( + dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(0, 0), Qt.DisplayRole), + "Layer1-0: The Artist formerly known as Prince", + ) + self.assertEqual( + dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(1, 0), Qt.DisplayRole), + "Layer1-1: Martina formerly known as Prisca", + ) + + dialog = LinkingChildManagerDialog( + self.mLayer1, + self.mLayer2, + parentFeature, + self.mRelation, + QgsRelation(), + QgsAttributeEditorContext(), + False, + "name LIKE '%formerly known as Prince%'", + {}, + None, + ) + + self.assertEqual(dialog.mLayerNameLabel.text(), self.mLayer1.name()) + + # one entry + # "Layer1-0: The Artist formerly known as Prince" + self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 1) + self.assertEqual( + dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(0, 0), Qt.DisplayRole), + "Layer1-0: The Artist formerly known as Prince", + ) + + dialog = LinkingChildManagerDialog( + self.mLayer1, + self.mLayer2, + parentFeature, + self.mRelation, + QgsRelation(), + QgsAttributeEditorContext(), + False, + "name LIKE '%formerly%'", + {}, + None, + ) + + self.assertEqual(dialog.mLayerNameLabel.text(), self.mLayer1.name()) + + # all entries with formerly + # "Layer1-0: The Artist formerly known as Prince" + # "Layer1-1: Martina formerly known as Prisca" + self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 2) + self.assertEqual( + dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(0, 0), Qt.DisplayRole), + "Layer1-0: The Artist formerly known as Prince", + ) + self.assertEqual( + dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(1, 0), Qt.DisplayRole), + "Layer1-1: Martina formerly known as Prisca", + ) \ No newline at end of file From 7a870d51d746f3f0593e8670c68681b18f9abd5a Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 21 Aug 2024 11:03:01 +0200 Subject: [PATCH 3/3] Use MagicMockup to mockup iface for tests... --- .../gui/linking_child_manager_dialog.py | 30 +++++++++------ .../test_linking_child_manager_dialog.py | 38 +++++++++---------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/linking_relation_editor/gui/linking_child_manager_dialog.py b/linking_relation_editor/gui/linking_child_manager_dialog.py index 70e9d61..c849120 100644 --- a/linking_relation_editor/gui/linking_child_manager_dialog.py +++ b/linking_relation_editor/gui/linking_child_manager_dialog.py @@ -29,6 +29,7 @@ from qgis.PyQt.QtWidgets import QAction, QDialog, QMessageBox from qgis.PyQt.uic import loadUiType from qgis.utils import iface +from unittest.mock import MagicMock from linking_relation_editor.core.model.attribute_form_delegate import ( AttributeFormDelegate, @@ -180,18 +181,23 @@ def __init__( self._feature_filter_widget = FeatureFilterWidget(self) self.mFooterHBoxLayout.insertWidget(0, self._feature_filter_widget) - if iface: # TODO how to use iface in tests? - self._feature_filter_widget.init( - self._layer, - self._editorContext, - self._featuresModelFilterLeft, - iface.messageBar(), - QgsMessageBar.defaultMessageTimeout(), - ) - if self._filterExpression: - self._feature_filter_widget.setFilterExpression(self._filterExpression,QgsAttributeForm.ReplaceFilter, True) - else: - self._feature_filter_widget.filterShowAll() + + # used for untittest + if not iface: + i = MagicMock() + else: + i = iface + self._feature_filter_widget.init( + self._layer, + self._editorContext, + self._featuresModelFilterLeft, + i.messageBar(), + QgsMessageBar.defaultMessageTimeout(), + ) + if self._filterExpression: + self._feature_filter_widget.setFilterExpression(self._filterExpression,QgsAttributeForm.ReplaceFilter, True) + else: + self._feature_filter_widget.filterShowAll() # Signal slots self.accepted.connect(self._accepting) diff --git a/linking_relation_editor/tests/test_linking_child_manager_dialog.py b/linking_relation_editor/tests/test_linking_child_manager_dialog.py index 03b5094..b98d632 100644 --- a/linking_relation_editor/tests/test_linking_child_manager_dialog.py +++ b/linking_relation_editor/tests/test_linking_child_manager_dialog.py @@ -320,7 +320,7 @@ def test_passed_filter_expression(self): break self.assertTrue(parentFeature.isValid()) - + dialog = LinkingChildManagerDialog( self.mLayer1, self.mLayer2, @@ -329,25 +329,20 @@ def test_passed_filter_expression(self): QgsRelation(), QgsAttributeEditorContext(), False, - None, + "name LIKE '%formerly known as Prince%'", {}, None, ) self.assertEqual(dialog.mLayerNameLabel.text(), self.mLayer1.name()) - # all entries + # one entry # "Layer1-0: The Artist formerly known as Prince" - # "Layer1-1: Martina formerly known as Prisca" - self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 2) + self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 1) self.assertEqual( dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(0, 0), Qt.DisplayRole), "Layer1-0: The Artist formerly known as Prince", ) - self.assertEqual( - dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(1, 0), Qt.DisplayRole), - "Layer1-1: Martina formerly known as Prisca", - ) dialog = LinkingChildManagerDialog( self.mLayer1, @@ -357,20 +352,25 @@ def test_passed_filter_expression(self): QgsRelation(), QgsAttributeEditorContext(), False, - "name LIKE '%formerly known as Prince%'", + "name LIKE '%formerly%'", {}, None, ) self.assertEqual(dialog.mLayerNameLabel.text(), self.mLayer1.name()) - # one entry + # all entries with formerly # "Layer1-0: The Artist formerly known as Prince" - self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 1) + # "Layer1-1: Martina formerly known as Prisca" + self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 2) self.assertEqual( dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(0, 0), Qt.DisplayRole), "Layer1-0: The Artist formerly known as Prince", ) + self.assertEqual( + dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(1, 0), Qt.DisplayRole), + "Layer1-1: Martina formerly known as Prisca", + ) dialog = LinkingChildManagerDialog( self.mLayer1, @@ -380,22 +380,18 @@ def test_passed_filter_expression(self): QgsRelation(), QgsAttributeEditorContext(), False, - "name LIKE '%formerly%'", + "name LIKE '%Prisca%'", {}, None, ) self.assertEqual(dialog.mLayerNameLabel.text(), self.mLayer1.name()) - # all entries with formerly + # one entry # "Layer1-0: The Artist formerly known as Prince" - # "Layer1-1: Martina formerly known as Prisca" - self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 2) + self.assertEqual(dialog._featuresModelFilterLeft.rowCount(), 1) self.assertEqual( dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(0, 0), Qt.DisplayRole), - "Layer1-0: The Artist formerly known as Prince", - ) - self.assertEqual( - dialog._featuresModelFilterLeft.data(dialog._featuresModelFilterLeft.index(1, 0), Qt.DisplayRole), "Layer1-1: Martina formerly known as Prisca", - ) \ No newline at end of file + ) + \ No newline at end of file