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