Address review: add vector layer tools context class, implement addFeatureV2

This commit is contained in:
Mathieu Pellerin 2024-05-22 11:20:29 +07:00
parent 19cb97109c
commit 96a711a2ae
25 changed files with 570 additions and 69 deletions

View File

@ -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, QgsExpressionContextScope *scope = 0 ) const;
virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature /Out/, const QgsVectorLayerToolsContext &context ) const;
%Docstring
This method calls the addFeature method of the backend :py:class:`QgsVectorLayerTools`
@ -28,13 +28,10 @@ This method calls the addFeature method of the backend :py:class:`QgsVectorLayer
:param layer: The layer to which the feature should be added
:param defaultValues: Default values for the feature to add
:param defaultGeometry: A default geometry to add to the feature
:param feature: A pointer to the feature
: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
:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
:return: ``True`` in case of success, ``False`` if the operation failed/was aborted
:return: - ``True`` in case of success, ``False`` if the operation failed/was aborted
- feature: A pointer to the feature
%End
virtual bool startEditing( QgsVectorLayer *layer ) const;

View File

@ -1777,7 +1777,7 @@ be updated. This can be used to override default field value expressions.
.. seealso:: :py:func:`updateFeature`
%End
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 );
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 );
%Docstring
Changes an attribute value for a feature (but does not immediately commit the changes).
The ``fid`` argument specifies the ID of the feature to be changed.
@ -1796,7 +1796,7 @@ so it is more efficient to explicitly pass an ``oldValue`` if it is already avai
If ``skipDefaultValues`` is set to ``True``, default field values will not
be updated. This can be used to override default field value expressions.
If ``context`` is provided, it will be used when updating default values.
If ``context`` is provided, it will be used when updating default values (since QGIS 3.38).
:return: ``True`` if the feature's attribute was successfully changed.
@ -1817,7 +1817,7 @@ If ``context`` is provided, it will be used when updating default values.
.. seealso:: :py:func:`updateFeature`
%End
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 );
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 );
%Docstring
Changes attributes' values for a feature (but does not immediately
commit the changes).
@ -1837,7 +1837,7 @@ If ``skipDefaultValues`` is set to ``True``, default field values will not
be updated. This can be used to override default field value
expressions.
If ``context`` is provided, it will be used when updating default values.
If ``context`` is provided, it will be used when updating default values (since QGIS 3.38).
:return: ``True`` if feature's attributes was successfully changed.

View File

@ -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, QgsExpressionContextScope *scope = 0 ) 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 ) const;
%Docstring
This method should/will be called, whenever a new feature will be added to the layer
@ -38,10 +38,32 @@ 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
.. note::
addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools
%End
virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, const QgsVectorLayerToolsContext &context = QgsVectorLayerToolsContext() ) const;
%Docstring
This method should/will be called, whenever a new feature will be added to the layer
:param layer: The layer to which the feature should be added
:param defaultValues: Default values for the feature to add
:param defaultGeometry: A default geometry to add to the feature
:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
:return: - ``True`` in case of success, ``False`` if the operation failed/was aborted
- feature: Updated feature after adding will be written back to this
.. note::
addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools
.. versionadded:: 3.38
%End

View File

@ -0,0 +1,115 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/vector/qgsvectorlayertoolscontext.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsVectorLayerToolsContext
{
%Docstring(signature="appended")
Contains settings which reflect the context in which vector layer tool operations should
consider.
.. versionadded:: 3.38
%End
%TypeHeaderCode
#include "qgsvectorlayertoolscontext.h"
%End
public:
QgsVectorLayerToolsContext();
%Docstring
Constructor for QgsVectorLayerToolsContext.
%End
QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other );
%Docstring
Copy constructor.
:param other: source QgsVectorLayerToolsContext
%End
void setExpressionContext( const QgsExpressionContext *context );
%Docstring
Sets the optional expression context used by the vector layer tools.
:param context: expression context pointer. Ownership is not transferred.
.. seealso:: :py:func:`expressionContext`
.. seealso:: :py:func:`setAdditionalExpressionContextScope`
%End
QgsExpressionContext *expressionContext() const;
%Docstring
Returns the optional expression context used by the vector layer tools.
.. seealso:: :py:func:`setExpressionContext`
.. seealso:: :py:func:`additionalExpressionContextScope`
%End
void setAdditionalExpressionContextScope( const QgsExpressionContextScope *scope );
%Docstring
Sets an additional expression context scope to be made available when calculating expressions.
:param scope: additional scope. Ownership is not transferred and a copy will be made.
.. seealso:: :py:func:`additionalExpressionContextScope`
%End
const QgsExpressionContextScope *additionalExpressionContextScope() const;
%Docstring
Returns an additional expression context scope to be made available when calculating expressions.
.. seealso:: :py:func:`setAdditionalExpressionContextScope`
%End
QWidget *parentWidget() const;
%Docstring
Returns the widget which should be parented to tools dialogues.
%End
void setParentWidget( QWidget *parent );
%Docstring
Sets the widget which should be parented to tools' dialogues.
:param parent: the widget actign as parent
%End
bool showModal() const;
%Docstring
Returns whether tools' dialogues should be modal.
%End
void setShowModal( bool modal );
%Docstring
Sets whether tools' dialogues should be modal.
%End
bool hideParent() const;
%Docstring
Returns whether the parent widget should be hidden when showing tools' dialogues.
%End
void setHideParent( bool hide );
%Docstring
Sets whether the parent widget should be hidden when showing tools' dialogues.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/vector/qgsvectorlayertoolscontext.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -771,6 +771,7 @@
%Include auto_generated/vector/qgsvectorlayerselectionproperties.sip
%Include auto_generated/vector/qgsvectorlayertemporalproperties.sip
%Include auto_generated/vector/qgsvectorlayertools.sip
%Include auto_generated/vector/qgsvectorlayertoolscontext.sip
%Include auto_generated/vector/qgsvectorlayerundocommand.sip
%Include auto_generated/vector/qgsvectorlayerundopassthroughcommand.sip
%Include auto_generated/vector/qgsvectorlayerutils.sip

View File

@ -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, QgsExpressionContextScope *scope = 0 ) const;
virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature /Out/, const QgsVectorLayerToolsContext &context ) const;
%Docstring
This method calls the addFeature method of the backend :py:class:`QgsVectorLayerTools`
@ -28,13 +28,10 @@ This method calls the addFeature method of the backend :py:class:`QgsVectorLayer
:param layer: The layer to which the feature should be added
:param defaultValues: Default values for the feature to add
:param defaultGeometry: A default geometry to add to the feature
:param feature: A pointer to the feature
: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
:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
:return: ``True`` in case of success, ``False`` if the operation failed/was aborted
:return: - ``True`` in case of success, ``False`` if the operation failed/was aborted
- feature: A pointer to the feature
%End
virtual bool startEditing( QgsVectorLayer *layer ) const;

View File

@ -1777,7 +1777,7 @@ be updated. This can be used to override default field value expressions.
.. seealso:: :py:func:`updateFeature`
%End
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 );
bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 );
%Docstring
Changes an attribute value for a feature (but does not immediately commit the changes).
The ``fid`` argument specifies the ID of the feature to be changed.
@ -1796,7 +1796,7 @@ so it is more efficient to explicitly pass an ``oldValue`` if it is already avai
If ``skipDefaultValues`` is set to ``True``, default field values will not
be updated. This can be used to override default field value expressions.
If ``context`` is provided, it will be used when updating default values.
If ``context`` is provided, it will be used when updating default values (since QGIS 3.38).
:return: ``True`` if the feature's attribute was successfully changed.
@ -1817,7 +1817,7 @@ If ``context`` is provided, it will be used when updating default values.
.. seealso:: :py:func:`updateFeature`
%End
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsExpressionContext *context = 0 );
bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = 0 );
%Docstring
Changes attributes' values for a feature (but does not immediately
commit the changes).
@ -1837,7 +1837,7 @@ If ``skipDefaultValues`` is set to ``True``, default field values will not
be updated. This can be used to override default field value
expressions.
If ``context`` is provided, it will be used when updating default values.
If ``context`` is provided, it will be used when updating default values (since QGIS 3.38).
:return: ``True`` if feature's attributes was successfully changed.

View File

@ -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, QgsExpressionContextScope *scope = 0 ) 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 ) const;
%Docstring
This method should/will be called, whenever a new feature will be added to the layer
@ -38,10 +38,32 @@ 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
.. note::
addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools
%End
virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature /Out/ = 0, const QgsVectorLayerToolsContext &context = QgsVectorLayerToolsContext() ) const;
%Docstring
This method should/will be called, whenever a new feature will be added to the layer
:param layer: The layer to which the feature should be added
:param defaultValues: Default values for the feature to add
:param defaultGeometry: A default geometry to add to the feature
:param context: A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
:return: - ``True`` in case of success, ``False`` if the operation failed/was aborted
- feature: Updated feature after adding will be written back to this
.. note::
addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools
.. versionadded:: 3.38
%End

View File

@ -0,0 +1,115 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/vector/qgsvectorlayertoolscontext.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsVectorLayerToolsContext
{
%Docstring(signature="appended")
Contains settings which reflect the context in which vector layer tool operations should
consider.
.. versionadded:: 3.38
%End
%TypeHeaderCode
#include "qgsvectorlayertoolscontext.h"
%End
public:
QgsVectorLayerToolsContext();
%Docstring
Constructor for QgsVectorLayerToolsContext.
%End
QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other );
%Docstring
Copy constructor.
:param other: source QgsVectorLayerToolsContext
%End
void setExpressionContext( const QgsExpressionContext *context );
%Docstring
Sets the optional expression context used by the vector layer tools.
:param context: expression context pointer. Ownership is not transferred.
.. seealso:: :py:func:`expressionContext`
.. seealso:: :py:func:`setAdditionalExpressionContextScope`
%End
QgsExpressionContext *expressionContext() const;
%Docstring
Returns the optional expression context used by the vector layer tools.
.. seealso:: :py:func:`setExpressionContext`
.. seealso:: :py:func:`additionalExpressionContextScope`
%End
void setAdditionalExpressionContextScope( const QgsExpressionContextScope *scope );
%Docstring
Sets an additional expression context scope to be made available when calculating expressions.
:param scope: additional scope. Ownership is not transferred and a copy will be made.
.. seealso:: :py:func:`additionalExpressionContextScope`
%End
const QgsExpressionContextScope *additionalExpressionContextScope() const;
%Docstring
Returns an additional expression context scope to be made available when calculating expressions.
.. seealso:: :py:func:`setAdditionalExpressionContextScope`
%End
QWidget *parentWidget() const;
%Docstring
Returns the widget which should be parented to tools dialogues.
%End
void setParentWidget( QWidget *parent );
%Docstring
Sets the widget which should be parented to tools' dialogues.
:param parent: the widget actign as parent
%End
bool showModal() const;
%Docstring
Returns whether tools' dialogues should be modal.
%End
void setShowModal( bool modal );
%Docstring
Sets whether tools' dialogues should be modal.
%End
bool hideParent() const;
%Docstring
Returns whether the parent widget should be hidden when showing tools' dialogues.
%End
void setHideParent( bool hide );
%Docstring
Sets whether the parent widget should be hidden when showing tools' dialogues.
%End
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/vector/qgsvectorlayertoolscontext.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -771,6 +771,7 @@
%Include auto_generated/vector/qgsvectorlayerselectionproperties.sip
%Include auto_generated/vector/qgsvectorlayertemporalproperties.sip
%Include auto_generated/vector/qgsvectorlayertools.sip
%Include auto_generated/vector/qgsvectorlayertoolscontext.sip
%Include auto_generated/vector/qgsvectorlayerundocommand.sip
%Include auto_generated/vector/qgsvectorlayerundopassthroughcommand.sip
%Include auto_generated/vector/qgsvectorlayerutils.sip

View File

@ -26,19 +26,20 @@
#include "qgsmessageviewer.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayerutils.h"
#include "qgsvectorlayertoolscontext.h"
bool QgsGuiVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feat, QWidget *parentWidget, bool showModal, bool hideParent, QgsExpressionContextScope *scope ) const
bool QgsGuiVectorLayerTools::addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, const QgsVectorLayerToolsContext &context ) const
{
QgsFeature *f = feat;
if ( !feat )
QgsFeature *f = feature;
if ( !feature )
f = new QgsFeature();
f->setGeometry( defaultGeometry );
QgsFeatureAction *a = new QgsFeatureAction( tr( "Add feature" ), *f, layer, QUuid(), -1, parentWidget );
QgsFeatureAction *a = new QgsFeatureAction( tr( "Add feature" ), *f, layer, QUuid(), -1, context.parentWidget() );
a->setForceSuppressFormPopup( forceSuppressFormPopup() );
connect( a, &QgsFeatureAction::addFeatureFinished, a, &QObject::deleteLater );
const QgsFeatureAction::AddFeatureResult result = a->addFeature( defaultValues, showModal, std::unique_ptr<QgsExpressionContextScope>( scope ), hideParent );
if ( !feat )
const QgsFeatureAction::AddFeatureResult result = a->addFeature( defaultValues, context.showModal(), std::unique_ptr<QgsExpressionContextScope>( context.additionalExpressionContextScope() ? new QgsExpressionContextScope( *context.additionalExpressionContextScope() ) : nullptr ), context.hideParent() );
if ( !feature )
delete f;
switch ( result )

View File

@ -40,15 +40,12 @@ class QgsGuiVectorLayerTools : public QgsVectorLayerTools
* \param layer The layer to which the feature should be added
* \param defaultValues Default values for the feature to add
* \param defaultGeometry A default geometry to add to the feature
* \param feat A pointer to the feature
* \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
* \param feature A pointer to the feature
* \param context A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
*
* \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, QgsExpressionContextScope *scope = nullptr ) const override;
bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, const QgsVectorLayerToolsContext &context ) const override;
/**
* This should be called, whenever a vector layer should be switched to edit mode. If successful

View File

@ -955,6 +955,7 @@ set(QGIS_CORE_SRCS
vector/qgsvectorlayerselectionproperties.cpp
vector/qgsvectorlayertemporalproperties.cpp
vector/qgsvectorlayertools.cpp
vector/qgsvectorlayertoolscontext.cpp
vector/qgsvectorlayerundocommand.cpp
vector/qgsvectorlayerundopassthroughcommand.cpp
vector/qgsvectorlayerutils.cpp
@ -2029,6 +2030,7 @@ set(QGIS_CORE_HDRS
vector/qgsvectorlayerselectionproperties.h
vector/qgsvectorlayertemporalproperties.h
vector/qgsvectorlayertools.h
vector/qgsvectorlayertoolscontext.h
vector/qgsvectorlayerundocommand.h
vector/qgsvectorlayerundopassthroughcommand.h
vector/qgsvectorlayerutils.h

View File

@ -13,18 +13,20 @@
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgstrackedvectorlayertools.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayertoolscontext.h"
bool QgsTrackedVectorLayerTools::addFeature( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, QWidget *parentWidget, bool showModal, bool hideParent, QgsExpressionContextScope *scope ) const
bool QgsTrackedVectorLayerTools::addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature, const QgsVectorLayerToolsContext &context ) const
{
QgsFeature *f = feature;
if ( !feature )
f = new QgsFeature();
const_cast<QgsVectorLayerTools *>( mBackend )->setForceSuppressFormPopup( forceSuppressFormPopup() );
if ( mBackend->addFeature( layer, defaultValues, defaultGeometry, f, parentWidget, showModal, hideParent, scope ) )
if ( mBackend->addFeatureV2( layer, defaultValues, defaultGeometry, f, context ) )
{
mAddedFeatures[layer].insert( f->id() );
if ( !feature )

View File

@ -41,14 +41,11 @@ class CORE_EXPORT QgsTrackedVectorLayerTools : public QgsVectorLayerTools
* \param defaultValues Default values for the feature to add
* \param defaultGeometry A default geometry to add to the feature
* \param feature A pointer to the feature
* \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
* \param context A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
*
* \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, QgsExpressionContextScope *scope = nullptr ) const override;
bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues, const QgsGeometry &defaultGeometry, QgsFeature *feature SIP_OUT, const QgsVectorLayerToolsContext &context ) const override;
bool startEditing( QgsVectorLayer *layer ) const override;
bool stopEditing( QgsVectorLayer *layer, bool allowCancel ) const override;
bool saveEdits( QgsVectorLayer *layer ) const override;

View File

@ -3398,7 +3398,7 @@ bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, QgsGeometry &geom, bool s
}
bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue, bool skipDefaultValues, QgsExpressionContext *context )
bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue, bool skipDefaultValues, QgsVectorLayerToolsContext *context )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
@ -3426,12 +3426,12 @@ bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QV
}
if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() )
updateDefaultValues( fid, QgsFeature(), context );
updateDefaultValues( fid, QgsFeature(), context ? context->expressionContext() : nullptr );
return result;
}
bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues, QgsExpressionContext *context )
bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues, QgsVectorLayerToolsContext *context )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
@ -3488,7 +3488,7 @@ bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttribute
if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() )
{
updateDefaultValues( fid, QgsFeature(), context );
updateDefaultValues( fid, QgsFeature(), context ? context->expressionContext() : nullptr );
}
return result;

View File

@ -33,6 +33,7 @@
#include "qgsfeaturesource.h"
#include "qgsfields.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayertoolscontext.h"
#include "qgsvectorsimplifymethod.h"
#include "qgseditformconfig.h"
#include "qgsattributetableconfig.h"
@ -1735,7 +1736,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* If \a skipDefaultValues is set to TRUE, default field values will not
* be updated. This can be used to override default field value expressions.
*
* If \a context is provided, it will be used when updating default values.
* If \a context is provided, it will be used when updating default values (since QGIS 3.38).
*
* \returns TRUE if the feature's attribute was successfully changed.
*
@ -1749,7 +1750,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* \see changeGeometry()
* \see updateFeature()
*/
Q_INVOKABLE bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsExpressionContext *context = nullptr );
Q_INVOKABLE bool changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue = QVariant(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = nullptr );
/**
* Changes attributes' values for a feature (but does not immediately
@ -1770,7 +1771,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* be updated. This can be used to override default field value
* expressions.
*
* If \a context is provided, it will be used when updating default values.
* If \a context is provided, it will be used when updating default values (since QGIS 3.38).
*
* \returns TRUE if feature's attributes was successfully changed.
*
@ -1787,7 +1788,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* \see changeAttributeValue()
*
*/
Q_INVOKABLE bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsExpressionContext *context = nullptr );
Q_INVOKABLE bool changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues = QgsAttributeMap(), bool skipDefaultValues = false, QgsVectorLayerToolsContext *context = nullptr );
/**
* Add an attribute field (but does not commit it)

View File

@ -23,6 +23,7 @@
#include "qgsexpressioncontext.h"
#include "qgsfeature.h"
#include "qgsgeometry.h"
#include "qgsvectorlayertoolscontext.h"
class QgsFeatureRequest;
class QgsVectorLayer;
@ -57,11 +58,37 @@ 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
* \returns TRUE in case of success, FALSE if the operation failed/was aborted
*
* \note addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools
*/
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;
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
{
QgsVectorLayerToolsContext context;
context.setParentWidget( parentWidget );
context.setShowModal( showModal );
context.setHideParent( hideParent );
return addFeatureV2( layer, defaultValues, defaultGeometry, feature, context );
}
/**
* This method should/will be called, whenever a new feature will be added to the layer
*
* \param layer The layer to which the feature should be added
* \param defaultValues Default values for the feature to add
* \param defaultGeometry A default geometry to add to the feature
* \param feature Updated feature after adding will be written back to this
* \param context A context object to be used for e.g. to calculate feature expression-based values (since QGIS 3.38)
* \returns TRUE in case of success, FALSE if the operation failed/was aborted
*
* \note addFeature or addFeatureV2 must be overwritten when implementing a class inheriting from QgsVectorLayerTools
* \since QGIS 3.38
*/
virtual bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &defaultValues = QgsAttributeMap(), const QgsGeometry &defaultGeometry = QgsGeometry(), QgsFeature *feature SIP_OUT = nullptr, const QgsVectorLayerToolsContext &context = QgsVectorLayerToolsContext() ) const
{
Q_UNUSED( context )
return addFeature( layer, defaultValues, defaultGeometry, feature, context.parentWidget(), context.showModal(), context.hideParent() );
}
// TODO QGIS 4: remove const qualifier

View File

@ -0,0 +1,78 @@
/***************************************************************************
qgsvectorlayertoolscontext.cpp
------------------------
begin : May 2024
copyright : (C) 2024 by Mathieu Pellerin
email : mathieu at opengis dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsvectorlayertoolscontext.h"
#include "qgsexpressioncontextutils.h"
QgsVectorLayerToolsContext::QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other )
: mParentWidget( other.mParentWidget )
, mShowModal( other.mShowModal )
, mHideParent( other.mHideParent )
{
if ( other.mAdditionalExpressionContextScope )
{
mAdditionalExpressionContextScope.reset( new QgsExpressionContextScope( *other.mAdditionalExpressionContextScope ) );
}
if ( other.mExpressionContext )
{
mExpressionContext.reset( new QgsExpressionContext( *other.mExpressionContext ) );
}
}
QgsVectorLayerToolsContext &QgsVectorLayerToolsContext::operator=( const QgsVectorLayerToolsContext &other )
{
mParentWidget = other.mParentWidget;
mShowModal = other.mShowModal;
mHideParent = other.mHideParent;
if ( other.mAdditionalExpressionContextScope )
{
mAdditionalExpressionContextScope.reset( new QgsExpressionContextScope( *other.mAdditionalExpressionContextScope ) );
}
if ( other.mExpressionContext )
{
mExpressionContext.reset( new QgsExpressionContext( *other.mExpressionContext ) );
}
else
{
mExpressionContext.reset();
}
return *this;
}
void QgsVectorLayerToolsContext::setExpressionContext( const QgsExpressionContext *context )
{
if ( context )
mExpressionContext.reset( new QgsExpressionContext( *context ) );
else
mExpressionContext.reset();
}
QgsExpressionContext *QgsVectorLayerToolsContext::expressionContext() const
{
return mExpressionContext.get();
}
void QgsVectorLayerToolsContext::setAdditionalExpressionContextScope( const QgsExpressionContextScope *scope )
{
if ( scope )
mAdditionalExpressionContextScope.reset( new QgsExpressionContextScope( *scope ) );
else
mAdditionalExpressionContextScope.reset();
}
const QgsExpressionContextScope *QgsVectorLayerToolsContext::additionalExpressionContextScope() const
{
return mAdditionalExpressionContextScope.get();
}

View File

@ -0,0 +1,117 @@
/***************************************************************************
qgsvectorlayertoolscontext.h
------------------------
begin : May 2024
copyright : (C) 2024 by Mathieu Pellerin
email : mathieu at opengis dot ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSVECTORLAYERTOOLSCONTEXT_H
#define QGSVECTORLAYERTOOLSCONTEXT_H
#include "qgsexpressioncontext.h"
#include "qgis_core.h"
#include <memory>
/**
* \ingroup core
* \class QgsVectorLayerToolsContext
* \brief Contains settings which reflect the context in which vector layer tool operations should
* consider.
* \since QGIS 3.38
*/
class CORE_EXPORT QgsVectorLayerToolsContext
{
public:
/**
* Constructor for QgsVectorLayerToolsContext.
*/
QgsVectorLayerToolsContext() = default;
/**
* Copy constructor.
* \param other source QgsVectorLayerToolsContext
*/
QgsVectorLayerToolsContext( const QgsVectorLayerToolsContext &other );
QgsVectorLayerToolsContext &operator=( const QgsVectorLayerToolsContext &other );
/**
* Sets the optional expression context used by the vector layer tools.
* \param context expression context pointer. Ownership is not transferred.
* \see expressionContext()
* \see setAdditionalExpressionContextScope()
*/
void setExpressionContext( const QgsExpressionContext *context );
/**
* Returns the optional expression context used by the vector layer tools.
* \see setExpressionContext()
* \see additionalExpressionContextScope()
*/
QgsExpressionContext *expressionContext() const;
/**
* Sets an additional expression context scope to be made available when calculating expressions.
* \param scope additional scope. Ownership is not transferred and a copy will be made.
* \see additionalExpressionContextScope()
*/
void setAdditionalExpressionContextScope( const QgsExpressionContextScope *scope );
/**
* Returns an additional expression context scope to be made available when calculating expressions.
* \see setAdditionalExpressionContextScope()
*/
const QgsExpressionContextScope *additionalExpressionContextScope() const;
/**
* Returns the widget which should be parented to tools dialogues.
*/
QWidget *parentWidget() const { return mParentWidget; }
/**
* Sets the widget which should be parented to tools' dialogues.
* \param parent the widget actign as parent
*/
void setParentWidget( QWidget *parent ) { mParentWidget = parent; }
/**
* Returns whether tools' dialogues should be modal.
*/
bool showModal() const { return mShowModal; }
/**
* Sets whether tools' dialogues should be modal.
*/
void setShowModal( bool modal ) { mShowModal = modal; }
/**
* Returns whether the parent widget should be hidden when showing tools' dialogues.
*/
bool hideParent() const { return mHideParent; }
/**
* Sets whether the parent widget should be hidden when showing tools' dialogues.
*/
void setHideParent( bool hide ) { mHideParent = hide; }
private:
std::unique_ptr< QgsExpressionContext > mExpressionContext;
std::unique_ptr< QgsExpressionContextScope > mAdditionalExpressionContextScope;
QWidget *mParentWidget = nullptr;
bool mShowModal = true;
bool mHideParent = false;
};
#endif // QGSVECTORLAYERTOOLSCONTEXT_H

View File

@ -287,9 +287,14 @@ 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() );
QgsVectorLayerToolsContext context;
context.setParentWidget( this );
context.setShowModal( true );
context.setHideParent( true );
std::unique_ptr<QgsExpressionContextScope> scope( QgsExpressionContextUtils::parentFormScope( mFeatureList.first(), mEditorContext.attributeFormModeString() ) );
context.setAdditionalExpressionContextScope( scope.get() );
QgsFeature linkFeature;
if ( !vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature, this, true, true, scope ) )
if ( !vlTools->addFeatureV2( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature, context ) )
return QgsFeatureIds();
addedFeatureIds.insert( linkFeature.id() );

View File

@ -44,6 +44,7 @@
#include "qgstabwidget.h"
#include "qgsscrollarea.h"
#include "qgsvectorlayerjoinbuffer.h"
#include "qgsvectorlayertoolscontext.h"
#include "qgsvectorlayerutils.h"
#include "qgsactionwidgetwrapper.h"
#include "qgsqmlwidgetwrapper.h"
@ -463,8 +464,10 @@ bool QgsAttributeForm::saveEdits( QString *error )
n++;
}
QgsExpressionContext context = createExpressionContext( updatedFeature );
success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues, false, &context );
std::unique_ptr<QgsVectorLayerToolsContext> context = std::make_unique<QgsVectorLayerToolsContext>();
QgsExpressionContext expressionContext = createExpressionContext( updatedFeature );
context->setExpressionContext( &expressionContext );
success = mLayer->changeAttributeValues( mFeature.id(), newValues, oldValues, false, context.get() );
if ( success && n > 0 )
{

View File

@ -31,6 +31,7 @@
#include "qgsgui.h"
#include "qgsmapcanvas.h"
#include "qgsvectorlayertools.h"
#include "qgsvectorlayertoolscontext.h"
#include "qgsadvanceddigitizingdockwidget.h"
#include "qgsmaptooldigitizefeature.h"
@ -609,12 +610,9 @@ 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, QgsExpressionContextScope *scope = nullptr ) const override
bool addFeatureV2( QgsVectorLayer *layer, const QgsAttributeMap &, const QgsGeometry &, QgsFeature *feat, const QgsVectorLayerToolsContext &context ) const override
{
Q_UNUSED( parentWidget );
Q_UNUSED( showModal );
Q_UNUSED( hideParent );
Q_UNUSED( scope );
Q_UNUSED( context );
feat->setAttribute( QStringLiteral( "pk" ), 13 );
feat->setAttribute( QStringLiteral( "material" ), QStringLiteral( "steel" ) );
feat->setAttribute( QStringLiteral( "diameter" ), 140 );

View File

@ -341,7 +341,7 @@ class TestQgsRelationEditWidget(QgisTestCase):
# Mock vector layer tool to just set default value on created feature
class DummyVlTools(QgsVectorLayerTools):
def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False, scope=None):
def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False):
f = QgsFeature(layer.fields())
for idx, value in defaultValues.items():
f.setAttribute(idx, value)
@ -440,7 +440,7 @@ class VlTools(QgsVectorLayerTools):
"""
self.values = values
def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False, scope=None):
def addFeature(self, layer, defaultValues, defaultGeometry, parentWidget=None, showModal=True, hideParent=False):
"""
Overrides the addFeature method
:param layer: vector layer

View File

@ -78,6 +78,7 @@ from qgis.core import (
QgsVectorLayerJoinInfo,
QgsVectorLayerSelectedFeatureSource,
QgsVectorLayerSimpleLabeling,
QgsVectorLayerToolsContext,
QgsWkbTypes,
)
from qgis.gui import QgsAttributeTableModel, QgsGui
@ -1162,8 +1163,10 @@ class TestQgsVectorLayer(QgisTestCase, FeatureSourceTestCase):
layer.startEditing()
context = layer.createExpressionContext()
context.appendScope(QgsExpressionContextUtils.parentFormScope(pf))
expressionContext = layer.createExpressionContext()
expressionContext.appendScope(QgsExpressionContextUtils.parentFormScope(pf))
context = QgsVectorLayerToolsContext()
context.setExpressionContext(expressionContext)
self.assertTrue(layer.changeAttributeValues(fid, {1: 100}, {}, False, context))
f = layer.getFeature(1)