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