QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 "qgsproject.h"
19 #include "qgsrelationmanager.h"
21 #include "qgsattributeform.h"
22 
23 QgsRelationReferenceWidgetWrapper::QgsRelationReferenceWidgetWrapper( QgsVectorLayer *vl, int fieldIdx, QWidget *editor, QgsMapCanvas *canvas, QgsMessageBar *messageBar, QWidget *parent )
24  : QgsEditorWidgetWrapper( vl, fieldIdx, editor, parent )
25  , mCanvas( canvas )
26  , mMessageBar( messageBar )
27  , mIndeterminateState( false )
28 {
29 }
30 
32 {
34  return w;
35 }
36 
38 {
39  QgsRelationReferenceWidget *w = qobject_cast<QgsRelationReferenceWidget *>( editor );
40  if ( !w )
41  {
42  w = new QgsRelationReferenceWidget( editor );
43  }
44 
45  mWidget = w;
46 
47  const QgsAttributeEditorContext *ctx = &context();
48 
49  mWidget->setEditorContext( *ctx, mCanvas, mMessageBar );
50 
51  const bool showForm = config( QStringLiteral( "ShowForm" ), false ).toBool();
52  const bool mapIdent = config( QStringLiteral( "MapIdentification" ), false ).toBool();
53  const bool readOnlyWidget = config( QStringLiteral( "ReadOnly" ), false ).toBool();
54  const bool orderByValue = config( QStringLiteral( "OrderByValue" ), false ).toBool();
55  const bool showOpenFormButton = config( QStringLiteral( "ShowOpenFormButton" ), true ).toBool();
56 
57  mWidget->setEmbedForm( showForm );
58  mWidget->setReadOnlySelector( readOnlyWidget );
59  mWidget->setAllowMapIdentification( mapIdent );
60  mWidget->setOrderByValue( orderByValue );
61  mWidget->setOpenFormButtonVisible( showOpenFormButton );
62  if ( config( QStringLiteral( "FilterFields" ), QVariant() ).isValid() )
63  {
64  mWidget->setFilterFields( config( QStringLiteral( "FilterFields" ) ).toStringList() );
65  mWidget->setChainFilters( config( QStringLiteral( "ChainFilters" ) ).toBool() );
66  }
67  if ( !config( QStringLiteral( "FilterExpression" ) ).toString().isEmpty() )
68  {
69  mWidget->setFilterExpression( config( QStringLiteral( "FilterExpression" ) ).toString() );
70  }
71  mWidget->setAllowAddFeatures( config( QStringLiteral( "AllowAddFeatures" ), false ).toBool() );
72 
73  const QVariant relationName = config( QStringLiteral( "Relation" ) );
74 
75  // Store relation data source and provider key
76  mWidget->setReferencedLayerDataSource( config( QStringLiteral( "ReferencedLayerDataSource" ) ).toString() );
77  mWidget->setReferencedLayerProviderKey( config( QStringLiteral( "ReferencedLayerProviderKey" ) ).toString() );
78  mWidget->setReferencedLayerId( config( QStringLiteral( "ReferencedLayerId" ) ).toString() );
79  mWidget->setReferencedLayerName( config( QStringLiteral( "ReferencedLayerName" ) ).toString() );
80 
81  QgsRelation relation; // invalid relation by default
82  if ( relationName.isValid() )
83  relation = QgsProject::instance()->relationManager()->relation( relationName.toString() );
84  if ( !relation.isValid() && !layer()->referencingRelations( fieldIdx() ).isEmpty() )
85  relation = layer()->referencingRelations( fieldIdx() )[0];
86 
87  // If this widget is already embedded by the same relation, reduce functionality
88  do
89  {
90  if ( ctx->relation().id() == relation.id() )
91  {
92  mWidget->setEmbedForm( false );
93  mWidget->setReadOnlySelector( true );
94  mWidget->setAllowMapIdentification( false );
95  mWidget->setOpenFormButtonVisible( false );
96  mWidget->setAllowAddFeatures( false );
97  break;
98  }
99  ctx = ctx->parentContext();
100  }
101  while ( ctx );
102 
103  mWidget->setRelation( relation, config( QStringLiteral( "AllowNULL" ) ).toBool() );
104 
105  connect( mWidget, &QgsRelationReferenceWidget::foreignKeysChanged, this, &QgsRelationReferenceWidgetWrapper::foreignKeysChanged );
106 }
107 
109 {
110  if ( !mWidget )
111  return QVariant( field().type() );
112 
113  const QVariantList fkeys = mWidget->foreignKeys();
114 
115  if ( fkeys.isEmpty() )
116  {
117  return QVariant( field().type() );
118  }
119  else
120  {
121  const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
122  Q_ASSERT( fieldPairs.count() == fkeys.count() );
123  for ( int i = 0; i < fieldPairs.count(); i++ )
124  {
125  if ( fieldPairs.at( i ).referencingField() == field().name() )
126  return fkeys.at( i );
127  }
128  return QVariant( field().type() ); // should not happen
129  }
130 }
131 
133 {
134  return mWidget;
135 }
136 
138 {
139  if ( mWidget )
140  {
141  mWidget->showIndeterminateState();
142  }
143  mIndeterminateState = true;
144 }
145 
147 {
148  if ( !mWidget || !mWidget->relation().isValid() )
149  {
150  QVariantList values;
151  for ( int i = 0; i < mWidget->relation().fieldPairs().count(); i++ )
152  {
153  values << QVariant();
154  }
155  return values;
156  }
157  else
158  {
159  QVariantList values = mWidget->foreignKeys();
160  const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
161  const int fieldCount = std::min( fieldPairs.count(), values.count() );
162  for ( int i = 0; i < fieldCount; i++ )
163  {
164  if ( fieldPairs.at( i ).referencingField() == field().name() )
165  {
166  values.removeAt( i );
167  break;
168  }
169  }
170  return values;
171  }
172 }
173 
175 {
176  if ( !mWidget || !mWidget->relation().isValid() )
177  return QStringList();
178 
179  QStringList fields;
180  const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
181  for ( int i = 0; i < fieldPairs.count(); i++ )
182  {
183  if ( fieldPairs.at( i ).referencingField() == field().name() )
184  continue;
185 
186  fields << fieldPairs.at( i ).referencingField();
187  }
188  return fields;
189 }
190 
191 void QgsRelationReferenceWidgetWrapper::updateValues( const QVariant &val, const QVariantList &additionalValues )
192 {
193  if ( !mWidget || ( !mIndeterminateState && val == value() && val.isNull() == value().isNull() ) )
194  return;
195 
196  mIndeterminateState = false;
197 
198  QVariantList values = additionalValues;
199  const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
200  for ( int i = 0; i < fieldPairs.count(); i++ )
201  {
202  if ( fieldPairs.at( i ).referencingField() == field().name() )
203  {
204  values.insert( i, val );
205  break;
206  }
207  }
208  Q_ASSERT( values.count() == fieldPairs.count() );
209 
210  mBlockChanges++;
211  mWidget->setForeignKeys( values );
212  mWidget->setFormFeature( formFeature() );
213  mBlockChanges--;
214 }
215 
217 {
218  if ( !mWidget )
219  return;
220 
221  mWidget->setRelationEditable( enabled );
222 }
223 
224 void QgsRelationReferenceWidgetWrapper::foreignKeysChanged( const QVariantList &values )
225 {
226  if ( mBlockChanges != 0 ) // initial value is being set, we can ignore this signal
227  return;
228 
229  QVariant mainValue = QVariant( field().type() );
230 
231  if ( !mWidget || !mWidget->relation().isValid() )
232  {
234  emit valueChanged( mainValue );
236  emit valuesChanged( mainValue );
237  return;
238  }
239 
240  QVariantList additionalValues = values;
241  const QList<QgsRelation::FieldPair> fieldPairs = mWidget->relation().fieldPairs();
242  for ( int i = 0; i < fieldPairs.count(); i++ )
243  {
244  if ( fieldPairs.at( i ).referencingField() == field().name() )
245  mainValue = additionalValues.takeAt( i ); // additional values in field pair order remain
246  }
247  Q_ASSERT( additionalValues.count() == values.count() - 1 );
248 
250  emit valueChanged( mainValue );
252  emit valuesChanged( mainValue, additionalValues );
253 }
254 
256 {
257  if ( mWidget )
258  {
259  if ( !constraintResultVisible() )
260  {
261  widget()->setStyleSheet( QString() );
262  }
263  else
264  {
265  switch ( constraintResult() )
266  {
268  mWidget->setStyleSheet( QString() );
269  break;
270 
272  mWidget->setStyleSheet( QStringLiteral( ".QComboBox { background-color: #dd7777; }" ) );
273  break;
274 
276  mWidget->setStyleSheet( QStringLiteral( ".QComboBox { background-color: #ffd85d; }" ) );
277  break;
278  }
279  }
280  }
281 }
This class contains context information for attribute editor widgets.
const QgsAttributeEditorContext * parentContext() const
const QgsRelation & relation() const
Returns the attribute relation.
Manages an editor widget Widget and wrapper share the same parent.
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.
ConstraintResult constraintResult
void valuesChanged(const QVariant &value, const QVariantList &additionalFieldValues=QVariantList())
Emit this signal, whenever the value changed.
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.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
QgsRelationManager * relationManager
Definition: qgsproject.h:111
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:467
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 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.
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.
void setFilterExpression(const QString &filterExpression)
If not empty, will be used as filter expression.
void setReferencedLayerProviderKey(const QString &referencedLayerProviderKey)
Set the data provider key of the referenced layer to referencedLayerProviderKey.
void foreignKeysChanged(const QVariantList &)
Emitted when the foreign keys changed.
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.
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 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 setOrderByValue(bool orderByValue)
Sets if the widget will order the combobox entries by value.
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 setFormFeature(const QgsFeature &formFeature)
Set the current form feature (from the referencing layer)
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
Q_GADGET QString id
Definition: qgsrelation.h:46
bool isValid
Definition: qgsrelation.h:50
Represents a vector layer which manages a vector based data sets.
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.
QVariantMap config() const
Returns the whole config.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:1742
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1741