Skip to content

Commit

Permalink
Update of virtual fields considering values of the same form fix #43900
Browse files Browse the repository at this point in the history
  • Loading branch information
domi4484 authored and github-actions[bot] committed Nov 11, 2021
1 parent 63abeb8 commit fbb59cf
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
101 changes: 101 additions & 0 deletions src/gui/qgsattributeform.cpp
Expand Up @@ -555,6 +555,71 @@ bool QgsAttributeForm::updateDefaultValues( const int originIdx )
return true;
}

void QgsAttributeForm::updateVirtualFields( const int originIdx )
{
// Synchronize
updateVirtualFieldsDependencies();

if ( !mVirtualFieldsDependencies.contains( originIdx ) )
return;

if ( !mFeature.isValid() )
return;

QgsAttributes featureAttributes = mFeature.attributes();
for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( !eww )
continue;

QVariantList dstVars = QVariantList() << featureAttributes.at( eww->fieldIdx() );
QVariantList srcVars = QVariantList() << eww->value();
QList<int> fieldIndexes = QList<int>() << eww->fieldIdx();

// append additional fields
const QStringList additionalFields = eww->additionalFields();
for ( const QString &fieldName : additionalFields )
{
int idx = eww->layer()->fields().lookupField( fieldName );
fieldIndexes << idx;
dstVars << featureAttributes.at( idx );
}
srcVars.append( eww->additionalFieldValues() );

Q_ASSERT( dstVars.count() == srcVars.count() );

for ( int i = 0; i < dstVars.count(); i++ )
{
if ( !qgsVariantEqual( dstVars[i], srcVars[i] ) && srcVars[i].isValid() && fieldIsEditable( fieldIndexes[i] ) )
featureAttributes[fieldIndexes[i]] = srcVars[i];
}
}

// go through depending fields and update the fields with virtual field
QList<QgsWidgetWrapper *> relevantWidgets = mVirtualFieldsDependencies.values( originIdx );
for ( QgsWidgetWrapper *ww : std::as_const( relevantWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( !eww )
return;

//do not update when this widget is already updating (avoid recursions)
if ( mAlreadyUpdatedFields.contains( eww->fieldIdx() ) )
continue;

// create updated Feature
QgsFeature updatedFeature = QgsFeature( mFeature );
updatedFeature.setAttributes( featureAttributes );
QgsExpressionContext context = createExpressionContext( updatedFeature );

QgsExpression exp( mLayer->expressionField( eww->fieldIdx() ) );
QVariant value = exp.evaluate( &context );

eww->setValue( value );
}
}

void QgsAttributeForm::resetMultiEdit( bool promptToSave )
{
if ( promptToSave )
Expand Down Expand Up @@ -961,6 +1026,7 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariant
//append field index here, so it's not updated recursive
mAlreadyUpdatedFields.append( eww->fieldIdx() );
updateDefaultValues( eww->fieldIdx() );
updateVirtualFields( eww->fieldIdx() );
mAlreadyUpdatedFields.removeAll( eww->fieldIdx() );

// Updates expression controlled labels
Expand Down Expand Up @@ -1769,6 +1835,7 @@ void QgsAttributeForm::init()
}

updateDefaultValueDependencies();
updateVirtualFieldsDependencies();

if ( !mButtonBox )
{
Expand Down Expand Up @@ -2665,6 +2732,40 @@ void QgsAttributeForm::updateDefaultValueDependencies()
}
}

void QgsAttributeForm::updateVirtualFieldsDependencies()
{
mVirtualFieldsDependencies.clear();

for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( !eww )
continue;

QString expressionField = eww->layer()->expressionField( eww->fieldIdx() );
QgsExpression exp( expressionField );
if ( expressionField.isEmpty() )
continue;

const QSet<QString> referencedColumns = exp.referencedColumns();
for ( const QString &referencedColumn : referencedColumns )
{
if ( referencedColumn == QgsFeatureRequest::ALL_ATTRIBUTES )
{
const QList<int> allAttributeIds( mLayer->fields().allAttributesList() );
for ( const int id : allAttributeIds )
{
mVirtualFieldsDependencies.insertMulti( id, eww );
}
}
else
{
mVirtualFieldsDependencies.insertMulti( mLayer->fields().lookupField( referencedColumn ), eww );
}
}
}
}

void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
{
if ( !eww->widget() || !mIconMap[eww->widget()] )
Expand Down
11 changes: 11 additions & 0 deletions src/gui/qgsattributeform.h
Expand Up @@ -379,6 +379,8 @@ class GUI_EXPORT QgsAttributeForm : public QWidget

void updateDefaultValueDependencies();

void updateVirtualFieldsDependencies();

struct WidgetInfo
{
QWidget *widget = nullptr;
Expand Down Expand Up @@ -412,6 +414,9 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
//! update the default values in the fields after a referenced field changed
bool updateDefaultValues( const int originIdx );

//! update the virtual fields values in the fields after a referenced field changed
void updateVirtualFields( const int originIdx );

void clearMultiEditMessages();
void pushSelectedFeaturesMessage();
void runSearchSelect( Qgis::SelectBehavior behavior );
Expand Down Expand Up @@ -518,6 +523,12 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
*/
QMap<int, QgsWidgetWrapper *> mDefaultValueDependencies;

/**
* Dependency map for values from virtual fields. Attribute index -> widget wrapper.
* Attribute indexes will be added multiple times if more than one widget depends on them.
*/
QMap<int, QgsWidgetWrapper *> mVirtualFieldsDependencies;

//! List of updated fields to avoid recursion on the setting of defaultValues
QList<int> mAlreadyUpdatedFields;

Expand Down

0 comments on commit fbb59cf

Please sign in to comment.