QGIS API Documentation  2.14.11-Essen
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 
23 #include "qgsattributeform.h"
24 #include "qgsattributedialog.h"
25 #include "qgsapplication.h"
26 #include "qgscollapsiblegroupbox.h"
27 #include "qgseditorwidgetfactory.h"
28 #include "qgsexpression.h"
29 #include "qgsfield.h"
30 #include "qgsgeometry.h"
31 #include "qgsmapcanvas.h"
32 #include "qgsmessagebar.h"
34 #include "qgsvectorlayer.h"
35 #include "qgsattributetablemodel.h"
36 
38  : QWidget( parent )
39  , mEditorContext( QgsAttributeEditorContext() )
40  , mCanvas( nullptr )
41  , mMessageBar( nullptr )
42  , mForeignKey( QVariant() )
43  , mReferencedFieldIdx( -1 )
44  , mReferencingFieldIdx( -1 )
45  , mAllowNull( true )
46  , mHighlight( nullptr )
47  , mMapTool( nullptr )
48  , mMessageBarItem( nullptr )
49  , mRelationName( "" )
50  , mReferencedAttributeForm( nullptr )
51  , mReferencedLayer( nullptr )
52  , mReferencingLayer( nullptr )
53  , mMasterModel( nullptr )
54  , mFilterModel( nullptr )
55  , mFeatureListModel( nullptr )
56  , mWindowWidget( nullptr )
57  , mShown( false )
58  , mIsEditable( true )
59  , mEmbedForm( false )
60  , mReadOnlySelector( false )
61  , mAllowMapIdentification( false )
62  , mOrderByValue( false )
63  , mOpenFormButtonVisible( true )
64  , mChainFilters( false )
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  // combobox (for non-geometric relation)
90  mComboBox = new QComboBox( this );
91  mChooserContainer->layout()->addWidget( mComboBox );
92 
93  // read-only line edit
94  mLineEdit = new QLineEdit( this );
95  mLineEdit->setReadOnly( true );
96  editLayout->addWidget( mLineEdit );
97 
98  // open form button
99  mOpenFormButton = new QToolButton( this );
100  mOpenFormButton->setIcon( QgsApplication::getThemeIcon( "/mActionPropertyItem.png" ) );
101  mOpenFormButton->setText( tr( "Open related feature form" ) );
102  editLayout->addWidget( mOpenFormButton );
103 
104  // highlight button
105  mHighlightFeatureButton = new QToolButton( this );
106  mHighlightFeatureButton->setPopupMode( QToolButton::MenuButtonPopup );
107  mHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionHighlightFeature.svg" ), tr( "Highlight feature" ), this );
108  mScaleHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionScaleHighlightFeature.svg" ), tr( "Scale and highlight feature" ), this );
109  mPanHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionPanHighlightFeature.svg" ), tr( "Pan and highlight feature" ), this );
110  mHighlightFeatureButton->addAction( mHighlightFeatureAction );
111  mHighlightFeatureButton->addAction( mScaleHighlightFeatureAction );
112  mHighlightFeatureButton->addAction( mPanHighlightFeatureAction );
113  mHighlightFeatureButton->setDefaultAction( mHighlightFeatureAction );
114  editLayout->addWidget( mHighlightFeatureButton );
115 
116  // map identification button
117  mMapIdentificationButton = new QToolButton( this );
118  mMapIdentificationButton->setIcon( QgsApplication::getThemeIcon( "/mActionMapIdentification.svg" ) );
119  mMapIdentificationButton->setText( tr( "Select on map" ) );
120  mMapIdentificationButton->setCheckable( true );
121  editLayout->addWidget( mMapIdentificationButton );
122 
123  // remove foreign key button
124  mRemoveFKButton = new QToolButton( this );
125  mRemoveFKButton->setIcon( QgsApplication::getThemeIcon( "/mActionRemove.svg" ) );
126  mRemoveFKButton->setText( tr( "No selection" ) );
127  editLayout->addWidget( mRemoveFKButton );
128 
129  // add line to top layout
130  mTopLayout->addLayout( editLayout );
131 
132  // embed form
133  mAttributeEditorFrame = new QgsCollapsibleGroupBox( this );
134  mAttributeEditorLayout = new QVBoxLayout( mAttributeEditorFrame );
135  mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
136  mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
137  mTopLayout->addWidget( mAttributeEditorFrame );
138 
139  // invalid label
140  mInvalidLabel = new QLabel( tr( "The relation is not valid. Please make sure your relation definitions are ok." ) );
141  mInvalidLabel->setWordWrap( true );
142  QFont font = mInvalidLabel->font();
143  font.setItalic( true );
144  mInvalidLabel->setStyleSheet( "QLabel { color: red; } " );
145  mInvalidLabel->setFont( font );
146  mTopLayout->addWidget( mInvalidLabel );
147 
148  // default mode is combobox, no geometric relation and no embed form
149  mLineEdit->hide();
150  mMapIdentificationButton->hide();
151  mHighlightFeatureButton->hide();
152  mAttributeEditorFrame->hide();
153  mInvalidLabel->hide();
154 
155  // connect buttons
156  connect( mOpenFormButton, SIGNAL( clicked() ), this, SLOT( openForm() ) );
157  connect( mHighlightFeatureButton, SIGNAL( triggered( QAction* ) ), this, SLOT( highlightActionTriggered( QAction* ) ) );
158  connect( mMapIdentificationButton, SIGNAL( clicked() ), this, SLOT( mapIdentification() ) );
159  connect( mRemoveFKButton, SIGNAL( clicked() ), this, SLOT( deleteForeignKey() ) );
160 }
161 
163 {
164  deleteHighlight();
165  unsetMapTool();
166  if ( mMapTool )
167  delete mMapTool;
168 }
169 
170 void QgsRelationReferenceWidget::setRelation( const QgsRelation& relation, bool allowNullValue )
171 {
172  mAllowNull = allowNullValue;
173  mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
174 
175  if ( relation.isValid() )
176  {
177  mInvalidLabel->hide();
178 
179  mRelation = relation;
180  mReferencingLayer = relation.referencingLayer();
181  mRelationName = relation.name();
182  mReferencedLayer = relation.referencedLayer();
183  mReferencedFieldIdx = mReferencedLayer->fieldNameIndex( relation.fieldPairs().at( 0 ).second );
184  mReferencingFieldIdx = mReferencingLayer->fieldNameIndex( relation.fieldPairs().at( 0 ).first );
185 
187 
188  if ( mEmbedForm )
189  {
190  mAttributeEditorFrame->setTitle( mReferencedLayer->name() );
191  mReferencedAttributeForm = new QgsAttributeForm( relation.referencedLayer(), QgsFeature(), context, this );
192  mReferencedAttributeForm->hideButtonBox();
193  mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
194  }
195  }
196  else
197  {
198  mInvalidLabel->show();
199  }
200 
201  if ( mShown && isVisible() )
202  {
203  init();
204  }
205 }
206 
208 {
209  if ( !editable )
210  unsetMapTool();
211 
212  mFilterContainer->setEnabled( editable );
213  mComboBox->setEnabled( editable );
214  mMapIdentificationButton->setEnabled( editable );
215  mRemoveFKButton->setEnabled( editable );
216  mIsEditable = editable;
217 }
218 
220 {
221  if ( !value.isValid() || value.isNull() )
222  {
224  return;
225  }
226 
227  if ( !mReferencedLayer )
228  return;
229 
230  // Attributes from the referencing layer
231  QgsAttributes attrs = QgsAttributes( mReferencingLayer->fields().count() );
232  // Set the value on the foreign key field of the referencing record
233  attrs[ mReferencingLayer->fieldNameIndex( mRelation.fieldPairs().at( 0 ).first )] = value;
234 
235  QgsFeatureRequest request = mRelation.getReferencedFeatureRequest( attrs );
236 
237  mReferencedLayer->getFeatures( request ).nextFeature( mFeature );
238 
239  if ( !mFeature.isValid() )
240  {
242  return;
243  }
244 
245  mForeignKey = mFeature.attribute( mReferencedFieldIdx );
246 
247  if ( mReadOnlySelector )
248  {
249  QgsExpression expr( mReferencedLayer->displayExpression() );
250  QgsExpressionContext context;
253  << QgsExpressionContextUtils::layerScope( mReferencedLayer );
254  context.setFeature( mFeature );
255  QString title = expr.evaluate( &context ).toString();
256  if ( expr.hasEvalError() )
257  {
258  title = mFeature.attribute( mReferencedFieldIdx ).toString();
259  }
260  mLineEdit->setText( title );
261  }
262  else
263  {
264  int i = mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole );
265  if ( i == -1 && mAllowNull )
266  {
267  mComboBox->setCurrentIndex( 0 );
268  }
269  else
270  {
271  mComboBox->setCurrentIndex( i );
272  }
273  }
274 
275  mRemoveFKButton->setEnabled( mIsEditable );
276  highlightFeature( mFeature );
277  updateAttributeEditorFrame( mFeature );
278  emit foreignKeyChanged( foreignKey() );
279 }
280 
282 {
283  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
284  if ( mReadOnlySelector )
285  {
286  QString nullText = "";
287  if ( mAllowNull )
288  {
289  nullText = tr( "%1 (no selection)" ).arg( nullValue.toString() );
290  }
291  mLineEdit->setText( nullText );
292  mForeignKey = QVariant();
293  mFeature.setValid( false );
294  }
295  else
296  {
297  if ( mAllowNull )
298  {
299  mComboBox->setCurrentIndex( 0 );
300  }
301  else
302  {
303  mComboBox->setCurrentIndex( -1 );
304  }
305  }
306  mRemoveFKButton->setEnabled( false );
307  updateAttributeEditorFrame( QgsFeature() );
308  emit foreignKeyChanged( QVariant( QVariant::Int ) );
309 }
310 
312 {
313  QgsFeature f;
314  if ( mReferencedLayer )
315  {
316  QgsFeatureId fid;
317  if ( mReadOnlySelector )
318  {
319  fid = mFeature.id();
320  }
321  else
322  {
323  fid = mComboBox->itemData( mComboBox->currentIndex(), QgsAttributeTableModel::FeatureIdRole ).value<QgsFeatureId>();
324  }
325  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( f );
326  }
327  return f;
328 }
329 
331 {
332  if ( mReadOnlySelector )
333  {
334  return mForeignKey;
335  }
336  else
337  {
338  if ( mReferencingFieldIdx < 0 || mReferencingFieldIdx >= mReferencingLayer->fields().count() )
339  {
340  return QVariant();
341  }
342  else if ( !mFeature.isValid() )
343  {
344  return QVariant( mReferencingLayer->fields().at( mReferencingFieldIdx ).type() );
345  }
346  else
347  {
348  return mFeature.attribute( mReferencedFieldIdx );
349  }
350  }
351 }
352 
354 {
355  mEditorContext = context;
356  mCanvas = canvas;
357  mMessageBar = messageBar;
358 
359  if ( mMapTool )
360  delete mMapTool;
361  mMapTool = new QgsMapToolIdentifyFeature( mCanvas );
362  mMapTool->setButton( mMapIdentificationButton );
363 }
364 
366 {
367  if ( display )
368  {
369  setSizePolicy( sizePolicy().horizontalPolicy(), QSizePolicy::MinimumExpanding );
370  mTopLayout->setAlignment( Qt::AlignTop );
371  }
372 
373  mAttributeEditorFrame->setVisible( display );
374  mEmbedForm = display;
375 }
376 
378 {
379  mChooserContainer->setHidden( readOnly );
380  mLineEdit->setVisible( readOnly );
381  mRemoveFKButton->setVisible( mAllowNull && readOnly );
382  mReadOnlySelector = readOnly;
383 }
384 
386 {
387  mHighlightFeatureButton->setVisible( allowMapIdentification );
388  mMapIdentificationButton->setVisible( allowMapIdentification );
389  mAllowMapIdentification = allowMapIdentification;
390 }
391 
393 {
394  mOrderByValue = orderByValue;
395 }
396 
398 {
399  mFilterFields = filterFields;
400 }
401 
403 {
404  mOpenFormButton->setVisible( openFormButtonVisible );
405  mOpenFormButtonVisible = openFormButtonVisible;
406 }
407 
409 {
410  mChainFilters = chainFilters;
411 }
412 
414 {
415  Q_UNUSED( e )
416 
417  mShown = true;
418 
419  init();
420 }
421 
423 {
424  if ( !mReadOnlySelector && mComboBox->count() == 0 && mReferencedLayer )
425  {
426  QApplication::setOverrideCursor( Qt::WaitCursor );
427 
428  QSet<QString> requestedAttrs;
429 
430  QgsVectorLayerCache* layerCache = new QgsVectorLayerCache( mReferencedLayer, 100000, this );
431 
432  if ( !mFilterFields.isEmpty() )
433  {
434  Q_FOREACH ( const QString& fieldName, mFilterFields )
435  {
436  QVariantList uniqueValues;
437  int idx = mReferencedLayer->fieldNameIndex( fieldName );
438  QComboBox* cb = new QComboBox();
439  cb->setProperty( "Field", fieldName );
440  mFilterComboBoxes << cb;
441  mReferencedLayer->uniqueValues( idx, uniqueValues );
442  cb->addItem( mReferencedLayer->attributeAlias( idx ).isEmpty() ? fieldName : mReferencedLayer->attributeAlias( idx ) );
443  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
444  cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->fields().at( idx ).type() ) );
445 
446  Q_FOREACH ( const QVariant& v, uniqueValues )
447  {
448  cb->addItem( v.toString(), v );
449  }
450 
451  connect( cb, SIGNAL( currentIndexChanged( int ) ), this, SLOT( filterChanged() ) );
452 
453  // Request this attribute for caching
454  requestedAttrs << fieldName;
455 
456  mFilterLayout->addWidget( cb );
457  }
458 
459  if ( mChainFilters )
460  {
461  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
462 
463  QgsFeature ft;
464  QgsFeatureIterator fit = layerCache->getFeatures();
465  while ( fit.nextFeature( ft ) )
466  {
467  for ( int i = 0; i < mFilterComboBoxes.count() - 1; ++i )
468  {
469  QVariant cv = ft.attribute( mFilterFields[i] );
470  QVariant nv = ft.attribute( mFilterFields[i + 1] );
471  QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
472  QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
473  mFilterCache[mFilterFields[i]][cf] << nf;
474  }
475  }
476  }
477  }
478  else
479  {
480  mFilterContainer->hide();
481  }
482 
483  QgsExpression exp( mReferencedLayer->displayExpression() );
484 
485  requestedAttrs += exp.referencedColumns().toSet();
486  requestedAttrs << mRelation.fieldPairs().at( 0 ).second;
487 
488  QgsAttributeList attributes;
489  Q_FOREACH ( const QString& attr, requestedAttrs )
490  attributes << mReferencedLayer->fieldNameIndex( attr );
491 
492  layerCache->setCacheSubsetOfAttributes( attributes );
493  mMasterModel = new QgsAttributeTableModel( layerCache );
494  mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->fields() ) );
495  mFilterModel = new QgsAttributeTableFilterModel( mCanvas, mMasterModel, mMasterModel );
496  mFeatureListModel = new QgsFeatureListModel( mFilterModel, this );
497  mFeatureListModel->setDisplayExpression( mReferencedLayer->displayExpression() );
498 
499  mMasterModel->loadLayer();
500 
501  mFeatureListModel->setInjectNull( mAllowNull );
502  if ( mOrderByValue )
503  {
504  const QStringList referencedColumns = QgsExpression( mReferencedLayer->displayExpression() ).referencedColumns();
505  if ( !referencedColumns.isEmpty() )
506  {
507  int sortIdx = mReferencedLayer->fieldNameIndex( referencedColumns.first() );
508  mFilterModel->sort( sortIdx );
509  }
510  }
511 
512  mComboBox->setModel( mFeatureListModel );
513 
514  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
515 
516  if ( mChainFilters && mFeature.isValid() )
517  {
518  for ( int i = 0; i < mFilterFields.size(); i++ )
519  {
520  QVariant v = mFeature.attribute( mFilterFields[i] );
521  QString f = v.isNull() ? nullValue.toString() : v.toString();
522  mFilterComboBoxes.at( i )->setCurrentIndex( mFilterComboBoxes.at( i )->findText( f ) );
523  }
524  }
525 
526  mComboBox->setCurrentIndex( mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole ) );
527 
528  // Only connect after iterating, to have only one iterator on the referenced table at once
529  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( comboReferenceChanged( int ) ) );
531  }
532 }
533 
534 void QgsRelationReferenceWidget::highlightActionTriggered( QAction* action )
535 {
536  if ( action == mHighlightFeatureAction )
537  {
538  highlightFeature();
539  }
540  else if ( action == mScaleHighlightFeatureAction )
541  {
542  highlightFeature( QgsFeature(), Scale );
543  }
544  else if ( action == mPanHighlightFeatureAction )
545  {
546  highlightFeature( QgsFeature(), Pan );
547  }
548 }
549 
551 {
552  QgsFeature feat = referencedFeature();
553 
554  if ( !feat.isValid() )
555  return;
556 
558  QgsAttributeDialog attributeDialog( mReferencedLayer, new QgsFeature( feat ), true, this, true, context );
559  attributeDialog.exec();
560 }
561 
562 void QgsRelationReferenceWidget::highlightFeature( QgsFeature f, CanvasExtent canvasExtent )
563 {
564  if ( !mCanvas )
565  return;
566 
567  if ( !f.isValid() )
568  {
569  f = referencedFeature();
570  if ( !f.isValid() )
571  return;
572  }
573 
574  if ( !f.constGeometry() )
575  {
576  return;
577  }
578 
579  const QgsGeometry* geom = f.constGeometry();
580 
581  // scale or pan
582  if ( canvasExtent == Scale )
583  {
584  QgsRectangle featBBox = geom->boundingBox();
585  featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox );
586  QgsRectangle extent = mCanvas->extent();
587  if ( !extent.contains( featBBox ) )
588  {
589  extent.combineExtentWith( &featBBox );
590  extent.scale( 1.1 );
591  mCanvas->setExtent( extent );
592  mCanvas->refresh();
593  }
594  }
595  else if ( canvasExtent == Pan )
596  {
597  QgsGeometry* centroid = geom->centroid();
598  QgsPoint center = centroid->asPoint();
599  delete centroid;
600  center = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, center );
601  mCanvas->zoomByFactor( 1.0, &center ); // refresh is done in this method
602  }
603 
604  // highlight
605  deleteHighlight();
606  mHighlight = new QgsHighlight( mCanvas, f, mReferencedLayer );
607  QSettings settings;
608  QColor color = QColor( settings.value( "/Map/highlight/color", QGis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
609  int alpha = settings.value( "/Map/highlight/colorAlpha", QGis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
610  double buffer = settings.value( "/Map/highlight/buffer", QGis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
611  double minWidth = settings.value( "/Map/highlight/minWidth", QGis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
612 
613  mHighlight->setColor( color ); // sets also fill with default alpha
614  color.setAlpha( alpha );
615  mHighlight->setFillColor( color ); // sets fill with alpha
616  mHighlight->setBuffer( buffer );
617  mHighlight->setMinWidth( minWidth );
618  mHighlight->show();
619 
620  QTimer* timer = new QTimer( this );
621  timer->setSingleShot( true );
622  connect( timer, SIGNAL( timeout() ), this, SLOT( deleteHighlight() ) );
623  timer->start( 3000 );
624 }
625 
626 void QgsRelationReferenceWidget::deleteHighlight()
627 {
628  if ( mHighlight )
629  {
630  mHighlight->hide();
631  delete mHighlight;
632  }
633  mHighlight = nullptr;
634 }
635 
637 {
638  if ( !mAllowMapIdentification || !mReferencedLayer )
639  return;
640 
641  const QgsVectorLayerTools* tools = mEditorContext.vectorLayerTools();
642  if ( !tools )
643  return;
644  if ( !mCanvas )
645  return;
646 
647  mMapTool->setLayer( mReferencedLayer );
648  mCanvas->setMapTool( mMapTool );
649 
650  mWindowWidget = window();
651 
652  mCanvas->window()->raise();
653  mCanvas->activateWindow();
654  mCanvas->setFocus();
655 
656  connect( mMapTool, SIGNAL( featureIdentified( QgsFeature ) ), this, SLOT( featureIdentified( const QgsFeature ) ) );
657  connect( mMapTool, SIGNAL( deactivated() ), this, SLOT( mapToolDeactivated() ) );
658 
659  if ( mMessageBar )
660  {
661  QString title = QString( "Relation %1 for %2." ).arg( mRelationName, mReferencingLayer->name() );
662  QString msg = tr( "Identify a feature of %1 to be associated. Press &lt;ESC&gt; to cancel." ).arg( mReferencedLayer->name() );
663  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
664  mMessageBar->pushItem( mMessageBarItem );
665  }
666 }
667 
668 void QgsRelationReferenceWidget::comboReferenceChanged( int index )
669 {
671  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( mFeature );
672  highlightFeature( mFeature );
673  updateAttributeEditorFrame( mFeature );
674  emit foreignKeyChanged( mFeature.attribute( mReferencedFieldIdx ) );
675 }
676 
677 void QgsRelationReferenceWidget::updateAttributeEditorFrame( const QgsFeature& feature )
678 {
679  // Check if we're running with an embedded frame we need to update
680  if ( mAttributeEditorFrame )
681  {
682  if ( mReferencedAttributeForm )
683  {
684  mReferencedAttributeForm->setFeature( feature );
685  }
686  }
687 }
688 
689 void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
690 {
691  if ( mReadOnlySelector )
692  {
693  QgsExpression expr( mReferencedLayer->displayExpression() );
694  QgsExpressionContext context;
697  << QgsExpressionContextUtils::layerScope( mReferencedLayer );
698  context.setFeature( feature );
699  QString title = expr.evaluate( &context ).toString();
700  if ( expr.hasEvalError() )
701  {
702  title = feature.attribute( mReferencedFieldIdx ).toString();
703  }
704  mLineEdit->setText( title );
705  mForeignKey = feature.attribute( mReferencedFieldIdx );
706  mFeature = feature;
707  }
708  else
709  {
710  mComboBox->setCurrentIndex( mComboBox->findData( feature.id(), QgsAttributeTableModel::FeatureIdRole ) );
711  mFeature = feature;
712  }
713 
714  mRemoveFKButton->setEnabled( mIsEditable );
715  highlightFeature( feature );
716  updateAttributeEditorFrame( feature );
717  emit foreignKeyChanged( foreignKey() );
718 
719  unsetMapTool();
720 }
721 
722 void QgsRelationReferenceWidget::unsetMapTool()
723 {
724  // deactivate map tool if activated
725  if ( mCanvas && mMapTool )
726  {
727  /* this will call mapToolDeactivated */
728  mCanvas->unsetMapTool( mMapTool );
729  }
730 }
731 
732 void QgsRelationReferenceWidget::mapToolDeactivated()
733 {
734  if ( mWindowWidget )
735  {
736  mWindowWidget->raise();
737  mWindowWidget->activateWindow();
738  }
739 
740  if ( mMessageBar && mMessageBarItem )
741  {
742  mMessageBar->popWidget( mMessageBarItem );
743  }
744  mMessageBarItem = nullptr;
745 }
746 
747 void QgsRelationReferenceWidget::filterChanged()
748 {
749  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
750 
751  QStringList filters;
752  QgsAttributeList attrs;
753 
754  QComboBox* scb = qobject_cast<QComboBox*>( sender() );
755 
756  Q_ASSERT( scb );
757 
758  if ( mChainFilters )
759  {
760  QComboBox* ccb = nullptr;
761  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
762  {
763  if ( !ccb )
764  {
765  if ( cb == scb )
766  ccb = cb;
767 
768  continue;
769  }
770 
771  if ( ccb->currentIndex() == 0 )
772  {
773  cb->setCurrentIndex( 0 );
774  cb->setEnabled( false );
775  }
776  else
777  {
778  cb->blockSignals( true );
779  cb->clear();
780  cb->addItem( cb->property( "Field" ).toString() );
781 
782  // ccb = scb
783  // cb = scb + 1
784  Q_FOREACH ( const QString& txt, mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] )
785  {
786  cb->addItem( txt );
787  }
788 
789  cb->setEnabled( true );
790  cb->blockSignals( false );
791 
792  ccb = cb;
793  }
794  }
795  }
796 
797  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
798  {
799  if ( cb->currentIndex() != 0 )
800  {
801  const QString fieldName = cb->property( "Field" ).toString();
802 
803  if ( cb->currentText() == nullValue.toString() )
804  {
805  filters << QString( "\"%1\" IS NULL" ).arg( fieldName );
806  }
807  else
808  {
809  if ( mReferencedLayer->fields().field( fieldName ).type() == QVariant::String )
810  {
811  filters << QString( "\"%1\" = '%2'" ).arg( fieldName, cb->currentText() );
812  }
813  else
814  {
815  filters << QString( "\"%1\" = %2" ).arg( fieldName, cb->currentText() );
816  }
817  }
818  attrs << mReferencedLayer->fieldNameIndex( fieldName );
819  }
820  }
821 
822  QString filterExpression = filters.join( " AND " );
823 
824  QgsFeatureIterator it( mMasterModel->layerCache()->getFeatures( QgsFeatureRequest().setFilterExpression( filterExpression ).setSubsetOfAttributes( attrs ) ) );
825 
826  QgsFeature f;
827  QgsFeatureIds featureIds;
828 
829  while ( it.nextFeature( f ) )
830  {
831  featureIds << f.id();
832  }
833 
834  mFilterModel->setFilteredFeatures( featureIds );
835 }
QLayout * layout() const
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
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:199
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
Class for parsing and evaluation of expressions (formerly called "search strings").
const QgsVectorLayerTools * vectorLayerTools() const
Wrapper for iterator of features from vector data provider or vector layer.
bool contains(const QgsRectangle &rect) const
return true when rectangle contains other rectangle
void setStyleSheet(const QString &styleSheet)
bool isValid() const
Returns the validity of this relation.
static unsigned index
static double DEFAULT_HIGHLIGHT_BUFFER_MM
Default highlight buffer in mm.
Definition: qgis.h:239
A rectangle specified with double values.
Definition: qgsrectangle.h:35
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition: qgis.h:235
void setContentsMargins(int left, int top, int right, int bottom)
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
void setLayer(QgsVectorLayer *vl)
change the layer used by the map tool to identify
bool chainFilters() const
Determines if the filters are chained.
bool setDisplayExpression(const QString &expression)
QWidget * window() const
QString name() const
void hideButtonBox()
Hides the button box (Ok/Cancel) and enables auto-commit.
void addAction(QAction *action)
void setText(const QString &)
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
void setFilterFields(const QStringList &filterFields)
Set the fields for which filter comboboxes will be created.
void deleteForeignKey()
unset the currently related feature
void foreignKeyChanged(const QVariant &)
void setDefaultAction(QAction *action)
This class contains context information for attribute editor widgets.
void uniqueValues(int index, QList< QVariant > &uniqueValues, int limit=-1)
Calculates a list of unique values contained within an attribute in the layer.
QObject * sender() const
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
void setOpenFormButtonVisible(bool openFormButtonVisible)
void setExtent(const QgsRectangle &r)
Set the extent of the map canvas.
const T & at(int i) const
void scale(double scaleFactor, const QgsPoint *c=nullptr)
Scale the rectangle around its center point.
void clear()
void setFillColor(const QColor &fillColor)
Set polygons fill color.
virtual void setVisible(bool visible)
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:42
T value() const
int exec()
void refresh()
Repaints the canvas map.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setAlpha(int alpha)
QVariant foreignKey()
returns the related feature foreign key
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
QString join(const QString &separator) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
bool orderByValue()
If the widget will order the combobox entries by value.
const QString displayExpression()
Get the preview expression, used to create a human readable preview string.
void setIcon(const QIcon &icon)
int count() const
Return number of items.
Definition: qgsfield.cpp:365
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the subset of attributes to be cached.
void setAllowMapIdentification(bool allowMapIdentification)
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
int size() const
bool allowMapIdentification()
determines if the widge offers the possibility to select the related feature on the map (using a dedi...
QgsGeometry * centroid() const
Returns the center of mass of a geometry.
void setButton(QAbstractButton *button)
Use this to associate a button to this maptool.
Definition: qgsmaptool.cpp:129
void setForeignKey(const QVariant &value)
this sets the related feature using from the foreign key
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
QgsFields fields() const
Returns the list of fields of this layer.
void addItem(const QString &text, const QVariant &userData)
void setReadOnly(bool)
void setMapTool(QgsMapTool *mapTool)
Sets the map tool currently being used on the canvas.
void setBuffer(double buffer)
Set line / outline buffer in millimeters.
Definition: qgshighlight.h:63
void setOrderByValue(bool orderByValue)
Set if the widget will order the combobox entries by value.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
make out a widget containing a message to be displayed on the bar
void setEnabled(bool)
void addWidget(QWidget *widget, int stretch, QFlags< Qt::AlignmentFlag > alignment)
int count(const T &value) const
void combineExtentWith(QgsRectangle *rect)
expand the rectangle so that covers both the original rectangle and the given rectangle ...
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QVariant property(const char *name) const
void setLayout(QLayout *layout)
int toInt(bool *ok) const
bool isNull() const
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 setFocus()
virtual void showEvent(QShowEvent *e) override
bool isEmpty() const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void raise()
bool isEmpty() const
QgsRectangle extent() const
Returns the current zoom exent of the map canvas.
A class for highlight features on the map.
Definition: qgshighlight.h:36
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void setOverrideCursor(const QCursor &cursor)
QgsVectorLayerCache * layerCache() const
Returns the layer cache this model uses as backend.
void restoreOverrideCursor()
T & first()
void setCheckable(bool)
void hide()
void setRelation(const QgsRelation &relation, bool allowNullValue)
void setSizePolicy(QSizePolicy)
void addWidget(QWidget *w)
A class to represent a point.
Definition: qgspoint.h:65
QVariant itemData(int index, int role) const
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
QList< FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names f...
bool blockSignals(bool block)
const QFont & font() const
QgsVectorLayer * referencedLayer() const
Access the referenced (parent) layer.
This class caches features of a given QgsVectorLayer.
void setItalic(bool enable)
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
The QgsMapToolIdentifyFeature class is a map tool to identify a feature on a chosen layer...
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:204
bool setAlignment(QWidget *w, QFlags< Qt::AlignmentFlag > alignment)
QVariant value(const QString &key, const QVariant &defaultValue) const
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
QString name() const
Get the display name of the layer.
void pushItem(QgsMessageBarItem *item)
Display a message item on the bar after hiding the currently visible one and putting it in a stack...
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer&#39;s CRS to output CRS
int findData(const QVariant &data, int role, QFlags< Qt::MatchFlag > flags) const
void activateWindow()
void setColor(const QColor &color)
Set line/outline to color, polygon fill to color with alpha = 63.
void setModel(QAbstractItemModel *model)
virtual void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
Sort by the given column using the given order.
void setTitle(const QString &title)
QWidget(QWidget *parent, QFlags< Qt::WindowType > f)
QgsRectangle boundingBox() const
Returns the bounding box of this feature.
void setPopupMode(ToolButtonPopupMode mode)
QgsFeature referencedFeature()
return the related feature (from the referenced layer) if no feature is related, it returns an invali...
void setCurrentIndex(int index)
qint64 QgsFeatureId
Definition: qgsfeature.h:31
void setText(const QString &text)
void start(int msec)
bool isValid() const
double toDouble(bool *ok) const
bool setProperty(const char *name, const QVariant &value)
const QgsField & field(int fieldIdx) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:390
void show()
void setInjectNull(bool injectNull)
If true is specified, a NULL value will be injected.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsfeature.h:115
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:89
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
void mapIdentification()
activate the map tool to select a new related feature on the map
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
static double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/outline minimum width in mm.
Definition: qgis.h:243
void setHidden(bool hidden)
void setWordWrap(bool on)
bool openFormButtonVisible()
determines the open form button is visible in the widget
void setSpacing(int spacing)
void openForm()
open the form of the related feature in a new dialog
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QString name() const
Returns a human readable name for this relation.
void setChainFilters(bool chainFilters)
Set if filters are chained.
void zoomByFactor(double scaleFactor, const QgsPoint *center=nullptr)
Zoom with the factor supplied.
QString attributeAlias(int attributeIndex) const
Returns the alias of an attribute name or an empty string if there is no alias.
A form was embedded as a widget on another form.
void addLayout(QLayout *layout, int stretch)
void setMinWidth(double width)
Set minimum line / outline width in millimeters.
Definition: qgshighlight.h:67
void setSingleShot(bool singleShot)