QGIS API Documentation  2.12.0-Lyon
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  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  , mChainFilters( false )
83 {
84  mTopLayout = new QVBoxLayout( this );
85  mTopLayout->setContentsMargins( 0, 0, 0, 0 );
86  mTopLayout->setAlignment( Qt::AlignTop );
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  // spacer
146  editLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
147 
148  // add line to top layout
149  mTopLayout->addLayout( editLayout );
150 
151  // embed form
152  mAttributeEditorFrame = new QgsCollapsibleGroupBox( this );
153  mAttributeEditorLayout = new QVBoxLayout( mAttributeEditorFrame );
154  mAttributeEditorFrame->setLayout( mAttributeEditorLayout );
155  mAttributeEditorFrame->setSizePolicy( mAttributeEditorFrame->sizePolicy().horizontalPolicy(), QSizePolicy::Expanding );
156  mTopLayout->addWidget( mAttributeEditorFrame );
157 
158  // invalid label
159  mInvalidLabel = new QLabel( tr( "The relation is not valid. Please make sure your relation definitions are ok." ) );
160  mInvalidLabel->setWordWrap( true );
161  QFont font = mInvalidLabel->font();
162  font.setItalic( true );
163  mInvalidLabel->setStyleSheet( "QLabel { color: red; } " );
164  mInvalidLabel->setFont( font );
165  mTopLayout->addWidget( mInvalidLabel );
166 
167  // default mode is combobox, no geometric relation and no embed form
168  mLineEdit->hide();
169  mMapIdentificationButton->hide();
170  mHighlightFeatureButton->hide();
171  mAttributeEditorFrame->hide();
172  mInvalidLabel->hide();
173 
174  // connect buttons
175  connect( mOpenFormButton, SIGNAL( clicked() ), this, SLOT( openForm() ) );
176  connect( mHighlightFeatureButton, SIGNAL( triggered( QAction* ) ), this, SLOT( highlightActionTriggered( QAction* ) ) );
177  connect( mMapIdentificationButton, SIGNAL( clicked() ), this, SLOT( mapIdentification() ) );
178  connect( mRemoveFKButton, SIGNAL( clicked() ), this, SLOT( deleteForeignKey() ) );
179 }
180 
182 {
183  deleteHighlight();
184  unsetMapTool();
185  if ( mMapTool )
186  delete mMapTool;
187 }
188 
189 void QgsRelationReferenceWidget::setRelation( const QgsRelation& relation, bool allowNullValue )
190 {
191  mAllowNull = allowNullValue;
192  mRemoveFKButton->setVisible( allowNullValue && mReadOnlySelector );
193 
194  if ( relation.isValid() )
195  {
196  mInvalidLabel->hide();
197 
198  mRelation = relation;
199  mReferencingLayer = relation.referencingLayer();
200  mRelationName = relation.name();
201  mReferencedLayer = relation.referencedLayer();
202  mFkeyFieldIdx = mReferencedLayer->fieldNameIndex( relation.fieldPairs().at( 0 ).second );
203 
205 
206  if ( mEmbedForm )
207  {
208  mAttributeEditorFrame->setTitle( mReferencedLayer->name() );
209  mReferencedAttributeForm = new QgsAttributeForm( relation.referencedLayer(), QgsFeature(), context, this );
210  mReferencedAttributeForm->hideButtonBox();
211  mAttributeEditorLayout->addWidget( mReferencedAttributeForm );
212  }
213  }
214  else
215  {
216  mInvalidLabel->show();
217  }
218 
219  if ( mShown && isVisible() )
220  {
221  init();
222  }
223 }
224 
226 {
227  if ( !editable )
228  unsetMapTool();
229 
230  mFilterContainer->setEnabled( editable );
231  mComboBox->setEnabled( editable );
232  mMapIdentificationButton->setEnabled( editable );
233  mRemoveFKButton->setEnabled( editable );
234  mIsEditable = editable;
235 }
236 
238 {
239  if ( !value.isValid() || value.isNull() )
240  {
242  return;
243  }
244 
245  if ( !mReferencedLayer )
246  return;
247 
248  QgsAttributes attrs = QgsAttributes( mReferencingLayer->fields().count() );
249  attrs[mFkeyFieldIdx] = 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( mFkeyFieldIdx );
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( mFkeyFieldIdx ).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( mFkeyFieldIdx ).type() );
357  }
358  else
359  {
360  return mFeature.attribute( mFkeyFieldIdx );
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  mAttributeEditorFrame->setVisible( display );
380  mEmbedForm = display;
381 }
382 
384 {
385  mChooserContainer->setHidden( readOnly );
386  mLineEdit->setVisible( readOnly );
387  mRemoveFKButton->setVisible( mAllowNull && readOnly );
388  mReadOnlySelector = readOnly;
389 }
390 
391 void QgsRelationReferenceWidget::setAllowMapIdentification( bool allowMapIdentification )
392 {
393  mHighlightFeatureButton->setVisible( allowMapIdentification );
394  mMapIdentificationButton->setVisible( allowMapIdentification );
395  mAllowMapIdentification = allowMapIdentification;
396 }
397 
399 {
400  mOrderByValue = orderByValue;
401 }
402 
404 {
405  mFilterFields = filterFields;
406 }
407 
409 {
410  mOpenFormButton->setVisible( openFormButtonVisible );
411  mOpenFormButtonVisible = openFormButtonVisible;
412 }
413 
415 {
416  mChainFilters = chainFilters;
417 }
418 
420 {
421  Q_UNUSED( e )
422 
423  mShown = true;
424 
425  init();
426 }
427 
429 {
430  if ( !mReadOnlySelector && mComboBox->count() == 0 && mReferencedLayer )
431  {
432  QApplication::setOverrideCursor( Qt::WaitCursor );
433 
434  QSet<QString> requestedAttrs;
435 
436  QgsVectorLayerCache* layerCache = new QgsVectorLayerCache( mReferencedLayer, 100000, this );
437 
438  if ( mFilterFields.size() )
439  {
440  Q_FOREACH ( const QString& fieldName, mFilterFields )
441  {
442  QVariantList uniqueValues;
443  int idx = mReferencedLayer->fieldNameIndex( fieldName );
444  QComboBox* cb = new QComboBox();
445  cb->setProperty( "Field", fieldName );
446  mFilterComboBoxes << cb;
447  mReferencedLayer->uniqueValues( idx, uniqueValues );
448  cb->addItem( mReferencedLayer->attributeAlias( idx ).isEmpty() ? fieldName : mReferencedLayer->attributeAlias( idx ) );
449  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
450  cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->fields().at( idx ).type() ) );
451 
452  Q_FOREACH ( const QVariant& v, uniqueValues )
453  {
454  cb->addItem( v.toString(), v );
455  }
456 
457  connect( cb, SIGNAL( currentIndexChanged( int ) ), this, SLOT( filterChanged() ) );
458 
459  // Request this attribute for caching
460  requestedAttrs << fieldName;
461 
462  mFilterLayout->addWidget( cb );
463  }
464 
465  if ( mChainFilters )
466  {
467  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
468 
469  QgsFeature ft;
470  QgsFeatureIterator fit = layerCache->getFeatures();
471  while ( fit.nextFeature( ft ) )
472  {
473  for ( int i = 0; i < mFilterComboBoxes.count() - 1; ++i )
474  {
475  QVariant cv = ft.attribute( mFilterFields[i] );
476  QVariant nv = ft.attribute( mFilterFields[i + 1] );
477  QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
478  QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
479  mFilterCache[mFilterFields[i]][cf] << nf;
480  }
481  }
482  }
483  }
484 
485  QgsExpression exp( mReferencedLayer->displayExpression() );
486 
487  requestedAttrs += exp.referencedColumns().toSet();
488  requestedAttrs << mRelation.fieldPairs().at( 0 ).second;
489 
490  QgsAttributeList attributes;
491  Q_FOREACH ( const QString& attr, requestedAttrs )
492  attributes << mReferencedLayer->fieldNameIndex( attr );
493 
494  layerCache->setCacheSubsetOfAttributes( attributes );
495  mMasterModel = new QgsAttributeTableModel( layerCache );
496  mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->fields() ) );
497  mFilterModel = new QgsAttributeTableFilterModel( mCanvas, mMasterModel, mMasterModel );
498  mFeatureListModel = new QgsFeatureListModel( mFilterModel, this );
499  mFeatureListModel->setDisplayExpression( mReferencedLayer->displayExpression() );
500 
501  mMasterModel->loadLayer();
502 
503  mFeatureListModel->setInjectNull( mAllowNull );
504  if ( mOrderByValue )
505  {
506  const QStringList referencedColumns = QgsExpression( mReferencedLayer->displayExpression() ).referencedColumns();
507  if ( referencedColumns.size() > 0 )
508  {
509  int sortIdx = mReferencedLayer->fieldNameIndex( referencedColumns.first() );
510  mFilterModel->sort( sortIdx );
511  }
512  }
513 
514  mComboBox->setModel( mFeatureListModel );
515 
516  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
517 
518  if ( mChainFilters && mFeature.isValid() )
519  {
520  for ( int i = 0; i < mFilterFields.size(); i++ )
521  {
522  QVariant v = mFeature.attribute( mFilterFields[i] );
523  QString f = v.isNull() ? nullValue.toString() : v.toString();
524  mFilterComboBoxes[i]->setCurrentIndex( mFilterComboBoxes[i]->findText( f ) );
525  }
526  }
527 
528  mComboBox->setCurrentIndex( mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole ) );
529 
530  // Only connect after iterating, to have only one iterator on the referenced table at once
531  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( comboReferenceChanged( int ) ) );
533  }
534 }
535 
536 void QgsRelationReferenceWidget::highlightActionTriggered( QAction* action )
537 {
538  if ( action == mHighlightFeatureAction )
539  {
540  highlightFeature();
541  }
542  else if ( action == mScaleHighlightFeatureAction )
543  {
544  highlightFeature( QgsFeature(), Scale );
545  }
546  else if ( action == mPanHighlightFeatureAction )
547  {
548  highlightFeature( QgsFeature(), Pan );
549  }
550 }
551 
553 {
554  QgsFeature feat = referencedFeature();
555 
556  if ( !feat.isValid() )
557  return;
558 
560  QgsAttributeDialog attributeDialog( mReferencedLayer, new QgsFeature( feat ), true, this, true, context );
561  attributeDialog.exec();
562 }
563 
564 void QgsRelationReferenceWidget::highlightFeature( QgsFeature f, CanvasExtent canvasExtent )
565 {
566  if ( !mCanvas )
567  return;
568 
569  if ( !f.isValid() )
570  {
571  f = referencedFeature();
572  if ( !f.isValid() )
573  return;
574  }
575 
576  if ( !f.constGeometry() )
577  {
578  return;
579  }
580 
581  const QgsGeometry* geom = f.constGeometry();
582 
583  // scale or pan
584  if ( canvasExtent == Scale )
585  {
586  QgsRectangle featBBox = geom->boundingBox();
587  featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox );
588  QgsRectangle extent = mCanvas->extent();
589  if ( !extent.contains( featBBox ) )
590  {
591  extent.combineExtentWith( &featBBox );
592  extent.scale( 1.1 );
593  mCanvas->setExtent( extent );
594  mCanvas->refresh();
595  }
596  }
597  else if ( canvasExtent == Pan )
598  {
599  QgsGeometry* centroid = geom->centroid();
600  QgsPoint center = centroid->asPoint();
601  delete centroid;
602  center = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, center );
603  mCanvas->zoomByFactor( 1.0, &center ); // refresh is done in this method
604  }
605 
606  // highlight
607  deleteHighlight();
608  mHighlight = new QgsHighlight( mCanvas, f, mReferencedLayer );
609  QSettings settings;
610  QColor color = QColor( settings.value( "/Map/highlight/color", QGis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
611  int alpha = settings.value( "/Map/highlight/colorAlpha", QGis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
612  double buffer = settings.value( "/Map/highlight/buffer", QGis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
613  double minWidth = settings.value( "/Map/highlight/minWidth", QGis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
614 
615  mHighlight->setColor( color ); // sets also fill with default alpha
616  color.setAlpha( alpha );
617  mHighlight->setFillColor( color ); // sets fill with alpha
618  mHighlight->setBuffer( buffer );
619  mHighlight->setMinWidth( minWidth );
620  mHighlight->show();
621 
622  QTimer* timer = new QTimer( this );
623  timer->setSingleShot( true );
624  connect( timer, SIGNAL( timeout() ), this, SLOT( deleteHighlight() ) );
625  timer->start( 3000 );
626 }
627 
628 void QgsRelationReferenceWidget::deleteHighlight()
629 {
630  if ( mHighlight )
631  {
632  mHighlight->hide();
633  delete mHighlight;
634  }
635  mHighlight = NULL;
636 }
637 
639 {
640  if ( !mAllowMapIdentification || !mReferencedLayer )
641  return;
642 
643  const QgsVectorLayerTools* tools = mEditorContext.vectorLayerTools();
644  if ( !tools )
645  return;
646  if ( !mCanvas )
647  return;
648 
649  mMapTool->setLayer( mReferencedLayer );
650  mCanvas->setMapTool( mMapTool );
651 
652  mWindowWidget = window();
653 
654  mCanvas->window()->raise();
655  mCanvas->activateWindow();
656  mCanvas->setFocus();
657 
658  connect( mMapTool, SIGNAL( featureIdentified( QgsFeature ) ), this, SLOT( featureIdentified( const QgsFeature ) ) );
659  connect( mMapTool, SIGNAL( deactivated() ), this, SLOT( mapToolDeactivated() ) );
660 
661  if ( mMessageBar )
662  {
663  QString title = QString( "Relation %1 for %2." ).arg( mRelationName, mReferencingLayer->name() );
664  QString msg = tr( "Identify a feature of %1 to be associated. Press &lt;ESC&gt; to cancel." ).arg( mReferencedLayer->name() );
665  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
666  mMessageBar->pushItem( mMessageBarItem );
667  }
668 }
669 
670 void QgsRelationReferenceWidget::comboReferenceChanged( int index )
671 {
673  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( mFeature );
674  highlightFeature( mFeature );
675  updateAttributeEditorFrame( mFeature );
676  emit foreignKeyChanged( mFeature.attribute( mFkeyFieldIdx ) );
677 }
678 
679 void QgsRelationReferenceWidget::updateAttributeEditorFrame( const QgsFeature& feature )
680 {
681  // Check if we're running with an embedded frame we need to update
682  if ( mAttributeEditorFrame )
683  {
684  if ( mReferencedAttributeForm )
685  {
686  mReferencedAttributeForm->setFeature( feature );
687  }
688  }
689 }
690 
691 void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
692 {
693  if ( mReadOnlySelector )
694  {
695  QgsExpression expr( mReferencedLayer->displayExpression() );
696  QgsExpressionContext context;
699  << QgsExpressionContextUtils::layerScope( mReferencedLayer );
700  context.setFeature( feature );
701  QString title = expr.evaluate( &context ).toString();
702  if ( expr.hasEvalError() )
703  {
704  title = feature.attribute( mFkeyFieldIdx ).toString();
705  }
706  mLineEdit->setText( title );
707  mForeignKey = feature.attribute( mFkeyFieldIdx );
708  mFeature = feature;
709  }
710  else
711  {
712  mComboBox->setCurrentIndex( mComboBox->findData( feature.attribute( mFkeyFieldIdx ), QgsAttributeTableModel::FeatureIdRole ) );
713  mFeature = feature;
714  }
715 
716  mRemoveFKButton->setEnabled( mIsEditable );
717  highlightFeature( feature );
718  updateAttributeEditorFrame( feature );
719  emit foreignKeyChanged( foreignKey() );
720 
721  unsetMapTool();
722 }
723 
724 void QgsRelationReferenceWidget::unsetMapTool()
725 {
726  // deactivate map tool if activated
727  if ( mCanvas && mMapTool )
728  {
729  /* this will call mapToolDeactivated */
730  mCanvas->unsetMapTool( mMapTool );
731  }
732 }
733 
734 void QgsRelationReferenceWidget::mapToolDeactivated()
735 {
736  if ( mWindowWidget )
737  {
738  mWindowWidget->raise();
739  mWindowWidget->activateWindow();
740  }
741 
742  if ( mMessageBar && mMessageBarItem )
743  {
744  mMessageBar->popWidget( mMessageBarItem );
745  }
746  mMessageBarItem = NULL;
747 }
748 
749 void QgsRelationReferenceWidget::filterChanged()
750 {
751  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
752 
753  QStringList filters;
754  QgsAttributeList attrs;
755 
756  QComboBox* scb = qobject_cast<QComboBox*>( sender() );
757 
758  Q_ASSERT( scb );
759 
760  if ( mChainFilters )
761  {
762  QComboBox* ccb = 0;
763  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
764  {
765  if ( ccb == 0 )
766  {
767  if ( cb != scb )
768  continue;
769  else
770  {
771  ccb = cb;
772  continue;
773  }
774  }
775 
776  if ( ccb->currentIndex() == 0 )
777  {
778  cb->setCurrentIndex( 0 );
779  cb->setEnabled( false );
780  }
781  else
782  {
783  cb->blockSignals( true );
784  cb->clear();
785  cb->addItem( cb->property( "Field" ).toString() );
786 
787  // ccb = scb
788  // cb = scb + 1
789  Q_FOREACH ( const QString& txt, mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] )
790  {
791  cb->addItem( txt );
792  }
793 
794  cb->setEnabled( true );
795  cb->blockSignals( false );
796 
797  ccb = cb;
798  }
799  }
800  }
801 
802  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
803  {
804  if ( cb->currentIndex() != 0 )
805  {
806  const QString fieldName = cb->property( "Field" ).toString();
807 
808  if ( cb->currentText() == nullValue.toString() )
809  {
810  filters << QString( "\"%1\" IS NULL" ).arg( fieldName );
811  }
812  else
813  {
814  if ( mReferencedLayer->fields().field( fieldName ).type() == QVariant::String )
815  {
816  filters << QString( "\"%1\" = '%2'" ).arg( fieldName, cb->currentText() );
817  }
818  else
819  {
820  filters << QString( "\"%1\" = %2" ).arg( fieldName, cb->currentText() );
821  }
822  }
823  attrs << mReferencedLayer->fieldNameIndex( fieldName );
824  }
825  }
826 
827  QString filterExpression = filters.join( " AND " );
828 
829  QgsFeatureIterator it( mMasterModel->layerCache()->getFeatures( QgsFeatureRequest().setFilterExpression( filterExpression ).setSubsetOfAttributes( attrs ) ) );
830 
831  QgsFeature f;
832  QgsFeatureIds featureIds;
833 
834  while ( it.nextFeature( f ) )
835  {
836  featureIds << f.id();
837  }
838 
839  mFilterModel->setFilteredFeatures( featureIds );
840 }
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:53
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:92
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:222
A rectangle specified with double values.
Definition: qgsrectangle.h:35
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition: qgis.h:218
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer'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:336
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 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
Returns the validity of this feature.
Definition: qgsfeature.cpp:170
bool setDisplayExpression(const QString &expression)
QWidget * window() const
QString name() const
void foreignKeyChanged(QVariant)
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 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
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:176
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:107
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
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 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)
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=0)
make out a widget containing a message to be displayed on the bar
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
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...
virtual void addItem(QLayoutItem *item)
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:311
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:331
void setSizePolicy(QSizePolicy)
void addWidget(QWidget *w)
A class to represent a point.
Definition: qgspoint.h:63
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:175
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:238
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.
bool chainFilters()
Determines if the filters are chained.
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:70
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:109
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:226
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.
A form was embedded as a widget on another form.
char * toString(const QLatin1String &string)
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:77
void setSingleShot(bool singleShot)
void scale(double scaleFactor, const QgsPoint *c=0)
Scale the rectangle around its center point.