Skip to content

Commit

Permalink
Fix #43901 No update of virtual fields considering layer dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
domi4484 committed Dec 9, 2021
1 parent b3d6deb commit 1e37936
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 4 deletions.
Expand Up @@ -242,6 +242,19 @@ Forward the signal to the embedded form
virtual bool valid() const;


signals:

void relatedFeaturesChanged();
%Docstring
Emit this signal, whenever the related features changed.
This happens for example when related features are added, removed,
linked or unlinked.

:param vectorLayer: The modified layer

.. versionadded:: 3.22
%End

public slots:
virtual void setFeature( const QgsFeature &feature );

Expand Down
20 changes: 20 additions & 0 deletions python/gui/auto_generated/qgsabstractrelationeditorwidget.sip.in
Expand Up @@ -156,6 +156,26 @@ Returns the widget configuration
virtual void setConfig( const QVariantMap &config ) = 0;
%Docstring
Defines the widget configuration
%End

bool multiEditModeActive() const;
%Docstring
Returns true if editing multiple features at a time

.. versionadded:: 3.24
%End

signals:

void relatedFeaturesChanged();
%Docstring
Emit this signal, whenever the related features changed.
This happens for example when related features are added, removed,
linked or unlinked.

:param vectorLayer: The modified layer

.. versionadded:: 3.22
%End

public slots:
Expand Down
16 changes: 12 additions & 4 deletions src/gui/editorwidgets/qgsrelationwidgetwrapper.cpp
Expand Up @@ -42,15 +42,17 @@ QWidget *QgsRelationWidgetWrapper::createWidget( QWidget *parent )
if ( form )
connect( form, &QgsAttributeForm::widgetValueChanged, this, &QgsRelationWidgetWrapper::widgetValueChanged );

QWidget *widget = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorId, widgetConfig(), parent );
QgsAbstractRelationEditorWidget *relationEditorWidget = QgsGui::instance()->relationWidgetRegistry()->create( mRelationEditorId, widgetConfig(), parent );

if ( !widget )
if ( !relationEditorWidget )
{
QgsLogger::warning( QStringLiteral( "Failed to create relation widget \"%1\", fallback to \"basic\" relation widget" ).arg( mRelationEditorId ) );
widget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "relation_editor" ), widgetConfig(), parent );
relationEditorWidget = QgsGui::instance()->relationWidgetRegistry()->create( QStringLiteral( "relation_editor" ), widgetConfig(), parent );
}

return widget;
connect( relationEditorWidget, &QgsAbstractRelationEditorWidget::relatedFeaturesChanged, this, &QgsRelationWidgetWrapper::onRelatedFeaturesChanged );

return relationEditorWidget;
}

void QgsRelationWidgetWrapper::setFeature( const QgsFeature &feature )
Expand All @@ -65,6 +67,11 @@ void QgsRelationWidgetWrapper::setVisible( bool visible )
mWidget->setVisible( visible );
}

void QgsRelationWidgetWrapper::onRelatedFeaturesChanged()
{
emit relatedFeaturesChanged();
}

void QgsRelationWidgetWrapper::aboutToSave()
{
if ( !mRelation.isValid() || !widget() || !widget()->isVisible() || mRelation.referencingLayer() == mRelation.referencedLayer() )
Expand Down Expand Up @@ -102,6 +109,7 @@ void QgsRelationWidgetWrapper::widgetValueChanged( const QString &attribute, con
{
if ( mWidget && attributeChanged )
{
// Maybe from here the other bug? jump to first feature?
QgsFeature feature { mWidget->feature() };
if ( feature.attribute( attribute ) != newValue )
{
Expand Down
16 changes: 16 additions & 0 deletions src/gui/editorwidgets/qgsrelationwidgetwrapper.h
Expand Up @@ -210,6 +210,18 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper
void initWidget( QWidget *editor ) override;
bool valid() const override;

signals:

/**
* Emit this signal, whenever the related features changed.
* This happens for example when related features are added, removed,
* linked or unlinked.
*
* \param vectorLayer The modified layer
* \since QGIS 3.22
*/
void relatedFeaturesChanged();

public slots:
void setFeature( const QgsFeature &feature ) override;

Expand All @@ -220,6 +232,10 @@ class GUI_EXPORT QgsRelationWidgetWrapper : public QgsWidgetWrapper
*/
void setVisible( bool visible );

private slots:

void onRelatedFeaturesChanged();

private:
void aboutToSave() override;
QgsRelation mRelation;
Expand Down
16 changes: 16 additions & 0 deletions src/gui/qgsabstractrelationeditorwidget.cpp
Expand Up @@ -248,11 +248,17 @@ void QgsAbstractRelationEditorWidget::addFeature( const QgsGeometry &geometry )
}

updateUi();

emit relatedFeaturesChanged();

return addedFeatureIds;
}

void QgsAbstractRelationEditorWidget::deleteFeature( const QgsFeatureId fid )
{
deleteFeatures( QgsFeatureIds() << fid );

emit relatedFeaturesChanged();
}

void QgsAbstractRelationEditorWidget::deleteFeatures( const QgsFeatureIds &fids )
Expand Down Expand Up @@ -369,6 +375,8 @@ void QgsAbstractRelationEditorWidget::deleteFeatures( const QgsFeatureIds &fids
}

updateUi();

emit relatedFeaturesChanged();
}
}

Expand Down Expand Up @@ -493,6 +501,8 @@ void QgsAbstractRelationEditorWidget::onLinkFeatureDlgAccepted()
}

updateUi();

emit relatedFeaturesChanged();
}

void QgsAbstractRelationEditorWidget::unlinkFeature( const QgsFeatureId fid )
Expand Down Expand Up @@ -580,6 +590,8 @@ void QgsAbstractRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &fids
}

updateUi();

emit relatedFeaturesChanged();
}

void QgsAbstractRelationEditorWidget::updateUi()
Expand Down Expand Up @@ -611,6 +623,8 @@ void QgsAbstractRelationEditorWidget::afterSetRelations()
void QgsAbstractRelationEditorWidget::duplicateFeature( const QgsFeatureId &fid )
{
duplicateFeatures( QgsFeatureIds() << fid );

emit relatedFeaturesChanged();
}

void QgsAbstractRelationEditorWidget::duplicateFeatures( const QgsFeatureIds &fids )
Expand All @@ -624,6 +638,8 @@ void QgsAbstractRelationEditorWidget::duplicateFeatures( const QgsFeatureIds &fi
QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext;
QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicatedFeatureContext );
}

emit relatedFeaturesChanged();
}

void QgsAbstractRelationEditorWidget::showEvent( QShowEvent * )
Expand Down
18 changes: 18 additions & 0 deletions src/gui/qgsabstractrelationeditorwidget.h
Expand Up @@ -170,6 +170,24 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget
*/
virtual void setConfig( const QVariantMap &config ) = 0;

/**
* Returns true if editing multiple features at a time
* \since QGIS 3.24
*/
bool multiEditModeActive() const;

signals:

/**
* Emit this signal, whenever the related features changed.
* This happens for example when related features are added, removed,
* linked or unlinked.
*
* \param vectorLayer The modified layer
* \since QGIS 3.22
*/
void relatedFeaturesChanged();

public slots:

/**
Expand Down
114 changes: 114 additions & 0 deletions src/gui/qgsattributeform.cpp
Expand Up @@ -622,6 +622,77 @@ void QgsAttributeForm::updateValuesDependenciesVirtualFields( const int originId
}
}

void QgsAttributeForm::updateRelatedLayerFields()
{
// Synchronize dependencies
for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( ! eww )
continue;

updateRelatedLayerFieldsDependencies( eww );
}

if ( mRelatedLayerFieldsDependencies.isEmpty() )
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
for ( QgsWidgetWrapper *ww : std::as_const( mRelatedLayerFieldsDependencies ) )
{
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
if ( !eww )
continue;

//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 @@ -1280,6 +1351,11 @@ void QgsAttributeForm::onAttributeDeleted( int idx )
setFeature( mFeature );
}

void QgsAttributeForm::onRelatedFeaturesChanged()
{
updateRelatedLayerFields();
}

void QgsAttributeForm::onUpdatedFields()
{
mPreventFeatureRefresh = false;
Expand Down Expand Up @@ -2430,6 +2506,14 @@ void QgsAttributeForm::afterWidgetInit()
connect( eww, &QgsEditorWidgetWrapper::valuesChanged, this, &QgsAttributeForm::onAttributeChanged );
connect( eww, &QgsEditorWidgetWrapper::constraintStatusChanged, this, &QgsAttributeForm::onConstraintStatusChanged );
}
else
{
QgsRelationWidgetWrapper *relationWidgetWrapper = qobject_cast<QgsRelationWidgetWrapper *>( ww );
if ( relationWidgetWrapper )
{
connect( relationWidgetWrapper, &QgsRelationWidgetWrapper::relatedFeaturesChanged, this, &QgsAttributeForm::onRelatedFeaturesChanged );
}
}
}
}

Expand Down Expand Up @@ -2713,6 +2797,7 @@ void QgsAttributeForm::updateFieldDependencies()
{
mDefaultValueDependencies.clear();
mVirtualFieldsDependencies.clear();
mRelatedLayerFieldsDependencies.clear();

//create defaultValueDependencies
for ( QgsWidgetWrapper *ww : std::as_const( mWidgets ) )
Expand All @@ -2724,6 +2809,8 @@ void QgsAttributeForm::updateFieldDependencies()
updateFieldDependenciesDefaultValue( eww );

updateFieldDependenciesVirtualFields( eww );

updateRelatedLayerFieldsDependencies( eww );
}
}

Expand Down Expand Up @@ -2774,6 +2861,33 @@ void QgsAttributeForm::updateFieldDependenciesVirtualFields( QgsEditorWidgetWrap
}
}

void QgsAttributeForm::updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww )
{
for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
{
QgsAttributeFormRelationEditorWidget *relationEditorWidget = dynamic_cast<QgsAttributeFormRelationEditorWidget *>( formWidget );
if ( !relationEditorWidget )
continue;

QString expressionField = eww->layer()->expressionField( eww->fieldIdx() );
if ( expressionField.contains( QStringLiteral( "relation_aggregate" ) )
|| expressionField.contains( QStringLiteral( "get_features" ) ) )
mRelatedLayerFieldsDependencies.insert( eww );
}
}

void QgsAttributeForm::setMultiEditFeatureIdsRelations( const QgsFeatureIds &fids )
{
for ( QgsAttributeFormWidget *formWidget : mFormWidgets )
{
QgsAttributeFormRelationEditorWidget *relationEditorWidget = dynamic_cast<QgsAttributeFormRelationEditorWidget *>( formWidget );
if ( !relationEditorWidget )
continue;

relationEditorWidget->setMultiEditFeatureIds( fids );
}
}

void QgsAttributeForm::updateIcon( QgsEditorWidgetWrapper *eww )
{
if ( !eww->widget() || !mIconMap[eww->widget()] )
Expand Down
9 changes: 9 additions & 0 deletions src/gui/qgsattributeform.h
Expand Up @@ -341,6 +341,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
void onAttributeChanged( const QVariant &value, const QVariantList &additionalFieldValues );
void onAttributeAdded( int idx );
void onAttributeDeleted( int idx );
void onRelatedFeaturesChanged();
void onUpdatedFields();
void onConstraintStatusChanged( const QString &constraint,
const QString &description, const QString &err, QgsEditorWidgetWrapper::ConstraintResult result );
Expand Down Expand Up @@ -380,6 +381,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
void updateFieldDependencies();
void updateFieldDependenciesDefaultValue( QgsEditorWidgetWrapper *eww );
void updateFieldDependenciesVirtualFields( QgsEditorWidgetWrapper *eww );
void updateRelatedLayerFieldsDependencies( QgsEditorWidgetWrapper *eww );

struct WidgetInfo
{
Expand Down Expand Up @@ -413,6 +415,8 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
void updateValuesDependenciesDefaultValues( const int originIdx );
void updateValuesDependenciesVirtualFields( const int originIdx );

void updateRelatedLayerFields();

void clearMultiEditMessages();
void pushSelectedFeaturesMessage();
void runSearchSelect( Qgis::SelectBehavior behavior );
Expand Down Expand Up @@ -525,6 +529,11 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
*/
QMap<int, QgsWidgetWrapper *> mVirtualFieldsDependencies;

/**
* Dependency list for values depending on related layers.
*/
QSet<QgsWidgetWrapper *> mRelatedLayerFieldsDependencies;

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

Expand Down

0 comments on commit 1e37936

Please sign in to comment.