Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
77 changes: 52 additions & 25 deletions src/gui/qfattributeformmodelbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ bool QfAttributeFormModelBase::setData( const QModelIndex &index, const QVariant
mExpressionContext << QgsExpressionContextUtils::formScope( mFeatureModel->feature() );
synchronizeFieldValue( fieldIndex, value );
}
updateDefaultValues( fieldIndex );
updateDefaultValuesAndVirtualFields( fieldIndex );
updateVisibilityAndConstraints( fieldIndex );
return changed;
}
Expand Down Expand Up @@ -364,6 +364,10 @@ void QfAttributeFormModelBase::applyRelationshipDefaultValues()
}
}
}

updateDefaultValuesAndVirtualFields( -1, true );
updateEditorWidgetCodes( QString(), true );
updateVisibilityAndConstraints( -1, true );
}

void QfAttributeFormModelBase::activateAllRememberValues()
Expand Down Expand Up @@ -782,12 +786,14 @@ void QfAttributeFormModelBase::synchronizeFieldValue( int fieldIndex, QVariant v
}
}

void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector<int> updatedFields )
void QfAttributeFormModelBase::updateDefaultValuesAndVirtualFields( int fieldIndex, bool updateRelationships, QVector<int> updatedFields )
{
const QgsFields fields = mFeatureModel->feature().fields();
if ( fieldIndex < 0 || fieldIndex >= fields.size() )
if ( fieldIndex < -1 || fieldIndex >= fields.size() )
{
return;
const QString fieldName = fields.at( fieldIndex ).name();
}
const QString fieldName = fieldIndex > -1 ? fields.at( fieldIndex ).name() : QString();

mExpressionContext.setFields( fields );
mExpressionContext.setFeature( mFeatureModel->feature() );
Expand All @@ -796,21 +802,37 @@ void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector<int>
for ( ; fieldIterator != mFields.constEnd(); ++fieldIterator )
{
const int fidx = fieldIterator.value();
if ( fidx == fieldIndex || !fields.at( fidx ).defaultValueDefinition().isValid() || !fields.at( fidx ).defaultValueDefinition().applyOnUpdate() )
if ( fidx == fieldIndex )
{
continue;
}

const QgsField field = fields.at( fidx );
QgsExpression exp;
if ( fields.fieldOrigin( fidx ) == Qgis::FieldOrigin::Expression )
{
exp.setExpression( mLayer->expressionField( fidx ) );
}
else
{
if ( !field.defaultValueDefinition().isValid() || !field.defaultValueDefinition().applyOnUpdate() )
{
continue;
}

QgsExpression exp( fields.at( fidx ).defaultValueDefinition().expression() );
exp.setExpression( field.defaultValueDefinition().expression() );
}
exp.prepare( &mExpressionContext );

// avoid cost of value update if expression doesn't contain the field which triggered the default values update
if ( !exp.referencedColumns().contains( fieldName ) && !exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) )
if ( !exp.referencedColumns().contains( fieldName ) && !exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) && ( !updateRelationships || !exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ) )
{
continue;
}

const QVariant defaultValue = exp.evaluate( &mExpressionContext );
const QVariant value = exp.evaluate( &mExpressionContext );
const QVariant previousValue = mFeatureModel->data( mFeatureModel->index( fidx ), QfFeatureModel::AttributeValue );
const bool success = mFeatureModel->setData( mFeatureModel->index( fidx ), defaultValue, QfFeatureModel::AttributeValue );
const bool success = mFeatureModel->setData( mFeatureModel->index( fidx ), value, QfFeatureModel::AttributeValue );
const QVariant updatedValue = mFeatureModel->data( mFeatureModel->index( fidx ), QfFeatureModel::AttributeValue );
if ( success && updatedValue != previousValue )
{
Expand All @@ -821,17 +843,20 @@ void QfAttributeFormModelBase::updateDefaultValues( int fieldIndex, QVector<int>
if ( !updatedFields.contains( fidx ) )
{
updatedFields.append( fidx );
updateDefaultValues( fidx, updatedFields );
updateDefaultValuesAndVirtualFields( fidx, false, updatedFields );
}
updateVisibilityAndConstraints( fidx );
}
}

updateDataDefinedProperties( fieldName );
updateEditorWidgetCodes( fieldName );
if ( !fieldName.isEmpty() )
{
updateDataDefinedProperties( fieldName );
updateEditorWidgetCodes( fieldName );
}
}

bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, const QString &code, const QRegularExpression &regEx )
bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, bool updateRelationships, const QString &code, const QRegularExpression &regEx )
{
if ( !mEditorWidgetCodesRequirements.contains( code ) )
{
Expand All @@ -847,14 +872,16 @@ bool QfAttributeFormModelBase::codeRequiresUpdate( const QString &fieldName, con
exp.prepare( &mExpressionContext );
codeRequirements.referencedColumns.unite( exp.referencedColumns() );
codeRequirements.formScope = codeRequirements.formScope || QgsValueRelationFieldFormatter::expressionRequiresFormScope( expression );
qDebug() << "---" << exp.expression() << ( exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ? "contains rel agg." : "nope" );
codeRequirements.relationships = exp.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) );
}
mEditorWidgetCodesRequirements.insert( code, codeRequirements );
}

return mEditorWidgetCodesRequirements[code].referencedColumns.contains( fieldName ) || mEditorWidgetCodesRequirements[code].referencedColumns.contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || mEditorWidgetCodesRequirements[code].formScope;
return mEditorWidgetCodesRequirements[code].referencedColumns.contains( fieldName ) || mEditorWidgetCodesRequirements[code].referencedColumns.contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || mEditorWidgetCodesRequirements[code].formScope || ( updateRelationships && mEditorWidgetCodesRequirements[code].relationships );
}

void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &fieldName )
void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &fieldName, bool updateRelationships )
{
QMap<QStandardItem *, QString>::ConstIterator aliasExpressionsIterator( mAliasExpressions.constBegin() );
for ( ; aliasExpressionsIterator != mAliasExpressions.constEnd(); aliasExpressionsIterator++ )
Expand All @@ -866,7 +893,7 @@ void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &field
}

QgsExpression exp( aliasExpressionsIterator.value() );
exp.referencedColumns().contains( fieldName );
if ( exp.referencedColumns().contains( fieldName ) || exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || ( updateRelationships && exp.referencedFunctions().contains( "relation_aggregate" ) ) )
{
exp.prepare( &mExpressionContext );
QVariant result = exp.evaluate( &mExpressionContext );
Expand All @@ -892,7 +919,7 @@ void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &field
}

QgsExpression exp( readOnlyExpressionsIterator.value() );
exp.referencedColumns().contains( fieldName );
if ( exp.referencedColumns().contains( fieldName ) || exp.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || ( updateRelationships && exp.referencedFunctions().contains( "relation_aggregate" ) ) )
{
exp.prepare( &mExpressionContext );
QVariant result = exp.evaluate( &mExpressionContext );
Expand All @@ -901,7 +928,7 @@ void QfAttributeFormModelBase::updateDataDefinedProperties( const QString &field
}
}

void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName )
void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName, bool updateRelationships )
{
QMap<QStandardItem *, QString>::ConstIterator editorWidgetCodesIterator( mEditorWidgetCodes.constBegin() );
for ( ; editorWidgetCodesIterator != mEditorWidgetCodes.constEnd(); editorWidgetCodesIterator++ )
Expand All @@ -915,7 +942,7 @@ void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName
if ( item->data( QfAttributeFormModel::ElementType ) == QStringLiteral( "qml" ) || item->data( QfAttributeFormModel::ElementType ) == QStringLiteral( "html" ) )
{
const thread_local QRegularExpression sRegEx( "expression\\.evaluate\\(\\s*\\\"(.*?[^\\\\])\\\"\\s*\\)", QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption );
if ( codeRequiresUpdate( fieldName, code, sRegEx ) )
if ( codeRequiresUpdate( fieldName, updateRelationships, code, sRegEx ) )
{
QRegularExpressionMatch match = sRegEx.match( code );
while ( match.hasMatch() )
Expand Down Expand Up @@ -953,7 +980,7 @@ void QfAttributeFormModelBase::updateEditorWidgetCodes( const QString &fieldName
else if ( item->data( QfAttributeFormModel::ElementType ) == QStringLiteral( "text" ) )
{
const thread_local QRegularExpression sRegEx( QStringLiteral( "\\[%(.*?)%\\]" ), QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption );
if ( codeRequiresUpdate( fieldName, code, sRegEx ) )
if ( codeRequiresUpdate( fieldName, updateRelationships, code, sRegEx ) )
{
code = QgsExpression::replaceExpressionText( code, &mExpressionContext );
item->setData( code, QfAttributeFormModel::EditorWidgetCode );
Expand Down Expand Up @@ -989,7 +1016,7 @@ void _checkChildrenValidity( const QStandardItem *parent, bool &hardValidity, bo
}
};

void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex )
void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex, bool updateRelationships )
{
const QString fieldName = fieldIndex > -1 && fieldIndex < mLayer->fields().size() ? mLayer->fields().at( fieldIndex ).name() : QString();
QgsFields fields = mFeatureModel->feature().fields();
Expand All @@ -1001,7 +1028,7 @@ void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex )
{
// If triggered by an updated field index (fieldIndex), check if the visibility
// expression refers to that field
if ( fieldIndex == -1 || it.first.referencedColumns().contains( fieldName ) || it.first.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) )
if ( fieldIndex == -1 || it.first.referencedColumns().contains( fieldName ) || it.first.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) || ( updateRelationships && it.first.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ) )
{
QgsExpression exp = it.first;
exp.prepare( &mExpressionContext );
Expand Down Expand Up @@ -1029,7 +1056,7 @@ void QfAttributeFormModelBase::updateVisibilityAndConstraints( int fieldIndex )
// Check whether the current field iterator index (fidx) has an expression constraints referencing the
// updated field index (fieldIndex) which triggered a constraints update
const QgsExpression expression = mLayer->fields().at( fidx ).constraints().constraintExpression();
if ( !expression.referencedColumns().contains( fieldName ) && !expression.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) )
if ( !expression.referencedColumns().contains( fieldName ) && !expression.referencedColumns().contains( QgsFeatureRequest::ALL_ATTRIBUTES ) && ( !updateRelationships || !expression.referencedFunctions().contains( QStringLiteral( "relation_aggregate" ) ) ) )
{
continue;
}
Expand Down Expand Up @@ -1175,7 +1202,7 @@ bool QfAttributeFormModelBase::changeAttribute( const QString &name, const QVari
mExpressionContext.popScope();
mExpressionContext << QgsExpressionContextUtils::formScope( mFeatureModel->feature() );
synchronizeFieldValue( fieldIndex, value );
updateDefaultValues( fieldIndex );
updateDefaultValuesAndVirtualFields( fieldIndex );
updateVisibilityAndConstraints( fieldIndex );
}

Expand All @@ -1192,7 +1219,7 @@ bool QfAttributeFormModelBase::changeGeometry( const QgsGeometry &geometry )
{
mExpressionContext.popScope();
mExpressionContext << QgsExpressionContextUtils::formScope( mFeatureModel->feature() );
updateDefaultValues();
updateDefaultValuesAndVirtualFields();
updateVisibilityAndConstraints();
}

Expand Down
15 changes: 8 additions & 7 deletions src/gui/qfattributeformmodelbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class QfAttributeFormModelBase : public QStandardItemModel
{
QSet<QString> referencedColumns;
bool formScope = false;
bool relationships = false;
};

/**
Expand All @@ -129,19 +130,19 @@ class QfAttributeFormModelBase : public QStandardItemModel
void synchronizeFieldValue( int fieldIndex, QVariant value );

//! Update default values refering to the \a fieldIndex.
void updateDefaultValues( int fieldIndex = -1, QVector<int> updatedFields = QVector<int>() );
void updateDefaultValuesAndVirtualFields( int fieldIndex = -1, bool updateRelationships = false, QVector<int> updatedFields = QVector<int>() );

//! Update QML, HTML, and text widget code.
void updateEditorWidgetCodes( const QString &fieldName );
void updateEditorWidgetCodes( const QString &fieldName, bool updateRelationships = false );

//! Update expression-driven alias and read-only values.
void updateDataDefinedProperties( const QString &fieldName );

//! Check if the given \a code requires update.
bool codeRequiresUpdate( const QString &fieldName, const QString &code, const QRegularExpression &regEx );
void updateDataDefinedProperties( const QString &fieldName, bool updateRelationships = false );

//! Udate the visibility state of groups as well as constraints of field items
void updateVisibilityAndConstraints( int fieldIndex = -1 );
void updateVisibilityAndConstraints( int fieldIndex = -1, bool updateRelationships = false );

//! Check if the given \a code requires update.
bool codeRequiresUpdate( const QString &fieldName, bool updateRelationships, const QString &code, const QRegularExpression &regEx );

void setConstraintsHardValid( bool constraintsHardValid );

Expand Down
34 changes: 33 additions & 1 deletion test/test_attributeformmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ TEST_CASE( "AttributeFormModel" )

QgsFeature f1;
f1.setAttributes( QgsAttributes() << 1 << QStringLiteral( "string_a1" ) << QStringLiteral( "string_b1" ) );
qDebug() << f1.attributes();
QgsFeature f2;
f2.setAttributes( QgsAttributes() << 2 << QStringLiteral( "string_a2" ) << QStringLiteral( "string_b2" ) );
QgsFeature f3;
Expand Down Expand Up @@ -101,6 +100,39 @@ TEST_CASE( "AttributeFormModel" )
REQUIRE( feature.attributes().at( 2 ) == QStringLiteral( "edit_feature__" ) );
}

SECTION( "VirtualFieldUpdate" )
{
std::unique_ptr<QgsVectorLayer> layer2 = std::make_unique<QgsVectorLayer>( QStringLiteral( "Point?crs=EPSG:3857&field=fid:integer&field=str:string&field=str2:string" ), QStringLiteral( "Input Layer" ), QStringLiteral( "memory" ) );
REQUIRE( layer2->isValid() );

QgsFeature l2f1;
l2f1.setAttributes( QgsAttributes() << 1 << QStringLiteral( "string_a1" ) << QStringLiteral( "string_b1" ) );

layer2->startEditing();
layer2->addFeature( l2f1 );
layer2->commitChanges();
REQUIRE( layer2->featureCount() == 1 );

layer2->addExpressionField( QStringLiteral( "\"str\"" ), QgsField( QStringLiteral( "str_virtual" ), QMetaType::Type::QString ) );

// Trigger attribute form re-build
featureModel->setCurrentLayer( layer2.get() );

featureModel->setFeature( layer2->getFeature( 1 ) );
REQUIRE( attributeFormModel->attribute( QStringLiteral( "str" ) ) == QStringLiteral( "string_a1" ) );
REQUIRE( attributeFormModel->attribute( QStringLiteral( "str_virtual" ) ) == QStringLiteral( "string_a1" ) );
REQUIRE( attributeFormModel->data( attributeFormModel->index( 1, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1" ) );
REQUIRE( attributeFormModel->data( attributeFormModel->index( 3, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1" ) );

attributeFormModel->changeAttribute( QStringLiteral( "str" ), QStringLiteral( "string_a1_modified" ) );
REQUIRE( attributeFormModel->attribute( QStringLiteral( "str" ) ) == QStringLiteral( "string_a1_modified" ) );
REQUIRE( attributeFormModel->attribute( QStringLiteral( "str_virtual" ) ) == QStringLiteral( "string_a1_modified" ) );
REQUIRE( attributeFormModel->data( attributeFormModel->index( 1, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1_modified" ) );
REQUIRE( attributeFormModel->data( attributeFormModel->index( 3, 0 ), QfAttributeFormModel::AttributeValue ) == QStringLiteral( "string_a1_modified" ) );

featureModel->setCurrentLayer( layer.get() );
}

SECTION( "ReadOnlyDataDefinedProperty" )
{
featureModel->resetFeature();
Expand Down
Loading