QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsrelationreferencewidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationreferencewidget.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 
17 
18 #include <QPushButton>
19 #include <QDialog>
20 #include <QHBoxLayout>
21 #include <QTimer>
22 #include <QCompleter>
23 
24 #include "qgsattributeform.h"
26 #include "qgsattributedialog.h"
27 #include "qgsapplication.h"
28 #include "qgscollapsiblegroupbox.h"
29 #include "qgseditorwidgetfactory.h"
30 #include "qgsexpression.h"
31 #include "qgsfeaturelistmodel.h"
32 #include "qgsfields.h"
33 #include "qgsgeometry.h"
34 #include "qgshighlight.h"
35 #include "qgsmapcanvas.h"
36 #include "qgsmessagebar.h"
38 #include "qgsvectorlayer.h"
39 #include "qgsattributetablemodel.h"
42 #include "qgsfeatureiterator.h"
43 #include "qgsfeaturelistcombobox.h"
45 #include "qgsfeaturefiltermodel.h"
46 #include "qgsidentifymenu.h"
47 #include "qgsvectorlayerutils.h"
48 
49 
50 bool qVariantListIsNull( const QVariantList &list )
51 {
52  if ( list.isEmpty() )
53  return true;
54 
55  for ( int i = 0; i < list.size(); ++i )
56  {
57  if ( !list.at( i ).isNull() )
58  return false;
59  }
60  return true;
61 }
62 
63 
65  : QWidget( parent )
66 {
67  mTopLayout = new QVBoxLayout( this );
68  mTopLayout->setContentsMargins( 0, 0, 0, 0 );
69 
70  setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::Fixed );
71 
72  setLayout( mTopLayout );
73 
74  QHBoxLayout *editLayout = new QHBoxLayout();
75  editLayout->setContentsMargins( 0, 0, 0, 0 );
76  editLayout->setSpacing( 2 );
77 
78  // Prepare the container and layout for the filter comboboxes
79  mChooserContainer = new QWidget;
80  editLayout->addWidget( mChooserContainer );
81  QHBoxLayout *chooserLayout = new QHBoxLayout;
82  chooserLayout->setContentsMargins( 0, 0, 0, 0 );
83  mFilterLayout = new QHBoxLayout;
84  mFilterLayout->setContentsMargins( 0, 0, 0, 0 );
85  mFilterContainer = new QWidget;
86  mFilterContainer->setLayout( mFilterLayout );
87  mChooserContainer->setLayout( chooserLayout );
88  chooserLayout->addWidget( mFilterContainer );
89 
90  mComboBox = new QgsFeatureListComboBox();
91  mChooserContainer->layout()->addWidget( mComboBox );
92 
93  // read-only line edit
94  mLineEdit = new QLineEdit();
95  mLineEdit->setReadOnly( true );
96  editLayout->addWidget( mLineEdit );
97 
98  // open form button
99  mOpenFormButton = new QToolButton();
100  mOpenFormButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
101  mOpenFormButton->setText( tr( "Open Related Feature Form" ) );
102  editLayout->addWidget( mOpenFormButton );
103 
104  mAddEntryButton = new QToolButton();
105  mAddEntryButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionAdd.svg" ) ) );
106  mAddEntryButton->setText( tr( "Add New Entry" ) );
107  editLayout->addWidget( mAddEntryButton );
108 
109  // highlight button
110  mHighlightFeatureButton = new QToolButton( this );
111  mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
112  mHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionHighlightFeature.svg" ) ), tr( "Highlight feature" ), this );
113  mScaleHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionScaleHighlightFeature.svg" ) ), tr( "Scale and highlight feature" ), this );
114  mPanHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPanHighlightFeature.svg" ) ), tr( "Pan and highlight feature" ), this );
115  mHighlightFeatureButton->addAction( mHighlightFeatureAction );
116  mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
117  mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
118  mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
119  editLayout->addWidget( mHighlightFeatureButton );
120 
121  // map identification button
122  mMapIdentificationButton = new QToolButton( this );
123  mMapIdentificationButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
124  mMapIdentificationButton->setText( tr( "Select on Map" ) );
125  mMapIdentificationButton->setCheckable( true );
126  editLayout->addWidget( mMapIdentificationButton );
127 
128  // remove foreign key button
129  mRemoveFKButton = new QToolButton( this );
130  mRemoveFKButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRemove.svg" ) ) );
131  mRemoveFKButton->setText( tr( "No Selection" ) );
132  editLayout->addWidget( mRemoveFKButton );
133 
134  // add line to top layout
135  mTopLayout->addLayout( editLayout );
136 
137  // embed form
138  mAttributeEditorFrame = new QgsCollapsibleGroupBox( this );
139  mAttributeEditorLayout = new QVBoxLayout( mAttributeEditorFrame );
140  mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
141  mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
142  mTopLayout->addWidget( mAttributeEditorFrame );
143 
144  // invalid label
145  mInvalidLabel = new QLabel( tr( "The relation is not valid. Please make sure your relation definitions are OK." ) );
146  mInvalidLabel->setWordWrap( true );
147  QFont font = mInvalidLabel->font();
148  font.setItalic( true );
149  mInvalidLabel->setStyleSheet( QStringLiteral( "QLabel { color: red; } " ) );
150  mInvalidLabel->setFont( font );
151  mTopLayout->addWidget( mInvalidLabel );
152 
153  // default mode is combobox, no geometric relation and no embed form
154  mLineEdit->hide();
155  mMapIdentificationButton->hide();
156  mHighlightFeatureButton->hide();
157  mAttributeEditorFrame->hide();
158  mInvalidLabel->hide();
159  mAddEntryButton->hide();
160 
161  // connect buttons
162  connect( mOpenFormButton, &QAbstractButton::clicked, this, &QgsRelationReferenceWidget::openForm );
163  connect( mHighlightFeatureButton, &QToolButton::triggered, this, &QgsRelationReferenceWidget::highlightActionTriggered );
164  connect( mMapIdentificationButton, &QAbstractButton::clicked, this, &QgsRelationReferenceWidget::mapIdentification );
165  connect( mRemoveFKButton, &QAbstractButton::clicked, this, &QgsRelationReferenceWidget::deleteForeignKeys );
166  connect( mAddEntryButton, &QAbstractButton::clicked, this, &QgsRelationReferenceWidget::addEntry );
167  connect( mComboBox, &QComboBox::editTextChanged, this, &QgsRelationReferenceWidget::updateAddEntryButton );
168 }
169 
171 {
172  deleteHighlight();
173  unsetMapTool();
174 }
175 
176 void QgsRelationReferenceWidget::setRelation( const QgsRelation &relation, bool allowNullValue )
177 {
178  mAllowNull = allowNullValue;
179  mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
180 
181  if ( relation.isValid() )
182  {
183  mReferencedLayerId = relation.referencedLayerId();
184  mReferencedLayerName = relation.referencedLayer()->name();
186  mReferencedLayerProviderKey = relation.referencedLayer()->providerType();
187  mInvalidLabel->hide();
188 
189  mRelation = relation;
190  mReferencingLayer = relation.referencingLayer();
191  mReferencedLayer = relation.referencedLayer();
192 
193  const QList<QgsRelation::FieldPair> fieldPairs = relation.fieldPairs();
194  for ( const QgsRelation::FieldPair &fieldPair : fieldPairs )
195  {
196  mReferencedFields << fieldPair.referencedField();
197  }
198  if ( mComboBox )
199  {
200  mComboBox->setAllowNull( mAllowNull );
201  mComboBox->setSourceLayer( mReferencedLayer );
202  mComboBox->setIdentifierFields( mReferencedFields );
203  mComboBox->setFilterExpression( mFilterExpression );
204  }
205  mAttributeEditorFrame->setObjectName( QStringLiteral( "referencing/" ) + relation.name() );
206 
207  if ( mEmbedForm )
208  {
210  mAttributeEditorFrame->setTitle( mReferencedLayer->name() );
211  mReferencedAttributeForm = new QgsAttributeForm( relation.referencedLayer(), QgsFeature(), context, this );
212  mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
213  }
214 
215  connect( mReferencedLayer, &QgsVectorLayer::editingStarted, this, &QgsRelationReferenceWidget::updateAddEntryButton );
216  connect( mReferencedLayer, &QgsVectorLayer::editingStopped, this, &QgsRelationReferenceWidget::updateAddEntryButton );
217  updateAddEntryButton();
218  }
219  else
220  {
221  mInvalidLabel->show();
222  }
223 
224  if ( mShown && isVisible() )
225  {
226  init();
227  }
228 }
229 
231 {
232  if ( !editable )
233  {
234  unsetMapTool();
235  }
236 
237  mFilterContainer->setEnabled( editable );
238  mComboBox->setEnabled( editable );
239  mComboBox->setEditable( true );
240  mMapIdentificationButton->setEnabled( editable );
241  mRemoveFKButton->setEnabled( editable );
242  mIsEditable = editable;
243 }
244 
245 void QgsRelationReferenceWidget::setForeignKey( const QVariant &value )
246 {
247  setForeignKeys( QVariantList() << value );
248 }
249 
250 void QgsRelationReferenceWidget::setForeignKeys( const QVariantList &values )
251 {
252  if ( values.isEmpty() )
253  {
254  return;
255  }
256  if ( qVariantListIsNull( values ) )
257  {
259  return;
260  }
261 
262  if ( !mReferencedLayer )
263  return;
264 
265  if ( mReadOnlySelector )
266  {
267  // Attributes from the referencing layer
268  QgsAttributes attrs = QgsAttributes( mReferencingLayer->fields().count() );
269  // Set the value on the foreign key fields of the referencing record
270 
271  const QList<QgsRelation::FieldPair> fieldPairs = mRelation.fieldPairs();
272  int fieldCount = std::min( fieldPairs.count(), values.count() );
273  for ( int i = 0; i < fieldCount; i++ )
274  {
275  int idx = mReferencingLayer->fields().lookupField( fieldPairs.at( i ).referencingField() );
276  attrs[idx] = values.at( i );
277  }
278 
279  QgsFeatureRequest request = mRelation.getReferencedFeatureRequest( attrs );
280 
281  mReferencedLayer->getFeatures( request ).nextFeature( mFeature );
282 
283  if ( !mFeature.isValid() )
284  {
285  return;
286  }
287 
288  mForeignKeys.clear();
289  for ( const QString &fieldName : qgis::as_const( mReferencedFields ) )
290  mForeignKeys << mFeature.attribute( fieldName );
291 
292  QgsExpression expr( mReferencedLayer->displayExpression() );
294  context.setFeature( mFeature );
295  QString title = expr.evaluate( &context ).toString();
296  if ( expr.hasEvalError() )
297  {
298  QStringList titleFields;
299  for ( const QString &fieldName : qgis::as_const( mReferencedFields ) )
300  titleFields << mFeature.attribute( fieldName ).toString();
301  title = titleFields.join( QLatin1Char( ' ' ) );
302  }
303  mLineEdit->setText( title );
304  }
305  else
306  {
307  mComboBox->setIdentifierValues( values );
308 
309  if ( mEmbedForm || mChainFilters )
310  {
311  QgsFeatureRequest request = mComboBox->currentFeatureRequest();
312  mReferencedLayer->getFeatures( request ).nextFeature( mFeature );
313  }
314  if ( mChainFilters )
315  {
316  QVariant nullValue = QgsApplication::nullRepresentation();
317  const int count = std::min( mFilterComboBoxes.size(), mFilterFields.size() );
318  for ( int i = 0; i < count; i++ )
319  {
320  QVariant v = mFeature.attribute( mFilterFields[i] );
321  QString f = v.isNull() ? nullValue.toString() : v.toString();
322  mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
323  }
324  }
325  }
326 
327  mRemoveFKButton->setEnabled( mIsEditable );
328  highlightFeature( mFeature ); // TODO : make this async
329  updateAttributeEditorFrame( mFeature );
330 
331  emitForeignKeysChanged( foreignKeys() );
332 }
333 
335 {
336  // deactivate filter comboboxes
337  if ( mChainFilters && !mFilterComboBoxes.isEmpty() )
338  {
339  QComboBox *cb = mFilterComboBoxes.first();
340  cb->setCurrentIndex( 0 );
341  disableChainedComboBoxes( cb );
342  }
343 
344  if ( mReadOnlySelector )
345  {
346  const QString nullValue = QgsApplication::nullRepresentation();
347 
348  QString nullText;
349  if ( mAllowNull )
350  {
351  nullText = tr( "%1 (no selection)" ).arg( nullValue );
352  }
353  mLineEdit->setText( nullText );
354  QVariantList nullAttributes;
355  for ( const QString &fieldName : qgis::as_const( mReferencedFields ) )
356  {
357  Q_UNUSED( fieldName );
358  nullAttributes << QVariant( QVariant::Int );
359  }
360  mForeignKeys = nullAttributes;
361  mFeature.setValid( false );
362  }
363  else
364  {
365  mComboBox->setIdentifierValuesToNull();
366  }
367  mRemoveFKButton->setEnabled( false );
368  updateAttributeEditorFrame( QgsFeature() );
369 
370  emitForeignKeysChanged( foreignKeys() );
371 }
372 
374 {
375  QgsFeature f;
376  if ( mReferencedLayer )
377  {
378  QgsFeatureRequest request;
379  if ( mReadOnlySelector )
380  {
381  request = QgsFeatureRequest().setFilterFid( mFeature.id() );
382  }
383  else
384  {
385  request = mComboBox->currentFeatureRequest();
386  }
387  mReferencedLayer->getFeatures( request ).nextFeature( f );
388  }
389  return f;
390 }
391 
393 {
394  if ( mReadOnlySelector )
395  {
396  whileBlocking( mLineEdit )->setText( QString() );
397  }
398  else
399  {
400  whileBlocking( mComboBox )->setIdentifierValuesToNull();
401  }
402  mRemoveFKButton->setEnabled( false );
403  updateAttributeEditorFrame( QgsFeature() );
404 }
405 
407 {
408  QVariantList fkeys;
409  if ( fkeys.isEmpty() )
410  return QVariant( QVariant::Int );
411  else
412  return fkeys.at( 0 );
413 }
414 
416 {
417  if ( mReadOnlySelector )
418  {
419  return mForeignKeys;
420  }
421  else
422  {
423  return mComboBox->identifierValues();
424  }
425 }
426 
428 {
429  mEditorContext = context;
430  mCanvas = canvas;
431  mMessageBar = messageBar;
432 
433  mMapToolIdentify.reset( new QgsMapToolIdentifyFeature( mCanvas ) );
434  mMapToolIdentify->setButton( mMapIdentificationButton );
435 
436  if ( mEditorContext.cadDockWidget() )
437  {
438  mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( mCanvas, mEditorContext.cadDockWidget() ) );
439  mMapToolDigitize->setButton( mAddEntryButton );
440  updateAddEntryButton();
441  }
442 }
443 
445 {
446  if ( display )
447  {
448  setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
449  mTopLayout->setAlignment( Qt::AlignTop );
450  }
451 
452  mAttributeEditorFrame->setVisible( display );
453  mEmbedForm = display;
454 }
455 
457 {
458  mChooserContainer->setHidden( readOnly );
459  mLineEdit->setVisible( readOnly );
460  mRemoveFKButton->setVisible( mAllowNull && readOnly );
461  mReadOnlySelector = readOnly;
462 }
463 
464 void QgsRelationReferenceWidget::setAllowMapIdentification( bool allowMapIdentification )
465 {
466  mHighlightFeatureButton->setVisible( allowMapIdentification );
467  mMapIdentificationButton->setVisible( allowMapIdentification );
468  mAllowMapIdentification = allowMapIdentification;
469 }
470 
472 {
473  mOrderByValue = orderByValue;
474 }
475 
476 void QgsRelationReferenceWidget::setFilterFields( const QStringList &filterFields )
477 {
478  mFilterFields = filterFields;
479 }
480 
482 {
483  mOpenFormButton->setVisible( openFormButtonVisible );
484  mOpenFormButtonVisible = openFormButtonVisible;
485 }
486 
488 {
489  mChainFilters = chainFilters;
490 }
491 
492 void QgsRelationReferenceWidget::setFilterExpression( const QString &expression )
493 {
494  mFilterExpression = expression;
495 }
496 
498 {
499  Q_UNUSED( e )
500 
501  mShown = true;
502  if ( !mInitialized )
503  init();
504 }
505 
507 {
508  if ( !mReadOnlySelector && mReferencedLayer )
509  {
510  QApplication::setOverrideCursor( Qt::WaitCursor );
511 
512  QSet<QString> requestedAttrs;
513 
514  if ( !mFilterFields.isEmpty() )
515  {
516  for ( const QString &fieldName : qgis::as_const( mFilterFields ) )
517  {
518  int idx = mReferencedLayer->fields().lookupField( fieldName );
519 
520  if ( idx == -1 )
521  continue;
522 
523  QComboBox *cb = new QComboBox();
524  cb->setProperty( "Field", fieldName );
525  cb->setProperty( "FieldAlias", mReferencedLayer->attributeDisplayName( idx ) );
526  mFilterComboBoxes << cb;
527  QVariantList uniqueValues = qgis::setToList( mReferencedLayer->uniqueValues( idx ) );
528  cb->addItem( mReferencedLayer->attributeDisplayName( idx ) );
529  QVariant nullValue = QgsApplication::nullRepresentation();
530  cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->fields().at( idx ).type() ) );
531 
532  std::sort( uniqueValues.begin(), uniqueValues.end(), qgsVariantLessThan );
533  const auto constUniqueValues = uniqueValues;
534  for ( const QVariant &v : constUniqueValues )
535  {
536  cb->addItem( v.toString(), v );
537  }
538 
539  connect( cb, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsRelationReferenceWidget::filterChanged );
540 
541  // Request this attribute for caching
542  requestedAttrs << fieldName;
543 
544  mFilterLayout->addWidget( cb );
545  }
546 
547  if ( mChainFilters )
548  {
549  QVariant nullValue = QgsApplication::nullRepresentation();
550 
551  QgsFeature ft;
552  QgsFeatureIterator fit = mFilterExpression.isEmpty()
553  ? mReferencedLayer->getFeatures()
554  : mReferencedLayer->getFeatures( mFilterExpression );
555  while ( fit.nextFeature( ft ) )
556  {
557  const int count = std::min( mFilterComboBoxes.count(), mFilterFields.count() );
558  for ( int i = 0; i < count - 1; i++ )
559  {
560  QVariant cv = ft.attribute( mFilterFields.at( i ) );
561  QVariant nv = ft.attribute( mFilterFields.at( i + 1 ) );
562  QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
563  QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
564  mFilterCache[mFilterFields[i]][cf] << nf;
565  }
566  }
567 
568  if ( !mFilterComboBoxes.isEmpty() )
569  {
570  QComboBox *cb = mFilterComboBoxes.first();
571  cb->setCurrentIndex( 0 );
572  disableChainedComboBoxes( cb );
573  }
574  }
575  }
576  else
577  {
578  mFilterContainer->hide();
579  }
580 
581  mComboBox->setSourceLayer( mReferencedLayer );
582  mComboBox->setDisplayExpression( mReferencedLayer->displayExpression() );
583  mComboBox->setAllowNull( mAllowNull );
584  mComboBox->setIdentifierFields( mReferencedFields );
585 
586  if ( ! mFilterExpression.isEmpty() )
587  mComboBox->setFilterExpression( mFilterExpression );
588 
589  QVariant nullValue = QgsApplication::nullRepresentation();
590 
591  if ( mChainFilters && mFeature.isValid() )
592  {
593  for ( int i = 0; i < mFilterFields.size(); i++ )
594  {
595  QVariant v = mFeature.attribute( mFilterFields[i] );
596  QString f = v.isNull() ? nullValue.toString() : v.toString();
597  mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
598  }
599  }
600 
601  // Only connect after iterating, to have only one iterator on the referenced table at once
602  connect( mComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsRelationReferenceWidget::comboReferenceChanged );
603 
604  QApplication::restoreOverrideCursor();
605 
606  mInitialized = true;
607  }
608 }
609 
610 void QgsRelationReferenceWidget::highlightActionTriggered( QAction *action )
611 {
612  if ( action == mHighlightFeatureAction )
613  {
614  highlightFeature();
615  }
616  else if ( action == mScaleHighlightFeatureAction )
617  {
618  highlightFeature( QgsFeature(), Scale );
619  }
620  else if ( action == mPanHighlightFeatureAction )
621  {
622  highlightFeature( QgsFeature(), Pan );
623  }
624 }
625 
627 {
628  QgsFeature feat = referencedFeature();
629 
630  if ( !feat.isValid() )
631  return;
632 
634  QgsAttributeDialog attributeDialog( mReferencedLayer, new QgsFeature( feat ), true, this, true, context );
635  attributeDialog.exec();
636 }
637 
638 void QgsRelationReferenceWidget::highlightFeature( QgsFeature f, CanvasExtent canvasExtent )
639 {
640  if ( !mCanvas )
641  return;
642 
643  if ( !f.isValid() )
644  {
645  f = referencedFeature();
646  if ( !f.isValid() )
647  return;
648  }
649 
650  if ( !f.hasGeometry() )
651  {
652  return;
653  }
654 
655  QgsGeometry geom = f.geometry();
656 
657  // scale or pan
658  if ( canvasExtent == Scale )
659  {
660  QgsRectangle featBBox = geom.boundingBox();
661  featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox );
662  QgsRectangle extent = mCanvas->extent();
663  if ( !extent.contains( featBBox ) )
664  {
665  extent.combineExtentWith( featBBox );
666  extent.scale( 1.1 );
667  mCanvas->setExtent( extent, true );
668  mCanvas->refresh();
669  }
670  }
671  else if ( canvasExtent == Pan )
672  {
673  QgsGeometry centroid = geom.centroid();
674  QgsPointXY center = centroid.asPoint();
675  center = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, center );
676  mCanvas->zoomByFactor( 1.0, &center ); // refresh is done in this method
677  }
678 
679  // highlight
680  deleteHighlight();
681  mHighlight = new QgsHighlight( mCanvas, f, mReferencedLayer );
682  QgsIdentifyMenu::styleHighlight( mHighlight );
683  mHighlight->show();
684 
685  QTimer *timer = new QTimer( this );
686  timer->setSingleShot( true );
687  connect( timer, &QTimer::timeout, this, &QgsRelationReferenceWidget::deleteHighlight );
688  timer->start( 3000 );
689 }
690 
691 void QgsRelationReferenceWidget::deleteHighlight()
692 {
693  if ( mHighlight )
694  {
695  mHighlight->hide();
696  delete mHighlight;
697  }
698  mHighlight = nullptr;
699 }
700 
702 {
703  if ( !mAllowMapIdentification || !mReferencedLayer )
704  return;
705 
706  const QgsVectorLayerTools *tools = mEditorContext.vectorLayerTools();
707  if ( !tools )
708  return;
709  if ( !mCanvas )
710  return;
711 
712  mMapToolIdentify->setLayer( mReferencedLayer );
713  setMapTool( mMapToolIdentify );
714 
715  connect( mMapToolIdentify, qgis::overload<const QgsFeature &>::of( &QgsMapToolIdentifyFeature::featureIdentified ), this, &QgsRelationReferenceWidget::featureIdentified );
716 
717  if ( mMessageBar )
718  {
719  QString title = tr( "Relation %1 for %2." ).arg( mRelation.name(), mReferencingLayer->name() );
720  QString msg = tr( "Identify a feature of %1 to be associated. Press &lt;ESC&gt; to cancel." ).arg( mReferencedLayer->name() );
721  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
722  mMessageBar->pushItem( mMessageBarItem );
723  }
724 }
725 
726 void QgsRelationReferenceWidget::comboReferenceChanged( int index )
727 {
728  Q_UNUSED( index )
729  mReferencedLayer->getFeatures( mComboBox->currentFeatureRequest() ).nextFeature( mFeature );
730  highlightFeature( mFeature );
731  updateAttributeEditorFrame( mFeature );
732 
733  emitForeignKeysChanged( mComboBox->identifierValues() );
734 }
735 
736 void QgsRelationReferenceWidget::updateAttributeEditorFrame( const QgsFeature &feature )
737 {
738  mOpenFormButton->setEnabled( feature.isValid() );
739  // Check if we're running with an embedded frame we need to update
740  if ( mAttributeEditorFrame && mReferencedAttributeForm )
741  {
742  mReferencedAttributeForm->setFeature( feature );
743  }
744 }
745 
747 {
748  return mAllowAddFeatures;
749 }
750 
752 {
753  mAllowAddFeatures = allowAddFeatures;
754  updateAddEntryButton();
755 }
756 
758 {
759  return mRelation;
760 }
761 
762 void QgsRelationReferenceWidget::featureIdentified( const QgsFeature &feature )
763 {
764  if ( mReadOnlySelector )
765  {
766  QgsExpression expr( mReferencedLayer->displayExpression() );
768  context.setFeature( feature );
769  QString title = expr.evaluate( &context ).toString();
770  if ( expr.hasEvalError() )
771  {
772  QStringList titleFields;
773  for ( const QString &fieldName : qgis::as_const( mReferencedFields ) )
774  titleFields << mFeature.attribute( fieldName ).toString();
775  title = titleFields.join( QLatin1Char( ' ' ) );
776  }
777  mLineEdit->setText( title );
778  mForeignKeys.clear();
779  mFeature = feature;
780  for ( const QString &fieldName : qgis::as_const( mReferencedFields ) )
781  mForeignKeys << mFeature.attribute( fieldName );
782  }
783  else
784  {
785  mComboBox->setCurrentFeature( feature );
786  mFeature = feature;
787  }
788 
789  mRemoveFKButton->setEnabled( mIsEditable );
790  highlightFeature( feature );
791  updateAttributeEditorFrame( feature );
792  emitForeignKeysChanged( foreignKeys(), true );
793 
794  unsetMapTool();
795 }
796 
797 void QgsRelationReferenceWidget::setMapTool( QgsMapTool *mapTool )
798 {
799  mCurrentMapTool = mapTool;
800  mCanvas->setMapTool( mapTool );
801 
802  mWindowWidget = window();
803 
804  mCanvas->window()->raise();
805  mCanvas->activateWindow();
806  mCanvas->setFocus();
807  connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationReferenceWidget::mapToolDeactivated );
808 }
809 
810 void QgsRelationReferenceWidget::unsetMapTool()
811 {
812  // deactivate map tools if activated
813  if ( mCurrentMapTool )
814  {
815  /* this will call mapToolDeactivated */
816  mCanvas->unsetMapTool( mCurrentMapTool );
817 
818  if ( mCurrentMapTool == mMapToolDigitize )
819  {
820  disconnect( mCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationReferenceWidget::onKeyPressed );
821  disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationReferenceWidget::entryAdded );
822  }
823  else
824  {
825  disconnect( mMapToolIdentify, qgis::overload<const QgsFeature &>::of( &QgsMapToolIdentifyFeature::featureIdentified ), this, &QgsRelationReferenceWidget::featureIdentified );
826  }
827  }
828 }
829 
830 void QgsRelationReferenceWidget::onKeyPressed( QKeyEvent *e )
831 {
832  if ( e->key() == Qt::Key_Escape )
833  {
834  unsetMapTool();
835  }
836 }
837 
838 void QgsRelationReferenceWidget::mapToolDeactivated()
839 {
840  if ( mWindowWidget )
841  {
842  mWindowWidget->raise();
843  mWindowWidget->activateWindow();
844  }
845 
846  if ( mMessageBar && mMessageBarItem )
847  {
848  mMessageBar->popWidget( mMessageBarItem );
849  }
850  mMessageBarItem = nullptr;
851 }
852 
853 void QgsRelationReferenceWidget::filterChanged()
854 {
855  QVariant nullValue = QgsApplication::nullRepresentation();
856 
857  QMap<QString, QString> filters;
858  QgsAttributeList attrs;
859 
860  QComboBox *scb = qobject_cast<QComboBox *>( sender() );
861 
862  Q_ASSERT( scb );
863 
864  QgsFeature f;
865  QgsFeatureIds featureIds;
866  QString filterExpression = mFilterExpression;
867 
868  // wrap the expression with parentheses as it might contain `OR`
869  if ( !filterExpression.isEmpty() )
870  filterExpression = QStringLiteral( " ( %1 ) " ).arg( filterExpression );
871 
872  // comboboxes have to be disabled before building filters
873  if ( mChainFilters )
874  disableChainedComboBoxes( scb );
875 
876  // build filters
877  const auto constMFilterComboBoxes = mFilterComboBoxes;
878  for ( QComboBox *cb : constMFilterComboBoxes )
879  {
880  if ( cb->currentIndex() != 0 )
881  {
882  const QString fieldName = cb->property( "Field" ).toString();
883 
884  if ( cb->currentText() == nullValue.toString() )
885  {
886  filters[fieldName] = QStringLiteral( "\"%1\" IS NULL" ).arg( fieldName );
887  }
888  else
889  {
890  filters[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, cb->currentText() );
891  }
892  attrs << mReferencedLayer->fields().lookupField( fieldName );
893  }
894  }
895 
896  if ( mChainFilters )
897  {
898  QComboBox *ccb = nullptr;
899  const auto constMFilterComboBoxes = mFilterComboBoxes;
900  for ( QComboBox *cb : constMFilterComboBoxes )
901  {
902  if ( !ccb )
903  {
904  if ( cb == scb )
905  ccb = cb;
906 
907  continue;
908  }
909 
910  if ( ccb->currentIndex() != 0 )
911  {
912  const QString fieldName = cb->property( "Field" ).toString();
913 
914  cb->blockSignals( true );
915  cb->clear();
916  cb->addItem( cb->property( "FieldAlias" ).toString() );
917 
918  // ccb = scb
919  // cb = scb + 1
920  QStringList texts;
921  const auto txts { mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] };
922  for ( const QString &txt : txts )
923  {
924  QMap<QString, QString> filtersAttrs = filters;
925  filtersAttrs[fieldName] = QgsExpression::createFieldEqualityExpression( fieldName, txt );
926  QgsAttributeList subset = attrs;
927 
928  QString expression = filterExpression;
929  if ( ! filterExpression.isEmpty() && ! filtersAttrs.values().isEmpty() )
930  expression += QLatin1String( " AND " );
931 
932  expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral( " ( " );
933  expression += filtersAttrs.values().join( QLatin1String( " AND " ) );
934  expression += filtersAttrs.isEmpty() ? QString() : QStringLiteral( " ) " );
935 
936  subset << mReferencedLayer->fields().lookupField( fieldName );
937 
938  QgsFeatureIterator it( mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterExpression( expression ).setSubsetOfAttributes( subset ) ) );
939 
940  bool found = false;
941  while ( it.nextFeature( f ) )
942  {
943  if ( !featureIds.contains( f.id() ) )
944  featureIds << f.id();
945 
946  found = true;
947  }
948 
949  // item is only provided if at least 1 feature exists
950  if ( found )
951  texts << txt;
952  }
953 
954  texts.sort();
955  cb->addItems( texts );
956 
957  cb->setEnabled( true );
958  cb->blockSignals( false );
959 
960  ccb = cb;
961  }
962  }
963  }
964 
965  if ( ! filterExpression.isEmpty() && ! filters.values().isEmpty() )
966  filterExpression += QLatin1String( " AND " );
967 
968  filterExpression += filters.isEmpty() ? QString() : QStringLiteral( " ( " );
969  filterExpression += filters.values().join( QLatin1String( " AND " ) );
970  filterExpression += filters.isEmpty() ? QString() : QStringLiteral( " ) " );
971 
973 }
974 
975 void QgsRelationReferenceWidget::addEntry()
976 {
977  if ( !mReferencedLayer )
978  return;
979 
980  const QgsVectorLayerTools *tools = mEditorContext.vectorLayerTools();
981  if ( !tools )
982  return;
983  if ( !mCanvas )
984  return;
985 
986  // no geometry, skip the digitizing
987  if ( mReferencedLayer->geometryType() == QgsWkbTypes::UnknownGeometry || mReferencedLayer->geometryType() == QgsWkbTypes::NullGeometry )
988  {
989  QgsFeature f( mReferencedLayer->fields() );
990  entryAdded( f );
991  return;
992  }
993 
994  mMapToolDigitize->setLayer( mReferencedLayer );
995  setMapTool( mMapToolDigitize );
996 
997  connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationReferenceWidget::entryAdded );
998  connect( mCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationReferenceWidget::onKeyPressed );
999 
1000  if ( mMessageBar )
1001  {
1002  QString title = tr( "Relation %1 for %2." ).arg( mRelation.name(), mReferencingLayer->name() );
1003 
1004  QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( mReferencingLayer, mFormFeature );
1005  QString msg = tr( "Link feature to %1 \"%2\" : Digitize the geometry for the new feature on layer %3. Press &lt;ESC&gt; to cancel." )
1006  .arg( mReferencingLayer->name(), displayString, mReferencedLayer->name() );
1007  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
1008  mMessageBar->pushItem( mMessageBarItem );
1009  }
1010 
1011 }
1012 
1013 void QgsRelationReferenceWidget::entryAdded( const QgsFeature &feat )
1014 {
1015  QgsFeature f( feat );
1016  QgsAttributeMap attributes;
1017 
1018  // if custom text is in the combobox and the displayExpression is simply a field, use the current text for the new feature
1019  if ( mComboBox->itemText( mComboBox->currentIndex() ) != mComboBox->currentText() )
1020  {
1021  int fieldIdx = mReferencedLayer->fields().lookupField( mReferencedLayer->displayExpression() );
1022 
1023  if ( fieldIdx != -1 )
1024  {
1025  attributes.insert( fieldIdx, mComboBox->currentText() );
1026  }
1027  }
1028 
1029  if ( mEditorContext.vectorLayerTools()->addFeature( mReferencedLayer, attributes, f.geometry(), &f ) )
1030  {
1031  QVariantList attrs;
1032  for ( const QString &fieldName : qgis::as_const( mReferencedFields ) )
1033  attrs << f.attribute( fieldName );
1034 
1035  setForeignKeys( attrs );
1036 
1037  mAddEntryButton->setEnabled( false );
1038  }
1039 
1040  unsetMapTool();
1041 }
1042 
1043 void QgsRelationReferenceWidget::updateAddEntryButton()
1044 {
1045  mAddEntryButton->setVisible( mAllowAddFeatures && mMapToolDigitize );
1046  mAddEntryButton->setEnabled( mReferencedLayer && mReferencedLayer->isEditable() );
1047 }
1048 
1049 void QgsRelationReferenceWidget::disableChainedComboBoxes( const QComboBox *scb )
1050 {
1051  QComboBox *ccb = nullptr;
1052  const auto constMFilterComboBoxes = mFilterComboBoxes;
1053  for ( QComboBox *cb : constMFilterComboBoxes )
1054  {
1055  if ( !ccb )
1056  {
1057  if ( cb == scb )
1058  {
1059  ccb = cb;
1060  }
1061 
1062  continue;
1063  }
1064 
1065  cb->setCurrentIndex( 0 );
1066  if ( ccb->currentIndex() == 0 )
1067  {
1068  cb->setEnabled( false );
1069  }
1070 
1071  ccb = cb;
1072  }
1073 }
1074 
1075 void QgsRelationReferenceWidget::emitForeignKeysChanged( const QVariantList &foreignKeys, bool force )
1076 {
1077  if ( foreignKeys == mForeignKeys && force == false && qVariantListIsNull( foreignKeys ) == qVariantListIsNull( mForeignKeys ) )
1078  return;
1079 
1080  mForeignKeys = foreignKeys;
1082  emit foreignKeyChanged( foreignKeys.at( 0 ) );
1085 }
1086 
1088 {
1089  return mReferencedLayerName;
1090 }
1091 
1092 void QgsRelationReferenceWidget::setReferencedLayerName( const QString &relationLayerName )
1093 {
1094  mReferencedLayerName = relationLayerName;
1095 }
1096 
1098 {
1099  return mReferencedLayerId;
1100 }
1101 
1102 void QgsRelationReferenceWidget::setReferencedLayerId( const QString &relationLayerId )
1103 {
1104  mReferencedLayerId = relationLayerId;
1105 }
1106 
1108 {
1109  return mReferencedLayerProviderKey;
1110 }
1111 
1112 void QgsRelationReferenceWidget::setReferencedLayerProviderKey( const QString &relationProviderKey )
1113 {
1114  mReferencedLayerProviderKey = relationProviderKey;
1115 }
1116 
1118 {
1119  return mReferencedLayerDataSource;
1120 }
1121 
1122 void QgsRelationReferenceWidget::setReferencedLayerDataSource( const QString &relationDataSource )
1123 {
1124  const QgsPathResolver resolver { QgsProject::instance()->pathResolver() };
1125  mReferencedLayerDataSource = resolver.writePath( relationDataSource );
1126 }
1127 
1129 {
1130  mFormFeature = formFeature;
1131 }
void reset(T *p=nullptr)
Will reset the managed pointer to p.
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
This class contains context information for attribute editor widgets.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
Returns the associated CAD dock widget (e.g.
@ Single
When showing a single feature (e.g. district information when looking at the form of a house)
@ Embed
A form was embedded as a widget on another form.
@ StandaloneDialog
A form was opened as a new dialog.
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
A vector of attributes.
Definition: qgsattributes.h:58
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This offers a combobox with autocompleter that allows selecting features from a layer.
void setIdentifierValues(const QVariantList &identifierValues)
The identifier values of the currently selected feature.
void setDisplayExpression(const QString &displayExpression)
The display expression will be used to display features as well as the value to match the typed text ...
void setFilterExpression(const QString &filterExpression)
An additional expression to further restrict the available features.
void setIdentifierFields(const QStringList &identifierFields)
Field name that will be used to uniquely identify the current feature.
void setSourceLayer(QgsVectorLayer *sourceLayer)
The layer from which features should be listed.
QgsFeatureRequest currentFeatureRequest() const
Shorthand for getting a feature request to query the currently selected feature.
void setIdentifierValuesToNull()
Sets the identifier values of the currently selected feature to NULL value(s).
void setCurrentFeature(const QgsFeature &feature)
Sets the current index by using the given feature.
void setAllowNull(bool allowNull)
Determines if a NULL value should be available in the list.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets feature ID that should be fetched.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:195
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:204
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:190
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:287
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QVariant::Type type
Definition: qgsfield.h:58
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsPointXY asPoint() const
Returns the contents of the geometry as a 2-dimensional point.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
A class for highlight features on the map.
Definition: qgshighlight.h:59
static void styleHighlight(QgsHighlight *highlight)
Applies style from the settings to the highlight.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:86
void setExtent(const QgsRectangle &r, bool magnified=false)
Sets the extent of the map canvas to the specified rectangle.
void zoomByFactor(double scaleFactor, const QgsPointXY *center=nullptr, bool ignoreScaleLock=false)
Zoom with the factor supplied.
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void keyPressed(QKeyEvent *e)
Emit key press event.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void refresh()
Repaints the canvas map.
QString name
Definition: qgsmaplayer.h:88
QString providerType() const
Returns the provider type (provider key) for this layer.
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsPointXY layerToMapCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from layer's CRS to output CRS
This tool digitizes geometry of new point/line/polygon features on already existing vector layers Onc...
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
void setLayer(QgsMapLayer *vl)
Change the layer edited by the map tool.
The QgsMapToolIdentifyFeature class is a map tool to identify a feature on a chosen layer.
void featureIdentified(const QgsFeature &)
void setLayer(QgsVectorLayer *vl)
change the layer used by the map tool to identify
Abstract base class for all map tools.
Definition: qgsmaptool.h:66
void deactivated()
signal emitted once the map tool is deactivated
void setButton(QAbstractButton *button)
Use this to associate a button to this maptool.
Definition: qgsmaptool.cpp:136
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar, after hiding the currently visible one and putting it in a stack.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Resolves relative paths into absolute paths and vice versa.
A class to represent a 2D point.
Definition: qgspointxy.h:44
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Definition: qgsrectangle.h:235
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:342
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
void showEvent(QShowEvent *e) override
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.
QString referencedLayerDataSource() const
Returns the public data source of the referenced layer.
void foreignKeysChanged(const QVariantList &)
Emitted when the foreign keys changed.
bool allowAddFeatures() const
Determines if a button for adding new features should be shown.
Q_DECL_DEPRECATED void setForeignKey(const QVariant &value)
this sets the related feature using from the foreign key
QString referencedLayerProviderKey() const
Returns the data provider key of the referenced layer.
void setChainFilters(bool chainFilters)
Set if filters are chained.
QString referencedLayerId() const
Returns the id of the referenced layer.
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 openForm()
open the form of the related feature in a new dialog
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.
Q_DECL_DEPRECATED void foreignKeyChanged(const QVariant &)
Emitted when the foreign key changed.
void setAllowMapIdentification(bool allowMapIdentification)
bool allowMapIdentification()
determines if the widget offers the possibility to select the related feature on the map (using a ded...
bool orderByValue()
If the widget will order the combobox entries by value.
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.
bool chainFilters() const
Determines if the filters are chained.
QVariantList foreignKeys() const
returns the related feature foreign key
void setForeignKeys(const QVariantList &values)
Sets the related feature using the foreign keys.
Q_DECL_DEPRECATED QVariant foreignKey() const
returns the related feature foreign key
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)
QgsFeature referencedFeature() const
Returns the related feature (from the referenced layer) if no feature is related, it returns an inval...
void mapIdentification()
activate the map tool to select a new related feature on the map
void setAllowAddFeatures(bool allowAddFeatures)
Determines if a button for adding new features should be shown.
void deleteForeignKeys()
unset the currently related feature
QString referencedLayerName() const
Returns the name of the referenced layer.
void setFormFeature(const QgsFeature &formFeature)
Set the current form feature (from the referencing layer)
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:89
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QString name
Definition: qgsrelation.h:49
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
bool isValid
Definition: qgsrelation.h:50
QString referencedLayerId() const
Access the referenced (parent) layer's id.
Methods in this class are used to handle basic operations on vector layers.
virtual bool addFeature(QgsVectorLayer *layer, const QgsAttributeMap &defaultValues=QgsAttributeMap(), const QgsGeometry &defaultGeometry=QgsGeometry(), QgsFeature *feature=nullptr) const =0
This method should/will be called, whenever a new feature will be added to the layer.
static QString getFeatureDisplayString(const QgsVectorLayer *layer, const QgsFeature &feature)
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void editingStarted()
Emitted when editing on this layer has started.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QString displayExpression
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:121
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:798
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:797
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:263
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
QList< int > QgsAttributeList
Definition: qgsfield.h:26
bool qVariantListIsNull(const QVariantList &list)