QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
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
19#include "qgsattributeform.h"
20#include "qgsproject.h"
21#include "qgsrelationmanager.h"
24
25#include "moc_qgsrelationreferencewidgetwrapper.cpp"
26
28 : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
29 , mCanvas( canvas )
30 , mMessageBar( messageBar )
31 , mIndeterminateState( false )
32{
33}
34
36{
37 QgsAttributeForm *form = qobject_cast<QgsAttributeForm *>( parent );
38 if ( form )
40
42 return w;
43}
44
46{
47 QgsRelationReferenceWidget *w = qobject_cast<QgsRelationReferenceWidget *>( editor );
48 if ( !w )
49 {
50 w = new QgsRelationReferenceWidget( editor );
51 }
52
53 mWidget = w;
54
55 const QgsAttributeEditorContext *ctx = &context();
56
57 mWidget->setEditorContext( *ctx, mCanvas, mMessageBar );
58
59 const bool showForm = config( QStringLiteral( "ShowForm" ), false ).toBool();
60 const bool mapIdent = config( QStringLiteral( "MapIdentification" ), false ).toBool();
61 const bool readOnlyWidget = config( QStringLiteral( "ReadOnly" ), false ).toBool();
62 const bool showOpenFormButton = config( QStringLiteral( "ShowOpenFormButton" ), true ).toBool();
63
64 mWidget->setEmbedForm( showForm );
65 mWidget->setReadOnlySelector( readOnlyWidget );
66 mWidget->setAllowMapIdentification( mapIdent );
67 mWidget->setOpenFormButtonVisible( showOpenFormButton );
68
69 const bool fetchLimitActive = config( QStringLiteral( "FetchLimitActive" ), QgsSettings().value( QStringLiteral( "maxEntriesRelationWidget" ), 100, QgsSettings::Gui ).toInt() > 0 ).toBool();
70 if ( fetchLimitActive )
71 {
72 mWidget->setFetchLimit( config( QStringLiteral( "FetchLimitNumber" ), QgsSettings().value( QStringLiteral( "maxEntriesRelationWidget" ), 100, QgsSettings::Gui ) ).toInt() );
73 }
74
75 if ( config( QStringLiteral( "FilterFields" ), QVariant() ).isValid() )
76 {
77 mWidget->setFilterFields( config( QStringLiteral( "FilterFields" ) ).toStringList() );
78 mWidget->setChainFilters( config( QStringLiteral( "ChainFilters" ) ).toBool() );
79 }
80 if ( !config( QStringLiteral( "FilterExpression" ) ).toString().isEmpty() )
81 {
82 mWidget->setFilterExpression( config( QStringLiteral( "FilterExpression" ) ).toString() );
83 mWidget->setFormFeature( formFeature() );
84 mWidget->setParentFormFeature( ctx->parentFormFeature() );
85 }
86 mWidget->setAllowAddFeatures( config( QStringLiteral( "AllowAddFeatures" ), false ).toBool() );
87
88 mWidget->setOrderExpression( config( QStringLiteral( "OrderExpression" ) ).toString() );
89 mWidget->setSortOrder( config( QStringLiteral( "OrderDescending" ), false ).toBool() ? Qt::DescendingOrder : Qt::AscendingOrder );
90
91 const QVariant relationName = config( QStringLiteral( "Relation" ) );
92
93 // Store relation data source and provider key
94 mWidget->setReferencedLayerDataSource( config( QStringLiteral( "ReferencedLayerDataSource" ) ).toString() );
95 mWidget->setReferencedLayerProviderKey( config( QStringLiteral( "ReferencedLayerProviderKey" ) ).toString() );
96 mWidget->setReferencedLayerId( config( QStringLiteral( "ReferencedLayerId" ) ).toString() );
97 mWidget->setReferencedLayerName( config( QStringLiteral( "ReferencedLayerName" ) ).toString() );
98
99 QgsRelation relation; // invalid relation by default
100 if ( relationName.isValid() )
101 relation = QgsProject::instance()->relationManager()->relation( relationName.toString() );
102 if ( !relation.isValid() && !layer()->referencingRelations( fieldIdx() ).isEmpty() )
103 relation = layer()->referencingRelations( fieldIdx() )[0];
104
105 // If this widget is already embedded by the same relation, reduce functionality
106 do
107 {
108 if ( ctx->relation().id() == relation.id() )
109 {
110 mWidget->setEmbedForm( false );
111 mWidget->setReadOnlySelector( true );
112 mWidget->setAllowMapIdentification( false );
113 mWidget->setOpenFormButtonVisible( false );
114 mWidget->setAllowAddFeatures( false );
115 break;
116 }
117 ctx = ctx->parentContext();
118 } while ( ctx );
119
120 // If AllowNULL is not set in the config, provide a default value based on the
121 // constraints of the referencing fields
122 if ( !config( QStringLiteral( "AllowNULL" ) ).isValid() )
123 {
124 mWidget->setRelation( relation, relation.referencingFieldsAllowNull() );
125 }
126 else
127 {
128 mWidget->setRelation( relation, config( QStringLiteral( "AllowNULL" ) ).toBool() );
129 }
130
131 connect( mWidget, &QgsRelationReferenceWidget::foreignKeysChanged, this, &QgsRelationReferenceWidgetWrapper::foreignKeysChanged );
132}
133
134void QgsRelationReferenceWidgetWrapper::aboutToSave()
135{
136 // Save changes in the embedded form
138}
139
141{
142 if ( !mWidget )
143 return QgsVariantUtils::createNullVariant( field().type() );
144
145 const QVariantList fkeys = mWidget->foreignKeys();
146
147 if ( fkeys.isEmpty() )
148 {
149 return QgsVariantUtils::createNullVariant( field().type() );
150 }
151 else
152 {
153 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
154 Q_ASSERT( fieldPairs.count() == fkeys.count() );
155 for ( int i = 0; i < fieldPairs.count(); i++ )
156 {
157 if ( fieldPairs.at( i ).referencingField() == field().name() )
158 return fkeys.at( i );
159 }
160 return QgsVariantUtils::createNullVariant( field().type() ); // should not happen
161 }
162}
163
165{
166 return mWidget;
167}
168
170{
171 if ( mWidget )
172 {
173 mWidget->showIndeterminateState();
174 }
175 mIndeterminateState = true;
176}
177
179{
180 if ( !mWidget )
181 return {};
182
183 if ( !mWidget->relation().isValid() )
184 {
185 QVariantList values;
186 for ( int i = 0; i < mWidget->relation().fieldPairs().count(); i++ )
187 {
188 values << QVariant();
189 }
190 return values;
191 }
192 else
193 {
194 QVariantList values = mWidget->foreignKeys();
195 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
196 const int fieldCount = std::min( fieldPairs.count(), values.count() );
197 for ( int i = 0; i < fieldCount; i++ )
198 {
199 if ( fieldPairs.at( i ).referencingField() == field().name() )
200 {
201 values.removeAt( i );
202 break;
203 }
204 }
205 return values;
206 }
207}
208
210{
211 if ( !mWidget || !mWidget->relation().isValid() )
212 return QStringList();
213
214 QStringList fields;
215 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
216 for ( int i = 0; i < fieldPairs.count(); i++ )
217 {
218 if ( fieldPairs.at( i ).referencingField() == field().name() )
219 continue;
220
221 fields << fieldPairs.at( i ).referencingField();
222 }
223 return fields;
224}
225
226void QgsRelationReferenceWidgetWrapper::updateValues( const QVariant &val, const QVariantList &additionalValues )
227{
228 if ( !mWidget || ( !mIndeterminateState && val == value() && QgsVariantUtils::isNull( val ) == QgsVariantUtils::isNull( value() ) ) )
229 return;
230
231 mIndeterminateState = false;
232
233 QVariantList values = additionalValues;
234 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
235 for ( int i = 0; i < fieldPairs.count(); i++ )
236 {
237 if ( fieldPairs.at( i ).referencingField() == field().name() )
238 {
239 values.insert( i, val );
240 break;
241 }
242 }
243 Q_ASSERT( values.count() == fieldPairs.count() );
244
245 mBlockChanges++;
246 mWidget->setForeignKeys( values );
247 mWidget->setFormFeature( formFeature() );
248 mBlockChanges--;
249}
250
252{
253 if ( !mWidget )
254 return;
255
256 mWidget->setRelationEditable( enabled );
257}
258
259void QgsRelationReferenceWidgetWrapper::foreignKeysChanged( const QVariantList &values )
260{
261 if ( mBlockChanges != 0 ) // initial value is being set, we can ignore this signal
262 return;
263
264 QVariant mainValue = QgsVariantUtils::createNullVariant( field().type() );
265
266 if ( !mWidget || !mWidget->relation().isValid() )
267 {
269 emit valueChanged( mainValue );
271 emit valuesChanged( mainValue );
272 return;
273 }
274
275 QVariantList additionalValues = values;
276 const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
277 for ( int i = 0; i < fieldPairs.count(); i++ )
278 {
279 if ( fieldPairs.at( i ).referencingField() == field().name() )
280 mainValue = additionalValues.takeAt( i ); // additional values in field pair order remain
281 }
282 Q_ASSERT( additionalValues.count() == values.count() - 1 );
283
285 emit valueChanged( mainValue );
287 emit valuesChanged( mainValue, additionalValues );
288}
289
291{
292 if ( mWidget )
293 {
295 {
296 widget()->setStyleSheet( QString() );
297 }
298 else
299 {
300 switch ( constraintResult() )
301 {
303 mWidget->setStyleSheet( QString() );
304 break;
305
307 mWidget->setStyleSheet( QStringLiteral( ".QComboBox { background-color: #dd7777; }" ) );
308 break;
309
311 mWidget->setStyleSheet( QStringLiteral( ".QComboBox { background-color: #ffd85d; }" ) );
312 break;
313 }
314 }
315 }
316}
317
318void QgsRelationReferenceWidgetWrapper::parentFormValueChanged( const QString &attribute, const QVariant &value )
319{
320 // Update the parent feature in the context ( which means to replace the whole context :/ )
322 QgsFeature feature { context().parentFormFeature() };
323 feature.setAttribute( attribute, value );
324 ctx.setParentFormFeature( feature );
325 setContext( ctx );
326
327 // Check if the change might affect the filter expression and the cache needs updates
329 && QgsValueRelationFieldFormatter::expressionParentFormAttributes( mExpression ).contains( attribute ) )
330 {
331 mWidget->setParentFormFeature( context().parentFormFeature() );
332 }
333}
334
335void QgsRelationReferenceWidgetWrapper::widgetValueChanged( const QString &attribute, const QVariant &newValue, bool attributeChanged )
336{
337 if ( attributeChanged )
338 {
339 setFormFeatureAttribute( attribute, newValue );
340 if ( QgsValueRelationFieldFormatter::expressionRequiresFormScope( mWidget->filterExpression() )
341 && QgsValueRelationFieldFormatter::expressionFormAttributes( mWidget->filterExpression() ).contains( attribute ) )
342 {
343 mWidget->setFormFeature( formFeature() );
344 }
345 }
346}
Contains context information for attribute editor widgets.
QgsFeature parentFormFeature() const
Returns the feature of the currently edited parent form in its actual state.
void setParentFormFeature(const QgsFeature &feature)
Sets the feature of the currently edited parent form.
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.
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.
QgsEditorWidgetWrapper(QgsVectorLayer *vl, int fieldIdx, QWidget *editor=nullptr, QWidget *parent=nullptr)
Create a new widget wrapper.
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:120
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.
bool saveReferencedAttributeForm()
Trigger save of the embedded referenced attribute form.
QgsRelation relation() const
Returns the current relation, which might be invalid.
void foreignKeysChanged(const QVariantList &keys)
Emitted when the foreign keys changed.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QString id
Definition qgsrelation.h:45
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:65
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:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169