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