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