Skip to content

Commit 632eca6

Browse files
authoredJul 7, 2017
Merge pull request #4795 from pblottiere/dynamicform
[FEATURE] Dynamic form for joined fields
2 parents 0036f27 + df5551a commit 632eca6

20 files changed

+438
-26
lines changed
 

‎python/core/qgsvectorlayerjoinbuffer.sip

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,24 @@ Quick way to test if there is any join at all
9999
:rtype: list of int
100100
%End
101101

102+
QList<const QgsVectorLayerJoinInfo *> joinsWhereFieldIsId( const QgsField &field ) const;
103+
%Docstring
104+
Returns joins where the field of a target layer is considered as an id.
105+
\param field the field of a target layer
106+
:return: a list of vector joins
107+
.. versionadded:: 3.0
108+
:rtype: list of const QgsVectorLayerJoinInfo
109+
%End
110+
111+
QgsFeature joinedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const;
112+
%Docstring
113+
Returns the joined feature corresponding to the feature.
114+
\param info the vector join information
115+
\param feature the feature of the target layer
116+
.. versionadded:: 3.0
117+
:rtype: QgsFeature
118+
%End
119+
102120
QgsVectorLayerJoinBuffer *clone() const /Factory/;
103121
%Docstring
104122
.. versionadded:: 2.6

‎python/core/qgsvectorlayerjoininfo.sip

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,30 @@ Returns whether values from the joined layer should be cached in memory to speed
8484
:rtype: bool
8585
%End
8686

87+
bool isDynamicFormEnabled() const;
88+
%Docstring
89+
Returns whether the form has to be dynamically updated with joined fields
90+
when a feature is being created in the target layer.
91+
.. versionadded:: 3.0
92+
:rtype: bool
93+
%End
94+
95+
void setDynamicFormEnabled( bool enabled );
96+
%Docstring
97+
Sets whether the form has to be dynamically updated with joined fields
98+
when a feature is being created in the target layer.
99+
.. versionadded:: 3.0
100+
%End
101+
102+
QString prefixedFieldName( const QgsField &field ) const;
103+
%Docstring
104+
Returns the prefixed name of the field.
105+
\param field the field
106+
:return: the prefixed name of the field
107+
.. versionadded:: 3.0
108+
:rtype: str
109+
%End
110+
87111
bool operator==( const QgsVectorLayerJoinInfo &other ) const;
88112

89113
void setJoinFieldNamesSubset( QStringList *fieldNamesSubset /Transfer/ );
@@ -108,6 +132,7 @@ Returns whether values from the joined layer should be cached in memory to speed
108132

109133

110134

135+
111136
};
112137

113138

‎python/gui/editorwidgets/core/qgseditorwidgetwrapper.sip

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ class QgsEditorWidgetWrapper : QgsWidgetWrapper
156156
:rtype: str
157157
%End
158158

159+
virtual void setHint( const QString &hintText );
160+
%Docstring
161+
Add a hint text on the widget
162+
\param hintText The hint text to display
163+
.. versionadded:: 3.0
164+
%End
165+
159166
signals:
160167

161168
void valueChanged( const QVariant &value );

‎python/gui/qgsattributeform.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ class QgsAttributeForm : QWidget
167167

168168
public slots:
169169

170-
void changeAttribute( const QString &field, const QVariant &value );
170+
void changeAttribute( const QString &field, const QVariant &value, const QString &hintText = QString() );
171171
%Docstring
172172
Call this to change the content of a given attribute. Will update the editor(s) related to this field.
173173

‎src/app/qgsjoindialog.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ QgsJoinDialog::QgsJoinDialog( QgsVectorLayer *layer, QList<QgsMapLayer *> alread
4242

4343
mTargetFieldComboBox->setLayer( mLayer );
4444

45+
mDynamicFormCheckBox->setToolTip( tr( "This option allows values of the joined fields to be automatically reloaded when the \"Target Field\" is changed" ) );
46+
4547
mJoinLayerComboBox->setFilters( QgsMapLayerProxyModel::VectorLayer );
4648
mJoinLayerComboBox->setExceptedLayerList( alreadyJoinedLayers );
4749
connect( mJoinLayerComboBox, &QgsMapLayerComboBox::layerChanged, mJoinFieldComboBox, &QgsFieldComboBox::setLayer );
@@ -73,6 +75,7 @@ void QgsJoinDialog::setJoinInfo( const QgsVectorLayerJoinInfo &joinInfo )
7375
mJoinFieldComboBox->setField( joinInfo.joinFieldName() );
7476
mTargetFieldComboBox->setField( joinInfo.targetFieldName() );
7577
mCacheInMemoryCheckBox->setChecked( joinInfo.isUsingMemoryCache() );
78+
mDynamicFormCheckBox->setChecked( joinInfo.isDynamicFormEnabled() );
7679
if ( joinInfo.prefix().isNull() )
7780
{
7881
mUseCustomPrefix->setChecked( false );
@@ -110,6 +113,7 @@ QgsVectorLayerJoinInfo QgsJoinDialog::joinInfo() const
110113
info.setJoinFieldName( mJoinFieldComboBox->currentField() );
111114
info.setTargetFieldName( mTargetFieldComboBox->currentField() );
112115
info.setUsingMemoryCache( mCacheInMemoryCheckBox->isChecked() );
116+
info.setDynamicFormEnabled( mDynamicFormCheckBox->isChecked() );
113117

114118
if ( mUseCustomPrefix->isChecked() )
115119
info.setPrefix( mCustomPrefix->text() );

‎src/app/qgsvectorlayerproperties.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,16 +1235,21 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo
12351235
joinItem->setText( 3, QChar( 0x2714 ) );
12361236
}
12371237

1238-
joinItem->setText( 4, join.prefix() );
1238+
if ( join.isDynamicFormEnabled() )
1239+
{
1240+
joinItem->setText( 4, QChar( 0x2714 ) );
1241+
}
1242+
1243+
joinItem->setText( 5, join.prefix() );
12391244

12401245
const QStringList *list = join.joinFieldNamesSubset();
12411246
if ( list )
12421247
{
1243-
joinItem->setText( 5, QStringLiteral( "%1" ).arg( list->count() ) );
1248+
joinItem->setText( 6, QStringLiteral( "%1" ).arg( list->count() ) );
12441249
}
12451250
else
12461251
{
1247-
joinItem->setText( 5, tr( "all" ) );
1252+
joinItem->setText( 6, tr( "all" ) );
12481253
}
12491254

12501255
if ( insertIndex >= 0 )
@@ -1255,7 +1260,7 @@ void QgsVectorLayerProperties::addJoinToTreeWidget( const QgsVectorLayerJoinInfo
12551260
{
12561261
mJoinTreeWidget->addTopLevelItem( joinItem );
12571262
}
1258-
for ( int c = 0; c < 5; c++ )
1263+
for ( int c = 0; c < 6; c++ )
12591264
{
12601265
mJoinTreeWidget->resizeColumnToContents( c );
12611266
}

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ SET(QGIS_CORE_SRCS
286286
qgsvectorlayerfeatureiterator.cpp
287287
qgsvectorlayerexporter.cpp
288288
qgsvectorlayerjoinbuffer.cpp
289+
qgsvectorlayerjoininfo.cpp
289290
qgsvectorlayerlabeling.cpp
290291
qgsvectorlayerlabelprovider.cpp
291292
qgsvectorlayerrenderer.cpp

‎src/core/qgsvectorlayerjoinbuffer.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &doc
275275
joinElem.setAttribute( QStringLiteral( "joinFieldName" ), joinIt->joinFieldName() );
276276

277277
joinElem.setAttribute( QStringLiteral( "memoryCache" ), joinIt->isUsingMemoryCache() );
278+
joinElem.setAttribute( QStringLiteral( "dynamicForm" ), joinIt->isDynamicFormEnabled() );
278279

279280
if ( joinIt->joinFieldNamesSubset() )
280281
{
@@ -315,6 +316,7 @@ void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node )
315316
info.setJoinLayerId( infoElem.attribute( QStringLiteral( "joinLayerId" ) ) );
316317
info.setTargetFieldName( infoElem.attribute( QStringLiteral( "targetFieldName" ) ) );
317318
info.setUsingMemoryCache( infoElem.attribute( QStringLiteral( "memoryCache" ) ).toInt() );
319+
info.setDynamicFormEnabled( infoElem.attribute( QStringLiteral( "dynamicForm" ) ).toInt() );
318320

319321
QDomElement subsetElem = infoElem.firstChildElement( QStringLiteral( "joinFieldsSubset" ) );
320322
if ( !subsetElem.isNull() )
@@ -392,6 +394,44 @@ const QgsVectorLayerJoinInfo *QgsVectorLayerJoinBuffer::joinForFieldIndex( int i
392394
return &( mVectorJoins[sourceJoinIndex] );
393395
}
394396

397+
QList<const QgsVectorLayerJoinInfo *> QgsVectorLayerJoinBuffer::joinsWhereFieldIsId( const QgsField &field ) const
398+
{
399+
QList<const QgsVectorLayerJoinInfo *> infos;
400+
401+
Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mVectorJoins )
402+
{
403+
if ( infos.contains( &info ) )
404+
continue;
405+
406+
if ( info.targetFieldName() == field.name() )
407+
infos.append( &info );
408+
}
409+
410+
return infos;
411+
}
412+
413+
QgsFeature QgsVectorLayerJoinBuffer::joinedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const
414+
{
415+
QgsFeature joinedFeature;
416+
417+
if ( info->joinLayer() )
418+
{
419+
const QVariant targetValue = feature.attribute( info->targetFieldName() );
420+
QString fieldRef = QgsExpression::quotedColumnRef( info->joinFieldName() );
421+
QString quotedVal = QgsExpression::quotedValue( targetValue.toString() );
422+
const QString filter = QStringLiteral( "%1 = %2" ).arg( fieldRef, quotedVal );
423+
424+
QgsFeatureRequest request;
425+
request.setFilterExpression( filter );
426+
request.setLimit( 1 );
427+
428+
QgsFeatureIterator it = info->joinLayer()->getFeatures( request );
429+
it.nextFeature( joinedFeature );
430+
}
431+
432+
return joinedFeature;
433+
}
434+
395435
QgsVectorLayerJoinBuffer *QgsVectorLayerJoinBuffer::clone() const
396436
{
397437
QgsVectorLayerJoinBuffer *cloned = new QgsVectorLayerJoinBuffer( mLayer );

‎src/core/qgsvectorlayerjoinbuffer.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ class CORE_EXPORT QgsVectorLayerJoinBuffer : public QObject
8484
//! \since QGIS 2.6
8585
static QVector<int> joinSubsetIndices( QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset );
8686

87+
/** Returns joins where the field of a target layer is considered as an id.
88+
* \param field the field of a target layer
89+
* \returns a list of vector joins
90+
* \since QGIS3.0
91+
*/
92+
QList<const QgsVectorLayerJoinInfo *> joinsWhereFieldIsId( const QgsField &field ) const;
93+
94+
/** Returns the joined feature corresponding to the feature.
95+
* \param info the vector join information
96+
* \param feature the feature of the target layer
97+
* \since QGIS 3.0
98+
*/
99+
QgsFeature joinedFeatureOf( const QgsVectorLayerJoinInfo *info, const QgsFeature &feature ) const;
100+
87101
//! Create a copy of the join buffer
88102
//! \since QGIS 2.6
89103
QgsVectorLayerJoinBuffer *clone() const SIP_FACTORY;

‎src/core/qgsvectorlayerjoininfo.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/***************************************************************************
2+
qgsvectorlayerjoininfo.cpp
3+
--------------------------
4+
begin : Jun 29, 2017
5+
copyright : (C) 2017 by Paul Blottiere
6+
email : paul.blottiere@oslandia.com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsvectorlayerjoininfo.h"
19+
20+
QString QgsVectorLayerJoinInfo::prefixedFieldName( const QgsField &f ) const
21+
{
22+
QString name;
23+
24+
if ( joinLayer() )
25+
{
26+
if ( prefix().isNull() )
27+
name = joinLayer()->name() + '_';
28+
else
29+
name = prefix();
30+
31+
name += f.name();
32+
}
33+
34+
return name;
35+
}

‎src/core/qgsvectorlayerjoininfo.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,25 @@ class CORE_EXPORT QgsVectorLayerJoinInfo
6868
//! Returns whether values from the joined layer should be cached in memory to speed up lookups
6969
bool isUsingMemoryCache() const { return mMemoryCache; }
7070

71+
/** Returns whether the form has to be dynamically updated with joined fields
72+
* when a feature is being created in the target layer.
73+
* \since QGIS 3.0
74+
*/
75+
bool isDynamicFormEnabled() const { return mDynamicForm; }
76+
77+
/** Sets whether the form has to be dynamically updated with joined fields
78+
* when a feature is being created in the target layer.
79+
* \since QGIS 3.0
80+
*/
81+
void setDynamicFormEnabled( bool enabled ) { mDynamicForm = enabled; }
82+
83+
/** Returns the prefixed name of the field.
84+
* \param field the field
85+
* \returns the prefixed name of the field
86+
* \since QGIS 3.0
87+
*/
88+
QString prefixedFieldName( const QgsField &field ) const;
89+
7190
bool operator==( const QgsVectorLayerJoinInfo &other ) const
7291
{
7392
return mTargetFieldName == other.mTargetFieldName &&
@@ -113,6 +132,8 @@ class CORE_EXPORT QgsVectorLayerJoinInfo
113132
//! True if the cached join attributes need to be updated
114133
bool cacheDirty;
115134

135+
bool mDynamicForm;
136+
116137
//! Cache for joined attributes to provide fast lookup (size is 0 if no memory caching)
117138
QHash< QString, QgsAttributes> cachedAttributes;
118139

‎src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,8 @@ bool QgsEditorWidgetWrapper::isInTable( const QWidget *parent )
211211
if ( qobject_cast<const QTableView *>( parent ) ) return true;
212212
return isInTable( parent->parentWidget() );
213213
}
214+
215+
void QgsEditorWidgetWrapper::setHint( const QString &hintText )
216+
{
217+
widget()->setToolTip( hintText );
218+
}

‎src/gui/editorwidgets/core/qgseditorwidgetwrapper.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
164164
*/
165165
QString constraintFailureReason() const;
166166

167+
/**
168+
* Add a hint text on the widget
169+
* \param hintText The hint text to display
170+
* \since QGIS 3.0
171+
*/
172+
virtual void setHint( const QString &hintText );
173+
167174
signals:
168175

169176
/**

‎src/gui/editorwidgets/qgstexteditwrapper.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,16 @@ void QgsTextEditWrapper::setWidgetValue( const QVariant &val )
240240
if ( mLineEdit )
241241
mLineEdit->setText( v );
242242
}
243+
244+
void QgsTextEditWrapper::setHint( const QString &hintText )
245+
{
246+
if ( hintText.isNull() )
247+
mPlaceholderText = mPlaceholderTextBackup;
248+
else
249+
{
250+
mPlaceholderTextBackup = mPlaceholderText;
251+
mPlaceholderText = hintText;
252+
}
253+
254+
mLineEdit->setPlaceholderText( mPlaceholderText );
255+
}

‎src/gui/editorwidgets/qgstexteditwrapper.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ class GUI_EXPORT QgsTextEditWrapper : public QgsEditorWidgetWrapper
4747
QVariant value() const override;
4848
void showIndeterminateState() override;
4949

50+
/**
51+
* Add a hint text on the widget
52+
* \param hintText The hint text to display
53+
* \since QGIS 3.0
54+
*/
55+
void setHint( const QString &hintText ) override;
56+
5057
protected:
5158
QWidget *createWidget( QWidget *parent ) override;
5259
void initWidget( QWidget *editor ) override;
@@ -66,6 +73,7 @@ class GUI_EXPORT QgsTextEditWrapper : public QgsEditorWidgetWrapper
6673
QPalette mReadOnlyPalette;
6774
QPalette mWritablePalette;
6875
QString mPlaceholderText;
76+
QString mPlaceholderTextBackup;
6977

7078
void setWidgetValue( const QVariant &value );
7179
};

‎src/gui/qgsattributeform.cpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include "qgssettings.h"
3434
#include "qgsscrollarea.h"
3535
#include "qgsgui.h"
36+
#include "qgsvectorlayerjoinbuffer.h"
37+
#include "qgsvectorlayerutils.h"
3638

3739
#include <QDir>
3840
#include <QTextStream>
@@ -221,14 +223,15 @@ void QgsAttributeForm::setMode( QgsAttributeForm::Mode mode )
221223
emit modeChanged( mMode );
222224
}
223225

224-
void QgsAttributeForm::changeAttribute( const QString &field, const QVariant &value )
226+
void QgsAttributeForm::changeAttribute( const QString &field, const QVariant &value, const QString &hintText )
225227
{
226228
Q_FOREACH ( QgsWidgetWrapper *ww, mWidgets )
227229
{
228230
QgsEditorWidgetWrapper *eww = qobject_cast<QgsEditorWidgetWrapper *>( ww );
229231
if ( eww && eww->field().name() == field )
230232
{
231233
eww->setValue( value );
234+
eww->setHint( hintText );
232235
}
233236
}
234237
}
@@ -659,6 +662,9 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value )
659662
{
660663
emit attributeChanged( eww->field().name(), value );
661664
}
665+
666+
updateJoinedFields( *eww );
667+
662668
break;
663669
}
664670
case MultiEditMode:
@@ -1930,3 +1936,58 @@ void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext *expres
19301936
isVisible = newVisibility;
19311937
}
19321938
}
1939+
1940+
void QgsAttributeForm::updateJoinedFields( const QgsEditorWidgetWrapper &eww )
1941+
{
1942+
QgsFeature formFeature;
1943+
QgsField field = eww.layer()->fields().field( eww.fieldIdx() );
1944+
QList<const QgsVectorLayerJoinInfo *> infos = eww.layer()->joinBuffer()->joinsWhereFieldIsId( field );
1945+
1946+
if ( infos.count() == 0 || !currentFormFeature( formFeature ) )
1947+
return;
1948+
1949+
const QString hint = tr( "No feature joined" );
1950+
Q_FOREACH ( const QgsVectorLayerJoinInfo *info, infos )
1951+
{
1952+
if ( !info->isDynamicFormEnabled() )
1953+
continue;
1954+
1955+
QgsFeature joinFeature = mLayer->joinBuffer()->joinedFeatureOf( info, formFeature );
1956+
1957+
QStringList *subsetFields = info->joinFieldNamesSubset();
1958+
if ( subsetFields )
1959+
{
1960+
Q_FOREACH ( const QString &field, *subsetFields )
1961+
{
1962+
QString prefixedName = info->prefixedFieldName( field );
1963+
QVariant val;
1964+
QString hintText = hint;
1965+
1966+
if ( joinFeature.isValid() )
1967+
{
1968+
val = joinFeature.attribute( field );
1969+
hintText.clear();
1970+
}
1971+
1972+
changeAttribute( prefixedName, val, hintText );
1973+
}
1974+
}
1975+
else
1976+
{
1977+
Q_FOREACH ( const QgsField &field, joinFeature.fields() )
1978+
{
1979+
QString prefixedName = info->prefixedFieldName( field );
1980+
QVariant val;
1981+
QString hintText = hint;
1982+
1983+
if ( joinFeature.isValid() )
1984+
{
1985+
val = joinFeature.attribute( field.name() );
1986+
hintText.clear();
1987+
}
1988+
1989+
changeAttribute( prefixedName, val, hintText );
1990+
}
1991+
}
1992+
}
1993+
}

‎src/gui/qgsattributeform.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
209209
* \param field The field to change
210210
* \param value The new value
211211
*/
212-
void changeAttribute( const QString &field, const QVariant &value );
212+
void changeAttribute( const QString &field, const QVariant &value, const QString &hintText = QString() );
213213

214214
/**
215215
* Update all editors to correspond to a different feature.
@@ -273,6 +273,8 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
273273

274274
void initPython();
275275

276+
void updateJoinedFields( const QgsEditorWidgetWrapper &eww );
277+
276278
struct WidgetInfo
277279
{
278280
WidgetInfo()

‎src/ui/qgsjoindialogbase.ui

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
<item row="2" column="1">
4545
<widget class="QgsFieldComboBox" name="mTargetFieldComboBox"/>
4646
</item>
47-
<item row="5" column="0" colspan="2">
47+
<item row="6" column="0" colspan="2">
4848
<widget class="QgsCollapsibleGroupBox" name="mUseJoinFieldsSubset">
4949
<property name="title">
50-
<string>Choose which fields are joined</string>
50+
<string>Choose which fields are &amp;joined</string>
5151
</property>
5252
<property name="checkable">
5353
<bool>true</bool>
@@ -65,10 +65,10 @@
6565
</layout>
6666
</widget>
6767
</item>
68-
<item row="6" column="0" colspan="2">
68+
<item row="7" column="0" colspan="2">
6969
<widget class="QgsCollapsibleGroupBox" name="mUseCustomPrefix">
7070
<property name="title">
71-
<string>Custom field name prefix</string>
71+
<string>Custom field &amp;name prefix</string>
7272
</property>
7373
<property name="checkable">
7474
<bool>true</bool>
@@ -86,7 +86,7 @@
8686
</layout>
8787
</widget>
8888
</item>
89-
<item row="7" column="0">
89+
<item row="8" column="0">
9090
<spacer name="verticalSpacer">
9191
<property name="orientation">
9292
<enum>Qt::Vertical</enum>
@@ -116,7 +116,7 @@
116116
</property>
117117
</widget>
118118
</item>
119-
<item row="8" column="0" colspan="2">
119+
<item row="9" column="0" colspan="2">
120120
<widget class="QDialogButtonBox" name="buttonBox">
121121
<property name="orientation">
122122
<enum>Qt::Horizontal</enum>
@@ -126,6 +126,13 @@
126126
</property>
127127
</widget>
128128
</item>
129+
<item row="5" column="0">
130+
<widget class="QCheckBox" name="mDynamicFormCheckBox">
131+
<property name="text">
132+
<string>Dynamic form</string>
133+
</property>
134+
</widget>
135+
</item>
129136
</layout>
130137
</widget>
131138
<customwidgets>

‎src/ui/qgsvectorlayerpropertiesbase.ui

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@
303303
</sizepolicy>
304304
</property>
305305
<property name="currentIndex">
306-
<number>0</number>
306+
<number>9</number>
307307
</property>
308308
<widget class="QWidget" name="mOptsPage_Information">
309309
<layout class="QVBoxLayout" name="verticalLayout_5">
@@ -379,8 +379,8 @@
379379
<rect>
380380
<x>0</x>
381381
<y>0</y>
382-
<width>639</width>
383-
<height>592</height>
382+
<width>653</width>
383+
<height>542</height>
384384
</rect>
385385
</property>
386386
<layout class="QVBoxLayout" name="verticalLayout_13">
@@ -831,7 +831,7 @@ border-radius: 2px;</string>
831831
<x>0</x>
832832
<y>0</y>
833833
<width>653</width>
834-
<height>536</height>
834+
<height>542</height>
835835
</rect>
836836
</property>
837837
<layout class="QVBoxLayout" name="verticalLayout_9">
@@ -1069,7 +1069,7 @@ border-radius: 2px;</string>
10691069
<x>0</x>
10701070
<y>0</y>
10711071
<width>653</width>
1072-
<height>536</height>
1072+
<height>542</height>
10731073
</rect>
10741074
</property>
10751075
<layout class="QVBoxLayout" name="verticalLayout_18">
@@ -1170,7 +1170,7 @@ border-radius: 2px;</string>
11701170
<x>0</x>
11711171
<y>0</y>
11721172
<width>653</width>
1173-
<height>536</height>
1173+
<height>542</height>
11741174
</rect>
11751175
</property>
11761176
<layout class="QVBoxLayout" name="verticalLayout_20">
@@ -1229,8 +1229,8 @@ border-radius: 2px;</string>
12291229
<rect>
12301230
<x>0</x>
12311231
<y>0</y>
1232-
<width>671</width>
1233-
<height>522</height>
1232+
<width>653</width>
1233+
<height>542</height>
12341234
</rect>
12351235
</property>
12361236
<layout class="QVBoxLayout" name="verticalLayout_32">
@@ -1249,7 +1249,7 @@ border-radius: 2px;</string>
12491249
<item>
12501250
<widget class="QgsCollapsibleGroupBox" name="mScaleVisibilityGroupBox">
12511251
<property name="title">
1252-
<string>Scale dependent visibility</string>
1252+
<string>Scale dependen&amp;t visibility</string>
12531253
</property>
12541254
<property name="checkable">
12551255
<bool>true</bool>
@@ -1268,7 +1268,7 @@ border-radius: 2px;</string>
12681268
<item>
12691269
<widget class="QGroupBox" name="mSimplifyDrawingGroupBox">
12701270
<property name="title">
1271-
<string>Simplify geometry</string>
1271+
<string>Simplify &amp;geometry</string>
12721272
</property>
12731273
<property name="checkable">
12741274
<bool>true</bool>
@@ -1590,7 +1590,7 @@ border-radius: 2px;</string>
15901590
<x>0</x>
15911591
<y>0</y>
15921592
<width>653</width>
1593-
<height>536</height>
1593+
<height>542</height>
15941594
</rect>
15951595
</property>
15961596
<layout class="QVBoxLayout" name="verticalLayout_21">
@@ -1656,7 +1656,7 @@ border-radius: 2px;</string>
16561656
<x>0</x>
16571657
<y>0</y>
16581658
<width>653</width>
1659-
<height>536</height>
1659+
<height>542</height>
16601660
</rect>
16611661
</property>
16621662
<layout class="QVBoxLayout" name="verticalLayout_23">
@@ -1675,7 +1675,7 @@ border-radius: 2px;</string>
16751675
<item>
16761676
<widget class="QTreeWidget" name="mJoinTreeWidget">
16771677
<property name="columnCount">
1678-
<number>6</number>
1678+
<number>7</number>
16791679
</property>
16801680
<column>
16811681
<property name="text">
@@ -1697,6 +1697,11 @@ border-radius: 2px;</string>
16971697
<string>Memory cache</string>
16981698
</property>
16991699
</column>
1700+
<column>
1701+
<property name="text">
1702+
<string>Dynamic form</string>
1703+
</property>
1704+
</column>
17001705
<column>
17011706
<property name="text">
17021707
<string>Prefix</string>

‎tests/src/gui/testqgsattributeform.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <qgsvectorlayer.h>
2525
#include "qgsvectordataprovider.h"
2626
#include <qgsfeature.h>
27+
#include <qgsvectorlayerjoininfo.h>
2728
#include "qgsgui.h"
2829

2930
class TestQgsAttributeForm : public QObject
@@ -41,6 +42,7 @@ class TestQgsAttributeForm : public QObject
4142
void testFieldConstraint();
4243
void testFieldMultiConstraints();
4344
void testOKButtonStatus();
45+
void testDynamicForm();
4446
};
4547

4648
void TestQgsAttributeForm::initTestCase()
@@ -306,5 +308,137 @@ void TestQgsAttributeForm::testOKButtonStatus()
306308
QVERIFY( !okButton->isEnabled() );
307309
}
308310

311+
void TestQgsAttributeForm::testDynamicForm()
312+
{
313+
// make temporary layers
314+
QString defA = QStringLiteral( "Point?field=id_a:integer" );
315+
QgsVectorLayer *layerA = new QgsVectorLayer( defA, QStringLiteral( "layerA" ), QStringLiteral( "memory" ) );
316+
317+
QString defB = QStringLiteral( "Point?field=id_b:integer&field=col0:integer" );
318+
QgsVectorLayer *layerB = new QgsVectorLayer( defB, QStringLiteral( "layerB" ), QStringLiteral( "memory" ) );
319+
320+
QString defC = QStringLiteral( "Point?field=id_c:integer&field=col0:integer" );
321+
QgsVectorLayer *layerC = new QgsVectorLayer( defC, QStringLiteral( "layerC" ), QStringLiteral( "memory" ) );
322+
323+
// join configuration
324+
QgsVectorLayerJoinInfo infoJoinAB;
325+
infoJoinAB.setTargetFieldName( "id_a" );
326+
infoJoinAB.setJoinLayer( layerB );
327+
infoJoinAB.setJoinFieldName( "id_b" );
328+
infoJoinAB.setDynamicFormEnabled( true );
329+
330+
layerA->addJoin( infoJoinAB );
331+
332+
QgsVectorLayerJoinInfo infoJoinAC;
333+
infoJoinAC.setTargetFieldName( "id_a" );
334+
infoJoinAC.setJoinLayer( layerC );
335+
infoJoinAC.setJoinFieldName( "id_c" );
336+
infoJoinAC.setDynamicFormEnabled( true );
337+
338+
layerA->addJoin( infoJoinAC );
339+
340+
// add features for main layer
341+
QgsFeature ftA( layerA->fields() );
342+
ftA.setAttribute( QStringLiteral( "id_a" ), 0 );
343+
layerA->startEditing();
344+
layerA->addFeature( ftA );
345+
layerA->commitChanges();
346+
347+
// add features for joined layers
348+
QgsFeature ft0B( layerB->fields() );
349+
ft0B.setAttribute( QStringLiteral( "id_b" ), 30 );
350+
ft0B.setAttribute( QStringLiteral( "col0" ), 10 );
351+
layerB->startEditing();
352+
layerB->addFeature( ft0B );
353+
layerB->commitChanges();
354+
355+
QgsFeature ft1B( layerB->fields() );
356+
ft1B.setAttribute( QStringLiteral( "id_b" ), 31 );
357+
ft1B.setAttribute( QStringLiteral( "col0" ), 11 );
358+
layerB->startEditing();
359+
layerB->addFeature( ft1B );
360+
layerB->commitChanges();
361+
362+
QgsFeature ft0C( layerC->fields() );
363+
ft0C.setAttribute( QStringLiteral( "id_c" ), 32 );
364+
ft0C.setAttribute( QStringLiteral( "col0" ), 12 );
365+
layerC->startEditing();
366+
layerC->addFeature( ft0C );
367+
layerC->commitChanges();
368+
369+
QgsFeature ft1C( layerC->fields() );
370+
ft1C.setAttribute( QStringLiteral( "id_c" ), 31 );
371+
ft1C.setAttribute( QStringLiteral( "col0" ), 13 );
372+
layerC->startEditing();
373+
layerC->addFeature( ft1C );
374+
layerC->commitChanges();
375+
376+
// build a form with feature A
377+
QgsAttributeForm form( layerA );
378+
form.setMode( QgsAttributeForm::AddFeatureMode );
379+
form.setFeature( ftA );
380+
381+
// test that there's no joined feature by default
382+
QgsEditorWidgetWrapper *ww = nullptr;
383+
384+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[1] );
385+
QCOMPARE( ww->field().name(), QString( "layerB_col0" ) );
386+
QCOMPARE( ww->value(), QVariant( QVariant::Int ) );
387+
388+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[2] );
389+
QCOMPARE( ww->field().name(), QString( "layerC_col0" ) );
390+
QCOMPARE( ww->value(), QVariant( QVariant::Int ) );
391+
392+
// change layerA join id field to join with layerB
393+
form.changeAttribute( "id_a", QVariant( 30 ) );
394+
395+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[0] );
396+
QCOMPARE( ww->field().name(), QString( "id_a" ) );
397+
QCOMPARE( ww->value(), QVariant( 30 ) );
398+
399+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[1] );
400+
QCOMPARE( ww->field().name(), QString( "layerB_col0" ) );
401+
QCOMPARE( ww->value(), QVariant( 10 ) );
402+
403+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[2] );
404+
QCOMPARE( ww->field().name(), QString( "layerC_col0" ) );
405+
QCOMPARE( ww->value(), QVariant( QVariant::Int ) );
406+
407+
// change layerA join id field to join with layerC
408+
form.changeAttribute( "id_a", QVariant( 32 ) );
409+
410+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[0] );
411+
QCOMPARE( ww->field().name(), QString( "id_a" ) );
412+
QCOMPARE( ww->value(), QVariant( 32 ) );
413+
414+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[1] );
415+
QCOMPARE( ww->field().name(), QString( "layerB_col0" ) );
416+
QCOMPARE( ww->value(), QVariant( QVariant::Int ) );
417+
418+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[2] );
419+
QCOMPARE( ww->field().name(), QString( "layerC_col0" ) );
420+
QCOMPARE( ww->value(), QVariant( 12 ) );
421+
422+
// change layerA join id field to join with layerA and layerC
423+
form.changeAttribute( "id_a", QVariant( 31 ) );
424+
425+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[0] );
426+
QCOMPARE( ww->field().name(), QString( "id_a" ) );
427+
QCOMPARE( ww->value(), QVariant( 31 ) );
428+
429+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[1] );
430+
QCOMPARE( ww->field().name(), QString( "layerB_col0" ) );
431+
QCOMPARE( ww->value(), QVariant( 11 ) );
432+
433+
ww = qobject_cast<QgsEditorWidgetWrapper *>( form.mWidgets[2] );
434+
QCOMPARE( ww->field().name(), QString( "layerC_col0" ) );
435+
QCOMPARE( ww->value(), QVariant( 13 ) );
436+
437+
// clean
438+
delete layerA;
439+
delete layerB;
440+
delete layerC;
441+
}
442+
309443
QGSTEST_MAIN( TestQgsAttributeForm )
310444
#include "testqgsattributeform.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.