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