Skip to content

Commit cc20aa1

Browse files
troopa81nyalldawson
authored andcommitted
fix(AttributeForm): Update same field widgets before firing signals
If not, saveEdits would be called when we fire widgetValueChanged signal and widgets containing non updated old values could potentially overwrite new ones, leading to discard user modifications.
1 parent ca9a5cf commit cc20aa1

File tree

2 files changed

+59
-13
lines changed

2 files changed

+59
-13
lines changed

src/gui/qgsattributeform.cpp

+13-13
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,19 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariant
10361036

10371037
mCurrentFormFeature.setAttribute( eww->field().name(), value );
10381038

1039+
// Update other widgets pointing to the same field, required to happen now to insure
1040+
// currentFormValuesFeature() gets the right value when processing constraints
1041+
const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->fieldIdx() );
1042+
for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
1043+
{
1044+
if ( formEditorWidget->editorWidget() == eww )
1045+
continue;
1046+
1047+
// formEditorWidget and eww points to the same field, so block signals
1048+
// as there is no need to handle valueChanged again for each duplicate
1049+
whileBlocking( formEditorWidget->editorWidget() )->setValue( value );
1050+
}
1051+
10391052
switch ( mMode )
10401053
{
10411054
case QgsAttributeEditorContext::SingleEditMode:
@@ -1091,19 +1104,6 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariant
10911104
break;
10921105
}
10931106

1094-
// Update other widgets pointing to the same field, required to happen now to insure
1095-
// currentFormValuesFeature() gets the right value when processing constraints
1096-
const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->fieldIdx() );
1097-
for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
1098-
{
1099-
if ( formEditorWidget->editorWidget() == eww )
1100-
continue;
1101-
1102-
// formEditorWidget and eww points to the same field, so block signals
1103-
// as there is no need to handle valueChanged again for each duplicate
1104-
whileBlocking( formEditorWidget->editorWidget() )->setValue( value );
1105-
}
1106-
11071107
updateConstraints( eww );
11081108

11091109
// Update dependent fields (only if form is not initializing)

tests/src/gui/testqgsdualview.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include <attributetable/qgsdualview.h>
2222
#include <editform/qgsattributeeditorhtmlelement.h>
2323
#include "qgsattributeform.h"
24+
#include "qgsattributeeditorcontainer.h"
25+
#include "qgsattributeeditorfield.h"
26+
#include "qgsattributeformeditorwidget.h"
2427
#include <qgsapplication.h>
2528
#include "qgsfeatureiterator.h"
2629
#include <qgsvectorlayer.h>
@@ -58,6 +61,8 @@ class TestQgsDualView : public QObject
5861
void testAttributeFormSharedValueScanning();
5962
void testNoGeom();
6063

64+
void testDuplicateField();
65+
6166
#ifdef WITH_QTWEBKIT
6267
void testHtmlWidget_data();
6368
void testHtmlWidget();
@@ -404,5 +409,46 @@ void TestQgsDualView::testHtmlWidget()
404409
}
405410
#endif
406411

412+
void TestQgsDualView::testDuplicateField()
413+
{
414+
// test updating same field appearing in different widget
415+
416+
// make a temporary vector layer
417+
const QString def = QStringLiteral( "Point?field=col0:integer" );
418+
QgsVectorLayer *layer = new QgsVectorLayer( def, QStringLiteral( "test" ), QStringLiteral( "memory" ) );
419+
layer->setEditorWidgetSetup( 0, QgsEditorWidgetSetup( QStringLiteral( "Range" ), QVariantMap() ) );
420+
421+
// add same field twice so they get synced
422+
QgsEditFormConfig editFormConfig = layer->editFormConfig();
423+
editFormConfig.clearTabs();
424+
editFormConfig.invisibleRootContainer()->addChildElement( new QgsAttributeEditorField( "col0", 0, editFormConfig.invisibleRootContainer() ) );
425+
editFormConfig.invisibleRootContainer()->addChildElement( new QgsAttributeEditorField( "col0", 0, editFormConfig.invisibleRootContainer() ) );
426+
editFormConfig.setLayout( Qgis::AttributeFormLayout::DragAndDrop );
427+
layer->setEditFormConfig( editFormConfig );
428+
429+
// add a feature to the vector layer
430+
QgsFeature ft( layer->dataProvider()->fields(), 1 );
431+
ft.setAttribute( QStringLiteral( "col0" ), 1 );
432+
layer->dataProvider()->addFeature( ft );
433+
434+
QgsDualView dualView;
435+
dualView.init( layer, mCanvas );
436+
437+
layer->startEditing();
438+
439+
const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = dualView.mAttributeForm->mFormEditorWidgets.values( 0 );
440+
441+
formEditorWidgets[0]->editorWidget()->setValues( 20, QVariantList() );
442+
ft = layer->getFeature( ft.id() );
443+
QCOMPARE( ft.attribute( QStringLiteral( "col0" ) ).toInt(), 20 );
444+
445+
formEditorWidgets[1]->editorWidget()->setValues( 21, QVariantList() );
446+
ft = layer->getFeature( ft.id() );
447+
QCOMPARE( ft.attribute( QStringLiteral( "col0" ) ).toInt(), 21 );
448+
449+
layer->rollBack();
450+
}
451+
452+
407453
QGSTEST_MAIN( TestQgsDualView )
408454
#include "testqgsdualview.moc"

0 commit comments

Comments
 (0)