QGIS API Documentation 3.43.0-Master (b60ef06885e)
qgsrelationreferencewidgetwrapper.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrelationreferencewidgetwrapper.cpp
3 --------------------------------------
4 Date : 20.4.2013
5 Copyright : (C) 2013 Matthias Kuhn
6 Email : matthias at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
18#include "moc_qgsrelationreferencewidgetwrapper.cpp"
19#include "qgsproject.h"
20#include "qgsrelationmanager.h"
22#include "qgsattributeform.h"
24
25QgsRelationReferenceWidgetWrapper::QgsRelationReferenceWidgetWrapper( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent )
26 : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
27 , mCanvas( canvas )
28 , mMessageBar( messageBar )
29 , mIndeterminateState( false )
30{
31}
32
34{
35 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
36 if ( form )
38
40 return w;
41}
42
44{
45 QgsRelationReferenceWidget *w = qobject_cast<QgsRelationReferenceWidget *>( editor );
46 if ( !w )
47 {
48 w = new QgsRelationReferenceWidget( editor );
49 }
50
51 mWidget = w;
52
53 const QgsAttributeEditorContext *ctx = &context();
54
55 mWidget->setEditorContext( *ctx, mCanvas, mMessageBar );
56
57 const bool showForm = config( QStringLiteral( "ShowForm" ), false ).toBool();
58 const bool mapIdent = config( QStringLiteral( "MapIdentification" ), false ).toBool();
59 const bool readOnlyWidget = config( QStringLiteral( "ReadOnly" ), false ).toBool();
60 const bool showOpenFormButton = config( QStringLiteral( "ShowOpenFormButton" ), true ).toBool();
61
62 mWidget->setEmbedForm( showForm );
63 mWidget->setReadOnlySelector( readOnlyWidget );
64 mWidget->setAllowMapIdentification( mapIdent );
65 mWidget->setOpenFormButtonVisible( showOpenFormButton );
66
67 const bool fetchLimitActive = config( QStringLiteral( "FetchLimitActive" ), QgsSettings().value( QStringLiteral( "maxEntriesRelationWidget" ), 100, QgsSettings::Gui ).toInt() > 0 ).toBool();
68 if ( fetchLimitActive )
69 {
70 mWidget->setFetchLimit( config( QStringLiteral( "FetchLimitNumber" ), QgsSettings().value( QStringLiteral( "maxEntriesRelationWidget" ), 100, QgsSettings::Gui ) ).toInt() );
71 }
72
73 if ( config( QStringLiteral( "FilterFields" ), QVariant() ).isValid() )
74 {
75 mWidget->setFilterFields( config( QStringLiteral( "FilterFields" ) ).toStringList() );
76 mWidget->setChainFilters( config( QStringLiteral( "ChainFilters" ) ).toBool() );
77 }
78 if ( !config( QStringLiteral( "FilterExpression" ) ).toString().isEmpty() )
79 {
80 mWidget->setFilterExpression( config( QStringLiteral( "FilterExpression" ) ).toString() );
81 mWidget->setFormFeature( formFeature() );
82 mWidget->setParentFormFeature( ctx->parentFormFeature() );
83 }
84 mWidget->setAllowAddFeatures( config( QStringLiteral( "AllowAddFeatures" ), false ).toBool() );
85
86 const QVariant relationName = config( QStringLiteral( "Relation" ) );
87
88 // Store relation data source and provider key
89 mWidget->setReferencedLayerDataSource( config( QStringLiteral( "ReferencedLayerDataSource" ) ).toString() );
90 mWidget->setReferencedLayerProviderKey( config( QStringLiteral( "ReferencedLayerProviderKey" ) ).toString() );
91 mWidget->setReferencedLayerId( config( QStringLiteral( "ReferencedLayerId" ) ).toString() );
92 mWidget->setReferencedLayerName( config( QStringLiteral( "ReferencedLayerName" ) ).toString() );
93
94 QgsRelation relation; // invalid relation by default
95 if ( relationName.isValid() )
96 relation = QgsProject::instance()->relationManager()->relation( relationName.toString() );
97 if ( !relation.isValid() && !layer()->referencingRelations( fieldIdx() ).isEmpty() )
98 relation = layer()->referencingRelations( fieldIdx() )[0];
99
100 // If this widget is already embedded by the same relation, reduce functionality
101 do
102 {
103 if ( ctx->relation().id() == relation.id() )
104 {
105 mWidget->setEmbedForm( false );
106 mWidget->setReadOnlySelector( true );
107 mWidget->setAllowMapIdentification( false );
108 mWidget->setOpenFormButtonVisible( false );
109 mWidget->setAllowAddFeatures( false );
110 break;
111 }
112 ctx = ctx->parentContext();
113 } while ( ctx );
114
115 // If AllowNULL is not set in the config, provide a default value based on the
116 // constraints of the referencing fields
117 if ( !config( QStringLiteral( "AllowNULL" ) ).isValid() )
118 {
119 mWidget->setRelation( relation, relation.referencingFieldsAllowNull() );
120 }
121 else
122 {
123 mWidget->setRelation( relation, config( QStringLiteral( "AllowNULL" ) ).toBool() );
124 }
125
126 connect( mWidget, &QgsRelationReferenceWidget::foreignKeysChanged, this, &QgsRelationReferenceWidgetWrapper::foreignKeysChanged );
127}
128
129void QgsRelationReferenceWidgetWrapper::aboutToSave()
130{
131 // Save changes in the embedded form
133}
134
136{
137 if ( !mWidget )
138 return QgsVariantUtils::createNullVariant( field().type() );
139
140 const QVariantList fkeys = mWidget->foreignKeys();
141
142 if ( fkeys.isEmpty() )
143 {
144 return QgsVariantUtils::createNullVariant( field().type() );
145 }
146 else
147 {
148 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
149 Q_ASSERT( fieldPairs.count() == fkeys.count() );
150 for ( int i = 0; i < fieldPairs.count(); i++ )
151 {
152 if ( fieldPairs.at( i ).referencingField() == field().name() )
153 return fkeys.at( i );
154 }
155 return QgsVariantUtils::createNullVariant( field().type() ); // should not happen
156 }
157}
158
160{
161 return mWidget;
162}
163
165{
166 if ( mWidget )
167 {
168 mWidget->showIndeterminateState();
169 }
170 mIndeterminateState = true;
171}
172
174{
175 if ( !mWidget )
176 return {};
177
178 if ( !mWidget->relation().isValid() )
179 {
180 QVariantList values;
181 for ( int i = 0; i < mWidget->relation().fieldPairs().count(); i++ )
182 {
183 values << QVariant();
184 }
185 return values;
186 }
187 else
188 {
189 QVariantList values = mWidget->foreignKeys();
190 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
191 const int fieldCount = std::min( fieldPairs.count(), values.count() );
192 for ( int i = 0; i < fieldCount; i++ )
193 {
194 if ( fieldPairs.at( i ).referencingField() == field().name() )
195 {
196 values.removeAt( i );
197 break;
198 }
199 }
200 return values;
201 }
202}
203
205{
206 if ( !mWidget || !mWidget->relation().isValid() )
207 return QStringList();
208
209 QStringList fields;
210 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
211 for ( int i = 0; i < fieldPairs.count(); i++ )
212 {
213 if ( fieldPairs.at( i ).referencingField() == field().name() )
214 continue;
215
216 fields << fieldPairs.at( i ).referencingField();
217 }
218 return fields;
219}
220
221void QgsRelationReferenceWidgetWrapper::updateValues( const QVariant &val, const QVariantList &additionalValues )
222{
223 if ( !mWidget || ( !mIndeterminateState && val == value() && QgsVariantUtils::isNull( val ) == QgsVariantUtils::isNull( value() ) ) )
224 return;
225
226 mIndeterminateState = false;
227
228 QVariantList values = additionalValues;
229 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
230 for ( int i = 0; i < fieldPairs.count(); i++ )
231 {
232 if ( fieldPairs.at( i ).referencingField() == field().name() )
233 {
234 values.insert( i, val );
235 break;
236 }
237 }
238 Q_ASSERT( values.count() == fieldPairs.count() );
239
240 mBlockChanges++;
241 mWidget->setForeignKeys( values );
242 mWidget->setFormFeature( formFeature() );
243 mBlockChanges--;
244}
245
247{
248 if ( !mWidget )
249 return;
250
251 mWidget->setRelationEditable( enabled );
252}
253
254void QgsRelationReferenceWidgetWrapper::foreignKeysChanged( const QVariantList &values )
255{
256 if ( mBlockChanges != 0 ) // initial value is being set, we can ignore this signal
257 return;
258
259 QVariant mainValue = QgsVariantUtils::createNullVariant( field().type() );
260
261 if ( !mWidget || !mWidget->relation().isValid() )
262 {
264 emit valueChanged( mainValue );
266 emit valuesChanged( mainValue );
267 return;
268 }
269
270 QVariantList additionalValues = values;
271 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
272 for ( int i = 0; i < fieldPairs.count(); i++ )
273 {
274 if ( fieldPairs.at( i ).referencingField() == field().name() )
275 mainValue = additionalValues.takeAt( i ); // additional values in field pair order remain
276 }
277 Q_ASSERT( additionalValues.count() == values.count() - 1 );
278
280 emit valueChanged( mainValue );
282 emit valuesChanged( mainValue, additionalValues );
283}
284
286{
287 if ( mWidget )
288 {
290 {
291 widget()->setStyleSheet( QString() );
292 }
293 else
294 {
295 switch ( constraintResult() )
296 {
298 mWidget->setStyleSheet( QString() );
299 break;
300
302 mWidget->setStyleSheet( QStringLiteral( ".QComboBox { background-color: #dd7777; }" ) );
303 break;
304
306 mWidget->setStyleSheet( QStringLiteral( ".QComboBox { background-color: #ffd85d; }" ) );
307 break;
308 }
309 }
310 }
311}
312
313void QgsRelationReferenceWidgetWrapper::parentFormValueChanged( const QString &attribute, const QVariant &value )
314{
315 // Update the parent feature in the context ( which means to replace the whole context :/ )
317 QgsFeature feature { context().parentFormFeature() };
318 feature.setAttribute( attribute, value );
319 ctx.setParentFormFeature( feature );
320 setContext( ctx );
321
322 // Check if the change might affect the filter expression and the cache needs updates
324 && QgsValueRelationFieldFormatter::expressionParentFormAttributes( mExpression ).contains( attribute ) )
325 {
326 mWidget->setParentFormFeature( context().parentFormFeature() );
327 }
328}
329
330void QgsRelationReferenceWidgetWrapper::widgetValueChanged( const QString &attribute, const QVariant &newValue, bool attributeChanged )
331{
332 if ( attributeChanged )
333 {
334 setFormFeatureAttribute( attribute, newValue );
337 {
338 mWidget->setFormFeature( formFeature() );
339 }
340 }
341}
Contains context information for attribute editor widgets.
QgsFeature parentFormFeature() const
Returns the feature of the currently edited parent form in its actual state.
const QgsAttributeEditorContext * parentContext() const
const QgsRelation & relation() const
Returns the attribute relation.
The attribute form widget for vector layer features.
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
Manages an editor widget.
QgsFeature formFeature() const
The feature currently being edited, in its current state.
Q_DECL_DEPRECATED void valueChanged(const QVariant &value)
Emit this signal, whenever the value changed.
int fieldIdx() const
Access the field index.
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues=QVariantList())
Emit this signal, whenever the value changed.
bool setFormFeatureAttribute(const QString &attributeName, const QVariant &attributeValue)
Update the feature currently being edited by changing its attribute attributeName to attributeValue.
QgsField field() const
Access the field.
@ ConstraintResultFailSoft
Widget failed at least one soft (non-enforced) constraint.
@ ConstraintResultPass
Widget passed constraints successfully.
@ ConstraintResultFailHard
Widget failed at least one hard (enforced) constraint.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Map canvas is a class for displaying all GIS data types on a canvas.
A bar for displaying non-blocking messages to the user.
QgsRelationManager * relationManager
Definition qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
bool valid() const override
Returns true if the widget has been properly initialized.
QVariant value() const override
Will be used to access the widget's value.
void widgetValueChanged(const QString &attribute, const QVariant &newValue, bool attributeChanged)
Will be called when a value in the current edited form or table row changes.
void showIndeterminateState() override
Sets the widget to display in an indeterminate "mixed value" state.
QgsRelationReferenceWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent=nullptr)
Constructor for QgsRelationReferenceWidgetWrapper.
void updateConstraintWidgetStatus() override
This should update the widget with a visual cue if a constraint status changed.
QVariantList additionalFieldValues() const override
Will be used to access the widget's values for potential additional fields handled by the widget.
void initWidget(QWidget *editor) override
This method should initialize the editor widget with runtime data.
void parentFormValueChanged(const QString &attribute, const QVariant &value) override
QStringList additionalFields() const override
Returns the list of additional fields which the editor handles.
QWidget * createWidget(QWidget *parent) override
This method should create a new widget with the provided parent.
A widget which shows related features.
void setFilterExpression(const QString &filterExpression)
If not empty, will be used as filter expression.
QString filterExpression() const
Returns the currently set filter expression.
void setReferencedLayerProviderKey(const QString &referencedLayerProviderKey)
Set the data provider key of the referenced layer to referencedLayerProviderKey.
void setChainFilters(bool chainFilters)
Set if filters are chained.
void setEditorContext(const QgsAttributeEditorContext &context, QgsMapCanvas *canvas, QgsMessageBar *messageBar)
Sets the editor context.
void setReferencedLayerName(const QString &referencedLayerName)
Set the name of the referenced layer to referencedLayerName.
bool saveReferencedAttributeForm()
Trigger save of the embedded referenced attribute form.
void showIndeterminateState()
Sets the widget to display in an indeterminate "mixed value" state.
void setReferencedLayerDataSource(const QString &referencedLayerDataSource)
Set the public data source of the referenced layer to referencedLayerDataSource.
void setParentFormFeature(const QgsFeature &parentFormFeature)
Set the current parent form feature.
void setFilterFields(const QStringList &filterFields)
Sets the fields for which filter comboboxes will be created.
void setAllowMapIdentification(bool allowMapIdentification)
QgsRelation relation() const
Returns the current relation, which might be invalid.
void setReferencedLayerId(const QString &referencedLayerId)
Set the id of the referenced layer to referencedLayerId.
QVariantList foreignKeys() const
returns the related feature foreign key
void setForeignKeys(const QVariantList &values)
Sets the related feature using the foreign keys.
void foreignKeysChanged(const QVariantList &keys)
Emitted when the foreign keys changed.
void setRelation(const QgsRelation &relation, bool allowNullValue)
void setOpenFormButtonVisible(bool openFormButtonVisible)
void setAllowAddFeatures(bool allowAddFeatures)
Determines if a button for adding new features should be shown.
void setFetchLimit(int fetchLimit)
Set the limit of fetched features (0 means all features)
void setFormFeature(const QgsFeature &formFeature)
Set the current form feature (from the referencing layer)
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QString id
Definition qgsrelation.h:47
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
bool referencingFieldsAllowNull() const
Returns true if none of the referencing fields has a NOT NULL constraint.
Stores settings for use within QGIS.
Definition qgssettings.h:66
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
static bool expressionRequiresParentFormScope(const QString &expression)
Check if the expression requires a parent form scope (i.e.
static QSet< QString > expressionParentFormAttributes(const QString &expression)
Returns a list of attributes required by the parent form's form context expression.
static QSet< QString > expressionFormAttributes(const QString &expression)
Returns a list of attributes required by the form context expression.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
Represents a vector layer which manages a vector based dataset.
QList< QgsRelation > referencingRelations(int idx) const
Returns the layer's relations, where the foreign key is on this layer.
QWidget * widget()
Access the widget managed by this wrapper.
const QgsAttributeEditorContext & context() const
Returns information about the context in which this widget is shown.
QgsVectorLayer * layer() const
Returns the vector layer associated with the widget.
void setContext(const QgsAttributeEditorContext &context)
Set the context in which this widget is shown.
QVariantMap config() const
Returns the whole config.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6819
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6818