Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate policy on QgsFields when duplicate features #57217

Merged
merged 6 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions python/PyQt6/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -3204,6 +3204,13 @@
# --
Qgis.FieldDomainMergePolicy.baseClass = Qgis
# monkey patching scoped based enum
Qgis.FieldDomainDuplicatePolicy.DefaultValue.__doc__ = "Use default field value"
Qgis.FieldDomainDuplicatePolicy.Duplicate.__doc__ = "Duplicate original value"
Qgis.FieldDomainDuplicatePolicy.UnsetField.__doc__ = "Clears the field value so that the data provider backend will populate using any backend triggers or similar logic (since QGIS 3.30)"
Qgis.FieldDomainDuplicatePolicy.__doc__ = "Duplicate policy for field domains.\n\nWhen a feature is duplicated, defines how the value of attributes\nfollowing the domain are computed.\n\n.. versionadded:: 3.38\n\n" + '* ``DefaultValue``: ' + Qgis.FieldDomainDuplicatePolicy.DefaultValue.__doc__ + '\n' + '* ``Duplicate``: ' + Qgis.FieldDomainDuplicatePolicy.Duplicate.__doc__ + '\n' + '* ``UnsetField``: ' + Qgis.FieldDomainDuplicatePolicy.UnsetField.__doc__
# --
Qgis.FieldDomainDuplicatePolicy.baseClass = Qgis
# monkey patching scoped based enum
Qgis.FieldDomainType.Coded.__doc__ = "Coded field domain"
Qgis.FieldDomainType.Range.__doc__ = "Numeric range field domain (min/max)"
Qgis.FieldDomainType.Glob.__doc__ = "Glob string pattern field domain"
Expand Down
7 changes: 7 additions & 0 deletions python/PyQt6/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,13 @@ The development version
GeometryWeighted,
};

enum class FieldDomainDuplicatePolicy /BaseType=IntEnum/
{
DefaultValue,
Duplicate,
UnsetField,
};

enum class FieldDomainType /BaseType=IntEnum/
{
Coded,
Expand Down
20 changes: 20 additions & 0 deletions python/PyQt6/core/auto_generated/qgsfield.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,26 @@ be handled during a split operation.
.. seealso:: :py:func:`splitPolicy`

.. versionadded:: 3.30
%End

Qgis::FieldDomainDuplicatePolicy duplicatePolicy() const;
%Docstring
Returns the field's duplicate policy, which indicates how field values should
be handled during a duplicate operation.

.. seealso:: :py:func:`setDuplicatePolicy`

.. versionadded:: 3.38
%End

void setDuplicatePolicy( Qgis::FieldDomainDuplicatePolicy policy );
%Docstring
Sets the field's duplicate ``policy``, which indicates how field values should
be handled during a duplicate operation.

.. seealso:: :py:func:`duplicatePolicy`

.. versionadded:: 3.38
%End

SIP_PYOBJECT __repr__();
Expand Down
21 changes: 21 additions & 0 deletions python/PyQt6/core/auto_generated/vector/qgsvectorlayer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,27 @@ Sets a split ``policy`` for the field with the specified index.
}
%End

void setFieldDuplicatePolicy( int index, Qgis::FieldDomainDuplicatePolicy policy );
%Docstring
Sets a duplicate ``policy`` for the field with the specified index.

:raises KeyError: if no field with the specified index exists

.. versionadded:: 3.38
%End

%MethodCode
if ( a0 < 0 || a0 >= sipCpp->fields().count() )
{
PyErr_SetString( PyExc_KeyError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
sipCpp->setFieldDuplicatePolicy( a0, a1 );
}
%End

QSet<QString> excludeAttributesWms() const /Deprecated/;
%Docstring
A set of attributes that are not advertised in WMS requests with QGIS server.
Expand Down
7 changes: 7 additions & 0 deletions python/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -3149,6 +3149,13 @@
# --
Qgis.FieldDomainMergePolicy.baseClass = Qgis
# monkey patching scoped based enum
Qgis.FieldDomainDuplicatePolicy.DefaultValue.__doc__ = "Use default field value"
Qgis.FieldDomainDuplicatePolicy.Duplicate.__doc__ = "Duplicate original value"
Qgis.FieldDomainDuplicatePolicy.UnsetField.__doc__ = "Clears the field value so that the data provider backend will populate using any backend triggers or similar logic (since QGIS 3.30)"
Qgis.FieldDomainDuplicatePolicy.__doc__ = "Duplicate policy for field domains.\n\nWhen a feature is duplicated, defines how the value of attributes\nfollowing the domain are computed.\n\n.. versionadded:: 3.38\n\n" + '* ``DefaultValue``: ' + Qgis.FieldDomainDuplicatePolicy.DefaultValue.__doc__ + '\n' + '* ``Duplicate``: ' + Qgis.FieldDomainDuplicatePolicy.Duplicate.__doc__ + '\n' + '* ``UnsetField``: ' + Qgis.FieldDomainDuplicatePolicy.UnsetField.__doc__
# --
Qgis.FieldDomainDuplicatePolicy.baseClass = Qgis
# monkey patching scoped based enum
Qgis.FieldDomainType.Coded.__doc__ = "Coded field domain"
Qgis.FieldDomainType.Range.__doc__ = "Numeric range field domain (min/max)"
Qgis.FieldDomainType.Glob.__doc__ = "Glob string pattern field domain"
Expand Down
7 changes: 7 additions & 0 deletions python/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,13 @@ The development version
GeometryWeighted,
};

enum class FieldDomainDuplicatePolicy
{
DefaultValue,
Duplicate,
UnsetField,
};

enum class FieldDomainType
{
Coded,
Expand Down
20 changes: 20 additions & 0 deletions python/core/auto_generated/qgsfield.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,26 @@ be handled during a split operation.
.. seealso:: :py:func:`splitPolicy`

.. versionadded:: 3.30
%End

Qgis::FieldDomainDuplicatePolicy duplicatePolicy() const;
%Docstring
Returns the field's duplicate policy, which indicates how field values should
be handled during a duplicate operation.

.. seealso:: :py:func:`setDuplicatePolicy`

.. versionadded:: 3.38
%End

void setDuplicatePolicy( Qgis::FieldDomainDuplicatePolicy policy );
%Docstring
Sets the field's duplicate ``policy``, which indicates how field values should
be handled during a duplicate operation.

.. seealso:: :py:func:`duplicatePolicy`

.. versionadded:: 3.38
%End

SIP_PYOBJECT __repr__();
Expand Down
21 changes: 21 additions & 0 deletions python/core/auto_generated/vector/qgsvectorlayer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,27 @@ Sets a split ``policy`` for the field with the specified index.
}
%End

void setFieldDuplicatePolicy( int index, Qgis::FieldDomainDuplicatePolicy policy );
%Docstring
Sets a duplicate ``policy`` for the field with the specified index.

:raises KeyError: if no field with the specified index exists

.. versionadded:: 3.38
%End

%MethodCode
if ( a0 < 0 || a0 >= sipCpp->fields().count() )
{
PyErr_SetString( PyExc_KeyError, QByteArray::number( a0 ) );
sipIsErr = 1;
}
else
{
sipCpp->setFieldDuplicatePolicy( a0, a1 );
}
%End

QSet<QString> excludeAttributesWms() const /Deprecated/;
%Docstring
A set of attributes that are not advertised in WMS requests with QGIS server.
Expand Down
16 changes: 16 additions & 0 deletions src/core/qgis.h
Original file line number Diff line number Diff line change
Expand Up @@ -3203,6 +3203,22 @@ class CORE_EXPORT Qgis
};
Q_ENUM( FieldDomainMergePolicy )

/**
* Duplicate policy for field domains.
*
* When a feature is duplicated, defines how the value of attributes
* following the domain are computed.
*
* \since QGIS 3.38
*/
enum class FieldDomainDuplicatePolicy : int
{
DefaultValue, //!< Use default field value
Duplicate, //!< Duplicate original value
UnsetField, //!< Clears the field value so that the data provider backend will populate using any backend triggers or similar logic (since QGIS 3.30)
};
Q_ENUM( FieldDomainDuplicatePolicy )

/**
* Types of field domain
*
Expand Down
19 changes: 18 additions & 1 deletion src/core/qgsfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,11 @@ bool QgsField::convertCompatible( QVariant &v, QString *errorMessage ) const
return true;
}

QgsField::operator QVariant() const
{
return QVariant::fromValue( *this );
}

void QgsField::setEditorWidgetSetup( const QgsEditorWidgetSetup &v )
{
d->editorWidgetSetup = v;
Expand Down Expand Up @@ -736,6 +741,16 @@ void QgsField::setSplitPolicy( Qgis::FieldDomainSplitPolicy policy )
d->splitPolicy = policy;
}

Qgis::FieldDomainDuplicatePolicy QgsField::duplicatePolicy() const
{
return d->duplicatePolicy;
}

void QgsField::setDuplicatePolicy( Qgis::FieldDomainDuplicatePolicy policy )
{
d->duplicatePolicy = policy;
}

/***************************************************************************
* This class is considered CRITICAL and any change MUST be accompanied with
* full unit tests in testqgsfield.cpp.
Expand Down Expand Up @@ -782,6 +797,7 @@ QDataStream &operator>>( QDataStream &in, QgsField &field )
quint32 strengthUnique;
quint32 strengthExpression;
int splitPolicy;
int duplicatePolicy;

bool applyOnUpdate;

Expand All @@ -796,7 +812,7 @@ QDataStream &operator>>( QDataStream &in, QgsField &field )

in >> name >> type >> typeName >> length >> precision >> comment >> alias
>> defaultValueExpression >> applyOnUpdate >> constraints >> originNotNull >> originUnique >> originExpression >> strengthNotNull >> strengthUnique >> strengthExpression >>
constraintExpression >> constraintDescription >> subType >> splitPolicy >> metadata;
constraintExpression >> constraintDescription >> subType >> splitPolicy >> duplicatePolicy >> metadata;
field.setName( name );
field.setType( static_cast< QVariant::Type >( type ) );
field.setTypeName( typeName );
Expand All @@ -806,6 +822,7 @@ QDataStream &operator>>( QDataStream &in, QgsField &field )
field.setAlias( alias );
field.setDefaultValueDefinition( QgsDefaultValue( defaultValueExpression, applyOnUpdate ) );
field.setSplitPolicy( static_cast< Qgis::FieldDomainSplitPolicy >( splitPolicy ) );
field.setDuplicatePolicy( static_cast< Qgis::FieldDomainDuplicatePolicy >( duplicatePolicy ) );
QgsFieldConstraints fieldConstraints;
if ( constraints & QgsFieldConstraints::ConstraintNotNull )
{
Expand Down
25 changes: 21 additions & 4 deletions src/core/qgsfield.h
Original file line number Diff line number Diff line change
Expand Up @@ -441,10 +441,7 @@ class CORE_EXPORT QgsField
#endif

//! Allows direct construction of QVariants from fields.
operator QVariant() const
{
return QVariant::fromValue( *this );
}
operator QVariant() const;

/**
* Set the editor widget setup for the field.
Expand Down Expand Up @@ -497,6 +494,26 @@ class CORE_EXPORT QgsField
*/
void setSplitPolicy( Qgis::FieldDomainSplitPolicy policy );

/**
* Returns the field's duplicate policy, which indicates how field values should
* be handled during a duplicate operation.
*
* \see setDuplicatePolicy()
*
* \since QGIS 3.38
*/
Qgis::FieldDomainDuplicatePolicy duplicatePolicy() const;

/**
* Sets the field's duplicate \a policy, which indicates how field values should
* be handled during a duplicate operation.
*
* \see duplicatePolicy()
*
* \since QGIS 3.38
*/
void setDuplicatePolicy( Qgis::FieldDomainDuplicatePolicy policy );

#ifdef SIP_RUN
SIP_PYOBJECT __repr__();
% MethodCode
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsfield_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class QgsFieldPrivate : public QSharedData
, constraints( other.constraints )
, editorWidgetSetup( other.editorWidgetSetup )
, splitPolicy( other.splitPolicy )
, duplicatePolicy( other.duplicatePolicy )
, isReadOnly( other.isReadOnly )
{
}
Expand All @@ -99,6 +100,7 @@ class QgsFieldPrivate : public QSharedData
&& ( alias == other.alias ) && ( defaultValueDefinition == other.defaultValueDefinition )
&& ( constraints == other.constraints ) && ( flags == other.flags )
&& ( splitPolicy == other.splitPolicy )
&& ( duplicatePolicy == other.duplicatePolicy )
&& ( isReadOnly == other.isReadOnly )
&& ( editorWidgetSetup == other.editorWidgetSetup ) );
}
Expand Down Expand Up @@ -144,6 +146,9 @@ class QgsFieldPrivate : public QSharedData
//! Split policy
Qgis::FieldDomainSplitPolicy splitPolicy = Qgis::FieldDomainSplitPolicy::Duplicate;

//! Duplicate policy
Qgis::FieldDomainDuplicatePolicy duplicatePolicy = Qgis::FieldDomainDuplicatePolicy::Duplicate;

//! Read-only
bool isReadOnly = false;

Expand Down
57 changes: 57 additions & 0 deletions src/core/vector/qgsvectorlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2243,6 +2243,10 @@ bool QgsVectorLayer::setDataProvider( QString const &provider, const QgsDataProv
{
mAttributeSplitPolicy[ field.name() ] = field.splitPolicy();
}
if ( !mAttributeDuplicatePolicy.contains( field.name() ) )
{
mAttributeDuplicatePolicy[ field.name() ] = field.duplicatePolicy();
}
}

if ( profile )
Expand Down Expand Up @@ -2573,6 +2577,21 @@ bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMes
}
}

// The duplicate policy is - unlike alias and split policy - never defined by the data provider, so we clear the map
mAttributeDuplicatePolicy.clear();
const QDomNode duplicatePoliciesNode = layerNode.namedItem( QStringLiteral( "duplicatePolicies" ) );
if ( !duplicatePoliciesNode.isNull() )
{
const QDomNodeList duplicatePolicyNodeList = duplicatePoliciesNode.toElement().elementsByTagName( QStringLiteral( "policy" ) );
for ( int i = 0; i < duplicatePolicyNodeList.size(); ++i )
{
const QDomElement duplicatePolicyElem = duplicatePolicyNodeList.at( i ).toElement();
const QString field = duplicatePolicyElem.attribute( QStringLiteral( "field" ) );
const Qgis::FieldDomainDuplicatePolicy policy = qgsEnumKeyToValue( duplicatePolicyElem.attribute( QStringLiteral( "policy" ) ), Qgis::FieldDomainDuplicatePolicy::Duplicate );
mAttributeDuplicatePolicy.insert( field, policy );
}
}

// default expressions
mDefaultExpressionMap.clear();
QDomNode defaultsNode = layerNode.namedItem( QStringLiteral( "defaults" ) );
Expand Down Expand Up @@ -3085,6 +3104,19 @@ bool QgsVectorLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString
node.appendChild( splitPoliciesElement );
}

//duplicate policies
{
QDomElement duplicatePoliciesElement = doc.createElement( QStringLiteral( "duplicatePolicies" ) );
for ( const QgsField &field : std::as_const( mFields ) )
{
QDomElement duplicatePolicyElem = doc.createElement( QStringLiteral( "policy" ) );
duplicatePolicyElem.setAttribute( QStringLiteral( "field" ), field.name() );
duplicatePolicyElem.setAttribute( QStringLiteral( "policy" ), qgsEnumValueToKey( field.duplicatePolicy() ) );
duplicatePoliciesElement.appendChild( duplicatePolicyElem );
}
node.appendChild( duplicatePoliciesElement );
}

//default expressions
QDomElement defaultsElem = doc.createElement( QStringLiteral( "defaults" ) );
for ( const QgsField &field : std::as_const( mFields ) )
Expand Down Expand Up @@ -3588,6 +3620,22 @@ void QgsVectorLayer::setFieldSplitPolicy( int index, Qgis::FieldDomainSplitPolic
emit layerModified(); // TODO[MD]: should have a different signal?
}

void QgsVectorLayer::setFieldDuplicatePolicy( int index, Qgis::FieldDomainDuplicatePolicy policy )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

if ( index < 0 || index >= fields().count() )
return;

const QString name = fields().at( index ).name();

mAttributeDuplicatePolicy.insert( name, policy );
mFields[ index ].setDuplicatePolicy( policy );
mEditFormConfig.setFields( mFields );
emit layerModified(); // TODO[MD]: should have a different signal?
}


QSet<QString> QgsVectorLayer::excludeAttributesWms() const
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Expand Down Expand Up @@ -4463,6 +4511,15 @@ void QgsVectorLayer::updateFields()
mFields[ index ].setSplitPolicy( splitPolicyIt.value() );
}

for ( auto duplicatePolicyIt = mAttributeDuplicatePolicy.constBegin(); duplicatePolicyIt != mAttributeDuplicatePolicy.constEnd(); ++duplicatePolicyIt )
{
int index = mFields.lookupField( duplicatePolicyIt.key() );
if ( index < 0 )
continue;

mFields[ index ].setDuplicatePolicy( duplicatePolicyIt.value() );
}

// Update configuration flags
QMap< QString, Qgis::FieldConfigurationFlags >::const_iterator flagsIt = mFieldConfigurationFlags.constBegin();
for ( ; flagsIt != mFieldConfigurationFlags.constEnd(); ++flagsIt )
Expand Down
Loading
Loading