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 mFeature.attribute( mFkeyFieldIdx );
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  const QStringList referencedColumns = QgsExpression( mReferencedLayer->displayExpression() ).referencedColumns();
514  if ( referencedColumns.size() > 0 )
515  {
516  int sortIdx = mReferencedLayer->fieldNameIndex( referencedColumns.first() );
517  mFilterModel->sort( sortIdx );
518  }
519  }
520 
521  mComboBox->setModel( mFeatureListModel );
522 
523  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
524 
525  if ( mChainFilters && mFeature.isValid() )
526  {
527  for ( int i = 0; i < mFilterFields.size(); i++ )
528  {
529  QVariant v = mFeature.attribute( mFilterFields[i] );
530  QString f = v.isNull() ? nullValue.toString() : v.toString();
531  mFilterComboBoxes[i]->setCurrentIndex( mFilterComboBoxes[i]->findText( f ) );
532  }
533  }
534 
535  mComboBox->setCurrentIndex( mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole ) );
536 
537  // Only connect after iterating, to have only one iterator on the referenced table at once
538  connect( mComboBox, SIGNAL( currentIndexChanged(int) ), this, SLOT( comboReferenceChanged( int ) ) );
539  QApplication::restoreOverrideCursor();
540  }
541 }
542 
543 void QgsRelationReferenceWidget::highlightActionTriggered( QAction* action )
544 {
545  if ( action == mHighlightFeatureAction )
546  {
547  highlightFeature();
548  }
549  else if ( action == mScaleHighlightFeatureAction )
550  {
551  highlightFeature( QgsFeature(), Scale );
552  }
553  else if ( action == mPanHighlightFeatureAction )
554  {
555  highlightFeature( QgsFeature(), Pan );
556  }
557 }
558 
560 {
561  QgsFeature feat = referencedFeature();
562 
563  if ( !feat.isValid() )
564  return;
565 
567  QgsAttributeDialog attributeDialog( mReferencedLayer, new QgsFeature( feat ), true, this, true, context );
568  attributeDialog.exec();
569 }
570 
571 void QgsRelationReferenceWidget::highlightFeature( QgsFeature f, CanvasExtent canvasExtent )
572 {
573  if ( !mCanvas )
574  return;
575 
576  if ( !f.isValid() )
577  {
578  f = referencedFeature();
579  if ( !f.isValid() )
580  return;
581  }
582 
583  QgsGeometry* geom = f.geometry();
584  if ( !geom )
585  {
586  return;
587  }
588 
589  // scale or pan
590  if ( canvasExtent == Scale )
591  {
592  QgsRectangle featBBox = geom->boundingBox();
593  featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox );
594  QgsRectangle extent = mCanvas->extent();
595  if ( !extent.contains( featBBox ) )
596  {
597  extent.combineExtentWith( &featBBox );
598  extent.scale( 1.1 );
599  mCanvas->setExtent( extent );
600  mCanvas->refresh();
601  }
602  }
603  else if ( canvasExtent == Pan )
604  {
605  QgsGeometry* centroid = geom->centroid();
606  QgsPoint center = centroid->asPoint();
607  delete centroid;
608  center = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, center );
609  mCanvas->zoomByFactor( 1.0, &center ); // refresh is done in this method
610  }
611 
612  // highlight
613  deleteHighlight();
614  mHighlight = new QgsHighlight( mCanvas, f, mReferencedLayer );
615  QSettings settings;
616  QColor color = QColor( settings.value( "/Map/highlight/color", QGis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
617  int alpha = settings.value( "/Map/highlight/colorAlpha", QGis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
618  double buffer = settings.value( "/Map/highlight/buffer", QGis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
619  double minWidth = settings.value( "/Map/highlight/minWidth", QGis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
620 
621  mHighlight->setColor( color ); // sets also fill with default alpha
622  color.setAlpha( alpha );
623  mHighlight->setFillColor( color ); // sets fill with alpha
624  mHighlight->setBuffer( buffer );
625  mHighlight->setMinWidth( minWidth );
626  mHighlight->show();
627 
628  QTimer* timer = new QTimer( this );
629  timer->setSingleShot( true );
630  connect( timer, SIGNAL( timeout() ), this, SLOT( deleteHighlight() ) );
631  timer->start( 3000 );
632 }
633 
634 void QgsRelationReferenceWidget::deleteHighlight()
635 {
636  if ( mHighlight )
637  {
638  mHighlight->hide();
639  delete mHighlight;
640  }
641  mHighlight = NULL;
642 }
643 
645 {
646  if ( !mAllowMapIdentification || !mReferencedLayer )
647  return;
648 
649  const QgsVectorLayerTools* tools = mEditorContext.vectorLayerTools();
650  if ( !tools )
651  return;
652  if ( !mCanvas )
653  return;
654 
655  mMapTool->setLayer( mReferencedLayer );
656  mCanvas->setMapTool( mMapTool );
657 
658  mWindowWidget = window();
659 
660  mCanvas->window()->raise();
661  mCanvas->activateWindow();
662  mCanvas->setFocus();
663 
664  connect( mMapTool, SIGNAL( featureIdentified( QgsFeature ) ), this, SLOT( featureIdentified( const QgsFeature ) ) );
665  connect( mMapTool, SIGNAL( deactivated() ), this, SLOT( mapToolDeactivated() ) );
666 
667  if ( mMessageBar )
668  {
669  QString title = QString( "Relation %1 for %2." ).arg( mRelationName ).arg( mReferencingLayer->name() );
670  QString msg = tr( "Identify a feature of %1 to be associated. Press &lt;ESC&gt; to cancel." ).arg( mReferencedLayer->name() );
671  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
672  mMessageBar->pushItem( mMessageBarItem );
673  }
674 }
675 
676 void QgsRelationReferenceWidget::comboReferenceChanged( int index )
677 {
678  QgsFeatureId fid = mComboBox->itemData( index, QgsAttributeTableModel::FeatureIdRole ).value<QgsFeatureId>();
679  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( mFeature );
680  highlightFeature( mFeature );
681  updateAttributeEditorFrame( mFeature );
682  emit foreignKeyChanged( mFeature.attribute( mFkeyFieldIdx ) );
683 }
684 
685 void QgsRelationReferenceWidget::updateAttributeEditorFrame( const QgsFeature feature )
686 {
687  // Check if we're running with an embedded frame we need to update
688  if ( mAttributeEditorFrame )
689  {
690  if ( mReferencedAttributeForm )
691  {
692  mReferencedAttributeForm->setFeature( feature );
693  }
694  }
695 }
696 
697 void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
698 {
699  if ( mReadOnlySelector )
700  {
701  QgsExpression expr( mReferencedLayer->displayExpression() );
702  QString title = expr.evaluate( &feature ).toString();
703  if ( expr.hasEvalError() )
704  {
705  title = feature.attribute( mFkeyFieldIdx ).toString();
706  }
707  mLineEdit->setText( title );
708  mForeignKey = feature.attribute( mFkeyFieldIdx );
709  mFeature = feature;
710  }
711  else
712  {
713  mComboBox->setCurrentIndex( mComboBox->findData( feature.attribute( mFkeyFieldIdx ), QgsAttributeTableModel::FeatureIdRole ) );
714  mFeature = feature;
715  }
716 
717  mRemoveFKButton->setEnabled( mIsEditable );
718  highlightFeature( feature );
719  updateAttributeEditorFrame( feature );
720  emit foreignKeyChanged( foreignKey() );
721 
722  unsetMapTool();
723 }
724 
725 void QgsRelationReferenceWidget::unsetMapTool()
726 {
727  // deactivate map tool if activated
728  if ( mCanvas && mMapTool )
729  {
730  /* this will call mapToolDeactivated */
731  mCanvas->unsetMapTool( mMapTool );
732  }
733 }
734 
735 void QgsRelationReferenceWidget::mapToolDeactivated()
736 {
737  if ( mWindowWidget )
738  {
739  mWindowWidget->raise();
740  mWindowWidget->activateWindow();
741  }
742 
743  if ( mMessageBar && mMessageBarItem )
744  {
745  mMessageBar->popWidget( mMessageBarItem );
746  }
747  mMessageBarItem = NULL;
748 }
749 
750 void QgsRelationReferenceWidget::filterChanged()
751 {
752  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
753 
754  QStringList filters;
755  QgsAttributeList attrs;
756 
757  QComboBox* scb = qobject_cast<QComboBox*>( sender() );
758 
759  Q_ASSERT( scb );
760 
761  if ( mChainFilters )
762  {
763  QComboBox* ccb = 0;
764  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
765  {
766  if ( ccb == 0 )
767  {
768  if ( cb != scb )
769  continue;
770  else
771  {
772  ccb = cb;
773  continue;
774  }
775  }
776 
777  if ( ccb->currentIndex() == 0 )
778  {
779  cb->setCurrentIndex( 0 );
780  cb->setEnabled( false );
781  }
782  else
783  {
784  cb->blockSignals( true );
785  cb->clear();
786  cb->addItem( cb->property( "Field" ).toString() );
787 
788  // ccb = scb
789  // cb = scb + 1
790  Q_FOREACH ( const QString& txt, mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] )
791  {
792  cb->addItem( txt );
793  }
794 
795  cb->setEnabled( true );
796  cb->blockSignals( false );
797 
798  ccb = cb;
799  }
800  }
801  }
802 
803  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
804  {
805  if ( cb->currentIndex() != 0 )
806  {
807  const QString fieldName = cb->property( "Field" ).toString();
808 
809  if ( cb->currentText() == nullValue.toString() )
810  {
811  filters << QString( "\"%1\" IS NULL" ).arg( fieldName );
812  }
813  else
814  {
815  if ( mReferencedLayer->pendingFields().field( fieldName ).type() == QVariant::String )
816  {
817  filters << QString( "\"%1\" = '%2'" ).arg( fieldName ).arg( cb->currentText() );
818  }
819  else
820  {
821  filters << QString( "\"%1\" = %2" ).arg( fieldName ).arg( cb->currentText() );
822  }
823  }
824  attrs << mReferencedLayer->fieldNameIndex( fieldName );
825  }
826  }
827 
828  QString filterExpression = filters.join( " AND " );
829 
830  QgsFeatureIterator it( mMasterModel->layerCache()->getFeatures( QgsFeatureRequest().setFilterExpression( filterExpression ).setSubsetOfAttributes( attrs ) ) );
831 
832  QgsFeature f;
833  QgsFeatureIds featureIds;
834 
835  while ( it.nextFeature( f ) )
836  {
837  featureIds << f.id();
838  }
839 
840  mFilterModel->setFilteredFeatures( featureIds );
841 }
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.