From 54e3668edd386e3df9743945c5fe798af5095f45 Mon Sep 17 00:00:00 2001 From: Mathieu Pellerin Date: Sun, 28 Apr 2024 16:22:54 +0700 Subject: [PATCH] [attribute form] Add parent feature scope when adding a child feature through the relation editor widget --- .../core/auto_generated/qgstrackedvectorlayertools.sip.in | 3 ++- .../core/auto_generated/vector/qgsvectorlayertools.sip.in | 3 ++- python/core/auto_generated/qgstrackedvectorlayertools.sip.in | 3 ++- python/core/auto_generated/vector/qgsvectorlayertools.sip.in | 3 ++- src/app/qgsfeatureaction.cpp | 2 ++ src/app/qgsguivectorlayertools.cpp | 4 ++-- src/app/qgsguivectorlayertools.h | 5 +++-- src/core/qgstrackedvectorlayertools.cpp | 5 ++--- src/core/qgstrackedvectorlayertools.h | 4 +++- src/core/vector/qgsvectorlayertools.h | 4 +++- src/gui/qgsabstractrelationeditorwidget.cpp | 4 +++- tests/src/gui/testqgsrelationreferencewidget.cpp | 3 ++- 12 files changed, 28 insertions(+), 15 deletions(-) diff --git a/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in b/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in index ca103b0fe903..5b720368f1cc 100644 --- a/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in +++ b/python/PyQt6/core/auto_generated/qgstrackedvectorlayertools.sip.in @@ -20,7 +20,7 @@ class QgsTrackedVectorLayerTools : QgsVectorLayerTools Constructor for QgsTrackedVectorLayerTools. %End - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false ) const; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const; %Docstring This method calls the addFeature method of the backend :py:class:`QgsVectorLayerTools` @@ -32,6 +32,7 @@ This method calls the addFeature method of the backend :py:class:`QgsVectorLayer :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened +:param scope: A context scope to be used to calculate feature expression-based values :return: ``True`` in case of success, ``False`` if the operation failed/was aborted %End diff --git a/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in b/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in index 85c5a1eee2e3..c37a098bb3da 100644 --- a/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in +++ b/python/PyQt6/core/auto_generated/vector/qgsvectorlayertools.sip.in @@ -28,7 +28,7 @@ in your application. QgsVectorLayerTools(); - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false ) const = 0; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const = 0; %Docstring This method should/will be called, whenever a new feature will be added to the layer @@ -38,6 +38,7 @@ This method should/will be called, whenever a new feature will be added to the l :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened +:param scope: A context scope to be used to calculate feature expression-based values :return: - ``True`` in case of success, ``False`` if the operation failed/was aborted - feature: Updated feature after adding will be written back to this diff --git a/python/core/auto_generated/qgstrackedvectorlayertools.sip.in b/python/core/auto_generated/qgstrackedvectorlayertools.sip.in index ca103b0fe903..5b720368f1cc 100644 --- a/python/core/auto_generated/qgstrackedvectorlayertools.sip.in +++ b/python/core/auto_generated/qgstrackedvectorlayertools.sip.in @@ -20,7 +20,7 @@ class QgsTrackedVectorLayerTools : QgsVectorLayerTools Constructor for QgsTrackedVectorLayerTools. %End - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false ) const; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const; %Docstring This method calls the addFeature method of the backend :py:class:`QgsVectorLayerTools` @@ -32,6 +32,7 @@ This method calls the addFeature method of the backend :py:class:`QgsVectorLayer :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened +:param scope: A context scope to be used to calculate feature expression-based values :return: ``True`` in case of success, ``False`` if the operation failed/was aborted %End diff --git a/python/core/auto_generated/vector/qgsvectorlayertools.sip.in b/python/core/auto_generated/vector/qgsvectorlayertools.sip.in index 85c5a1eee2e3..c37a098bb3da 100644 --- a/python/core/auto_generated/vector/qgsvectorlayertools.sip.in +++ b/python/core/auto_generated/vector/qgsvectorlayertools.sip.in @@ -28,7 +28,7 @@ in your application. QgsVectorLayerTools(); - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false ) const = 0; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, QWidget *parentWidget = 0, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = 0 ) const = 0; %Docstring This method should/will be called, whenever a new feature will be added to the layer @@ -38,6 +38,7 @@ This method should/will be called, whenever a new feature will be added to the l :param parentWidget: The widget calling this function to be passed to the used dialog :param showModal: If the used dialog should be modal or not :param hideParent: If the parent widget should be hidden, when the used dialog is opened +:param scope: A context scope to be used to calculate feature expression-based values :return: - ``True`` in case of success, ``False`` if the operation failed/was aborted - feature: Updated feature after adding will be written back to this diff --git a/src/app/qgsfeatureaction.cpp b/src/app/qgsfeatureaction.cpp index cac3d2b796bf..26d51147fc15 100644 --- a/src/app/qgsfeatureaction.cpp +++ b/src/app/qgsfeatureaction.cpp @@ -203,7 +203,9 @@ QgsFeatureAction::AddFeatureResult QgsFeatureAction::addFeature( const QgsAttrib // values and field constraints QgsExpressionContext context = mLayer->createExpressionContext(); if ( scope ) + { context.appendScope( scope.release() ); + } const QgsFeature newFeature = QgsVectorLayerUtils::createFeature( mLayer, mFeature->geometry(), initialAttributeValues, &context ); diff --git a/src/app/qgsguivectorlayertools.cpp b/src/app/qgsguivectorlayertools.cpp index 94157f17dba2..acd58434155e 100644 --- a/src/app/qgsguivectorlayertools.cpp +++ b/src/app/qgsguivectorlayertools.cpp @@ -27,7 +27,7 @@ #include "qgsvectorlayer.h" #include "qgsvectorlayerutils.h" -bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat, QWidget *parentWidget, bool showModal, bool hideParent ) const +bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat, QWidget *parentWidget, bool showModal, bool hideParent, QgsExpressionContextScope *scope ) const { QgsFeature *f = feat; if ( !feat ) @@ -37,7 +37,7 @@ bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttribu QgsFeatureAction *a = new QgsFeatureAction( tr( "Add feature" ), *f, layer, QUuid(), -1, parentWidget ); a->setForceSuppressFormPopup( forceSuppressFormPopup() ); connect( a, &QgsFeatureAction::addFeatureFinished, a, &QObject::deleteLater ); - const QgsFeatureAction::AddFeatureResult result = a->addFeature( defaultValues, showModal, nullptr, hideParent ); + const QgsFeatureAction::AddFeatureResult result = a->addFeature( defaultValues, showModal, std::unique_ptr( scope ), hideParent ); if ( !feat ) delete f; diff --git a/src/app/qgsguivectorlayertools.h b/src/app/qgsguivectorlayertools.h index 400995960b6b..57be6215afeb 100644 --- a/src/app/qgsguivectorlayertools.h +++ b/src/app/qgsguivectorlayertools.h @@ -44,10 +44,11 @@ class QgsGuiVectorLayerTools : public QgsVectorLayerTools * \param parentWidget The widget calling this function to be passed to the used dialog * \param showModal If the used dialog should be modal or not * \param hideParent If the parent widget should be hidden, when the used dialog is opened + * \param scope A context scope to be used to calculate feature expression-based values * - * \returns TRUE in case of success, FALSE if the operation failed/was aborted + * \returns TRUE in case of success, FALSE if the operation failed/was aborted */ - bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false ) const override; + bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const override; /** * This should be called, whenever a vector layer should be switched to edit mode. If successful diff --git a/src/core/qgstrackedvectorlayertools.cpp b/src/core/qgstrackedvectorlayertools.cpp index 3256f0fd89ac..b6b753b785b4 100644 --- a/src/core/qgstrackedvectorlayertools.cpp +++ b/src/core/qgstrackedvectorlayertools.cpp @@ -17,15 +17,14 @@ #include "qgsvectorlayer.h" -bool QgsTrackedVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget, bool showModal, bool hideParent ) const +bool QgsTrackedVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget, bool showModal, bool hideParent, QgsExpressionContextScope *scope ) const { QgsFeature *f = feature; if ( !feature ) f = new QgsFeature(); const_cast( mBackend )->setForceSuppressFormPopup( forceSuppressFormPopup() ); - - if ( mBackend->addFeature( layer, defaultValues, defaultGeometry, f, parentWidget, showModal, hideParent ) ) + if ( mBackend->addFeature( layer, defaultValues, defaultGeometry, f, parentWidget, showModal, hideParent, scope ) ) { mAddedFeatures[layer].insert( f->id() ); if ( !feature ) diff --git a/src/core/qgstrackedvectorlayertools.h b/src/core/qgstrackedvectorlayertools.h index f12b127d9a78..64bb534394a4 100644 --- a/src/core/qgstrackedvectorlayertools.h +++ b/src/core/qgstrackedvectorlayertools.h @@ -18,6 +18,7 @@ #include "qgis_core.h" #include "qgsvectorlayertools.h" +#include "qgsexpressioncontext.h" /** * \ingroup core @@ -43,10 +44,11 @@ class CORE_EXPORT QgsTrackedVectorLayerTools : public QgsVectorLayerTools * \param parentWidget The widget calling this function to be passed to the used dialog * \param showModal If the used dialog should be modal or not * \param hideParent If the parent widget should be hidden, when the used dialog is opened + * \param scope A context scope to be used to calculate feature expression-based values * * \returns TRUE in case of success, FALSE if the operation failed/was aborted */ - bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false ) const override; + bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const override; bool startEditing( QgsVectorLayer *layer ) const override; bool stopEditing( QgsVectorLayer *layer, bool allowCancel ) const override; bool saveEdits( QgsVectorLayer *layer ) const override; diff --git a/src/core/vector/qgsvectorlayertools.h b/src/core/vector/qgsvectorlayertools.h index ac9dac7dfd7e..d8cc9e1386eb 100644 --- a/src/core/vector/qgsvectorlayertools.h +++ b/src/core/vector/qgsvectorlayertools.h @@ -20,6 +20,7 @@ #include "qgis_sip.h" #include +#include "qgsexpressioncontext.h" #include "qgsfeature.h" #include "qgsgeometry.h" @@ -56,10 +57,11 @@ class CORE_EXPORT QgsVectorLayerTools : public QObject * \param parentWidget The widget calling this function to be passed to the used dialog * \param showModal If the used dialog should be modal or not * \param hideParent If the parent widget should be hidden, when the used dialog is opened + * \param scope A context scope to be used to calculate feature expression-based values * \returns TRUE in case of success, FALSE if the operation failed/was aborted * */ - virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature SIP_OUT = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false ) const = 0; + virtual bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature SIP_OUT = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const = 0; // TODO QGIS 4: remove const qualifier diff --git a/src/gui/qgsabstractrelationeditorwidget.cpp b/src/gui/qgsabstractrelationeditorwidget.cpp index cd03a80e089c..808a73010be7 100644 --- a/src/gui/qgsabstractrelationeditorwidget.cpp +++ b/src/gui/qgsabstractrelationeditorwidget.cpp @@ -19,6 +19,7 @@ #include "qgsfeatureiterator.h" #include "qgsexpression.h" +#include "qgsexpressioncontextutils.h" #include "qgsfeature.h" #include "qgsfeatureselectiondlg.h" #include "qgsrelation.h" @@ -286,8 +287,9 @@ QgsFeatureIds QgsAbstractRelationEditorWidget::addFeature( const QgsGeometry &ge for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs ) keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeatureList.first().attribute( fieldPair.referencedField() ) ); + QgsExpressionContextScope *scope = QgsExpressionContextUtils::parentFormScope( mFeatureList.first(), mEditorContext.attributeFormModeString() ); QgsFeature linkFeature; - if ( !vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature, this, true, true ) ) + if ( !vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature, this, true, true, scope ) ) return QgsFeatureIds(); addedFeatureIds.insert( linkFeature.id() ); diff --git a/tests/src/gui/testqgsrelationreferencewidget.cpp b/tests/src/gui/testqgsrelationreferencewidget.cpp index 546f1b854183..9530dfcfabf0 100644 --- a/tests/src/gui/testqgsrelationreferencewidget.cpp +++ b/tests/src/gui/testqgsrelationreferencewidget.cpp @@ -609,11 +609,12 @@ void TestQgsRelationReferenceWidget::testIdentifyOnMap() // referenced layer class DummyVectorLayerTools : public QgsVectorLayerTools // clazy:exclude=missing-qobject-macro { - bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &, const QgsGeometry &, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false ) const override + bool addFeature( QgsVectorLayer *layer, const QgsAttributeMap &, const QgsGeometry &, QgsFeature *feat = nullptr, QWidget *parentWidget = nullptr, bool showModal = true, bool hideParent = false, QgsExpressionContextScope *scope = nullptr ) const override { Q_UNUSED( parentWidget ); Q_UNUSED( showModal ); Q_UNUSED( hideParent ); + Q_UNUSED( scope ); feat->setAttribute( QStringLiteral( "pk" ), 13 ); feat->setAttribute( QStringLiteral( "material" ), QStringLiteral( "steel" ) ); feat->setAttribute( QStringLiteral( "diameter" ), 140 );