QGIS API Documentation  2.11.0-Master
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  , 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  mChooserGroupBox = new QGroupBox( this );
95  editLayout->addWidget( mChooserGroupBox );
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  mChooserGroupBox->setLayout( chooserLayout );
103  chooserLayout->addWidget( mFilterContainer );
104 
105  // combobox (for non-geometric relation)
106  mComboBox = new QComboBox( this );
107  mChooserGroupBox->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( 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().first().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  QgsFeatureIterator fit;
249 
250  // TODO: Rewrite using expression
251  if ( mMasterModel )
252  {
253  fit = mMasterModel->layerCache()->getFeatures( QgsFeatureRequest() );
254  }
255  else
256  {
257  fit = mReferencedLayer->getFeatures( QgsFeatureRequest() );
258  }
259  while ( fit.nextFeature( mFeature ) )
260  {
261  if ( mFeature.attribute( mFkeyFieldIdx ) == value )
262  {
263  break;
264  }
265  }
266 
267  if ( !mFeature.isValid() )
268  {
270  return;
271  }
272 
273  mForeignKey = mFeature.attribute( mFkeyFieldIdx );
274 
275  if ( mReadOnlySelector )
276  {
277  QgsExpression expr( mReferencedLayer->displayExpression() );
278  QString title = expr.evaluate( &mFeature ).toString();
279  if ( expr.hasEvalError() )
280  {
281  title = mFeature.attribute( mFkeyFieldIdx ).toString();
282  }
283  mLineEdit->setText( title );
284  }
285  else
286  {
287  int i = mComboBox->findData( value, QgsAttributeTableModel::FeatureIdRole );
288  if ( i == -1 && mAllowNull )
289  {
290  mComboBox->setCurrentIndex( 0 );
291  }
292  else
293  {
294  mComboBox->setCurrentIndex( i );
295  }
296  }
297 
298  mRemoveFKButton->setEnabled( mIsEditable );
299  highlightFeature( mFeature );
300  updateAttributeEditorFrame( mFeature );
301  emit foreignKeyChanged( foreignKey() );
302 }
303 
305 {
306  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
307  if ( mReadOnlySelector )
308  {
309  QString nullText = "";
310  if ( mAllowNull )
311  {
312  nullText = tr( "%1 (no selection)" ).arg( nullValue.toString() );
313  }
314  mLineEdit->setText( nullText );
315  mForeignKey = QVariant();
316  mFeature.setValid( false );
317  }
318  else
319  {
320  if ( mAllowNull )
321  {
322  mComboBox->setCurrentIndex( 0 );
323  }
324  else
325  {
326  mComboBox->setCurrentIndex( -1 );
327  }
328  }
329  mRemoveFKButton->setEnabled( false );
330  updateAttributeEditorFrame( QgsFeature() );
331  emit foreignKeyChanged( QVariant( QVariant::Int ) );
332 }
333 
335 {
336  QgsFeature f;
337  if ( mReferencedLayer )
338  {
339  QgsFeatureId fid;
340  if ( mReadOnlySelector )
341  {
342  fid = mFeature.id();
343  }
344  else
345  {
346  fid = mComboBox->itemData( mComboBox->currentIndex(), QgsAttributeTableModel::FeatureIdRole ).value<QgsFeatureId>();
347  }
348  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( f );
349  }
350  return f;
351 }
352 
354 {
355  if ( mReadOnlySelector )
356  {
357  return mForeignKey;
358  }
359  else
360  {
361  QVariant varFid = mComboBox->itemData( mComboBox->currentIndex(), QgsAttributeTableModel::FeatureIdRole );
362  if ( varFid.isNull() )
363  {
364  return QVariant();
365  }
366  else
367  {
368  return mFeature.attribute( mFkeyFieldIdx );
369  }
370  }
371 }
372 
374 {
375  mEditorContext = context;
376  mCanvas = canvas;
377  mMessageBar = messageBar;
378 
379  if ( mMapTool )
380  delete mMapTool;
381  mMapTool = new QgsMapToolIdentifyFeature( mCanvas );
382  mMapTool->setAction( mMapIdentificationButton->defaultAction() );
383 }
384 
386 {
387  mAttributeEditorFrame->setVisible( display );
388  mEmbedForm = display;
389 }
390 
392 {
393  mChooserGroupBox->setHidden( readOnly );
394  mLineEdit->setVisible( readOnly );
395  mRemoveFKButton->setVisible( mAllowNull && readOnly );
396  mReadOnlySelector = readOnly;
397 }
398 
399 void QgsRelationReferenceWidget::setAllowMapIdentification( bool allowMapIdentification )
400 {
401  mHighlightFeatureButton->setVisible( allowMapIdentification );
402  mMapIdentificationButton->setVisible( allowMapIdentification );
403  mAllowMapIdentification = allowMapIdentification;
404 }
405 
407 {
408  mOrderByValue = orderByValue;
409 }
410 
412 {
413  mFilterFields = filterFields;
414 }
415 
417 {
418  mOpenFormButton->setVisible( openFormButtonVisible );
419  mOpenFormButtonVisible = openFormButtonVisible;
420 }
421 
423 {
424  mChainFilters = chainFilters;
425 }
426 
428 {
429  Q_UNUSED( e )
430 
431  mShown = true;
432 
433  init();
434 }
435 
437 {
438  if ( !mReadOnlySelector && mComboBox->count() == 0 && mReferencedLayer )
439  {
440  QApplication::setOverrideCursor( Qt::WaitCursor );
441 
442  QSet<QString> requestedAttrs;
443 
444  QgsVectorLayerCache* layerCache = new QgsVectorLayerCache( mReferencedLayer, 100000, this );
445 
446  if ( mFilterFields.size() )
447  {
448  Q_FOREACH ( const QString& fieldName, mFilterFields )
449  {
450  QVariantList uniqueValues;
451  int idx = mReferencedLayer->fieldNameIndex( fieldName );
452  QComboBox* cb = new QComboBox();
453  cb->setProperty( "Field", fieldName );
454  mFilterComboBoxes << cb;
455  mReferencedLayer->uniqueValues( idx, uniqueValues );
456  cb->addItem( mReferencedLayer->attributeAlias( idx ).isEmpty() ? fieldName : mReferencedLayer->attributeAlias( idx ) );
457  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
458  cb->addItem( nullValue.toString(), QVariant( mReferencedLayer->pendingFields()[idx].type() ) );
459 
460  Q_FOREACH ( QVariant v, uniqueValues )
461  {
462  cb->addItem( v.toString(), v );
463  }
464 
465  connect( cb, SIGNAL( currentIndexChanged( int ) ), this, SLOT( filterChanged() ) );
466 
467  // Request this attribute for caching
468  requestedAttrs << fieldName;
469 
470  mFilterLayout->addWidget( cb );
471  }
472 
473  if ( mChainFilters )
474  {
475  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
476 
477  QgsFeature ft;
478  QgsFeatureIterator fit = layerCache->getFeatures();
479  while ( fit.nextFeature( ft ) )
480  {
481  for ( int i = 0; i < mFilterComboBoxes.count() - 1; ++i )
482  {
483  QVariant cv = ft.attribute( mFilterFields[i] );
484  QVariant nv = ft.attribute( mFilterFields[i + 1] );
485  QString cf = cv.isNull() ? nullValue.toString() : cv.toString();
486  QString nf = nv.isNull() ? nullValue.toString() : nv.toString();
487  mFilterCache[mFilterFields[i]][cf] << nf;
488  }
489  }
490  }
491  }
492 
493  QgsExpression exp( mReferencedLayer->displayExpression() );
494 
495  requestedAttrs += exp.referencedColumns().toSet();
496  requestedAttrs << mRelation.fieldPairs().first().second;
497 
498  QgsAttributeList attributes;
499  Q_FOREACH ( const QString& attr, requestedAttrs )
500  attributes << mReferencedLayer->fieldNameIndex( attr );
501 
502  layerCache->setCacheSubsetOfAttributes( attributes );
503  mMasterModel = new QgsAttributeTableModel( layerCache );
504  mMasterModel->setRequest( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( requestedAttrs.toList(), mReferencedLayer->pendingFields() ) );
505  mFilterModel = new QgsAttributeTableFilterModel( mCanvas, mMasterModel, mMasterModel );
506  mFeatureListModel = new QgsFeatureListModel( mFilterModel, this );
507  mFeatureListModel->setDisplayExpression( mReferencedLayer->displayExpression() );
508 
509  mMasterModel->loadLayer();
510 
511  mFeatureListModel->setInjectNull( mAllowNull );
512  if ( mOrderByValue )
513  {
514  const QStringList referencedColumns = QgsExpression( mReferencedLayer->displayExpression() ).referencedColumns();
515  if ( referencedColumns.size() > 0 )
516  {
517  int sortIdx = mReferencedLayer->fieldNameIndex( referencedColumns.first() );
518  mFilterModel->sort( sortIdx );
519  }
520  }
521 
522  mComboBox->setModel( mFeatureListModel );
523 
524  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
525 
526  if ( mChainFilters && mFeature.isValid() )
527  {
528  for ( int i = 0; i < mFilterFields.size(); i++ )
529  {
530  QVariant v = mFeature.attribute( mFilterFields[i] );
531  QString f = v.isNull() ? nullValue.toString() : v.toString();
532  mFilterComboBoxes[i]->setCurrentIndex( mFilterComboBoxes[i]->findText( f ) );
533  }
534  }
535 
536  mComboBox->setCurrentIndex( mComboBox->findData( mFeature.id(), QgsAttributeTableModel::FeatureIdRole ) );
537 
538  // Only connect after iterating, to have only one iterator on the referenced table at once
539  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( comboReferenceChanged( int ) ) );
541  }
542 }
543 
544 void QgsRelationReferenceWidget::highlightActionTriggered( QAction* action )
545 {
546  if ( action == mHighlightFeatureAction )
547  {
548  highlightFeature();
549  }
550  else if ( action == mScaleHighlightFeatureAction )
551  {
552  highlightFeature( QgsFeature(), Scale );
553  }
554  else if ( action == mPanHighlightFeatureAction )
555  {
556  highlightFeature( QgsFeature(), Pan );
557  }
558 }
559 
561 {
562  QgsFeature feat = referencedFeature();
563 
564  if ( !feat.isValid() )
565  return;
566 
568  QgsAttributeDialog attributeDialog( mReferencedLayer, new QgsFeature( feat ), true, this, true, context );
569  attributeDialog.exec();
570 }
571 
572 void QgsRelationReferenceWidget::highlightFeature( QgsFeature f, CanvasExtent canvasExtent )
573 {
574  if ( !mCanvas )
575  return;
576 
577  if ( !f.isValid() )
578  {
579  f = referencedFeature();
580  if ( !f.isValid() )
581  return;
582  }
583 
584  if ( !f.constGeometry() )
585  {
586  return;
587  }
588 
589  const QgsGeometry* geom = f.constGeometry();
590 
591  // scale or pan
592  if ( canvasExtent == Scale )
593  {
594  QgsRectangle featBBox = geom->boundingBox();
595  featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox );
596  QgsRectangle extent = mCanvas->extent();
597  if ( !extent.contains( featBBox ) )
598  {
599  extent.combineExtentWith( &featBBox );
600  extent.scale( 1.1 );
601  mCanvas->setExtent( extent );
602  mCanvas->refresh();
603  }
604  }
605  else if ( canvasExtent == Pan )
606  {
607  QgsGeometry* centroid = geom->centroid();
608  QgsPoint center = centroid->asPoint();
609  delete centroid;
610  center = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, center );
611  mCanvas->zoomByFactor( 1.0, &center ); // refresh is done in this method
612  }
613 
614  // highlight
615  deleteHighlight();
616  mHighlight = new QgsHighlight( mCanvas, f, mReferencedLayer );
617  QSettings settings;
618  QColor color = QColor( settings.value( "/Map/highlight/color", QGis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
619  int alpha = settings.value( "/Map/highlight/colorAlpha", QGis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
620  double buffer = settings.value( "/Map/highlight/buffer", QGis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
621  double minWidth = settings.value( "/Map/highlight/minWidth", QGis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
622 
623  mHighlight->setColor( color ); // sets also fill with default alpha
624  color.setAlpha( alpha );
625  mHighlight->setFillColor( color ); // sets fill with alpha
626  mHighlight->setBuffer( buffer );
627  mHighlight->setMinWidth( minWidth );
628  mHighlight->show();
629 
630  QTimer* timer = new QTimer( this );
631  timer->setSingleShot( true );
632  connect( timer, SIGNAL( timeout() ), this, SLOT( deleteHighlight() ) );
633  timer->start( 3000 );
634 }
635 
636 void QgsRelationReferenceWidget::deleteHighlight()
637 {
638  if ( mHighlight )
639  {
640  mHighlight->hide();
641  delete mHighlight;
642  }
643  mHighlight = NULL;
644 }
645 
647 {
648  if ( !mAllowMapIdentification || !mReferencedLayer )
649  return;
650 
651  const QgsVectorLayerTools* tools = mEditorContext.vectorLayerTools();
652  if ( !tools )
653  return;
654  if ( !mCanvas )
655  return;
656 
657  mMapTool->setLayer( mReferencedLayer );
658  mCanvas->setMapTool( mMapTool );
659 
660  mWindowWidget = window();
661 
662  mCanvas->window()->raise();
663  mCanvas->activateWindow();
664  mCanvas->setFocus();
665 
666  connect( mMapTool, SIGNAL( featureIdentified( QgsFeature ) ), this, SLOT( featureIdentified( const QgsFeature ) ) );
667  connect( mMapTool, SIGNAL( deactivated() ), this, SLOT( mapToolDeactivated() ) );
668 
669  if ( mMessageBar )
670  {
671  QString title = QString( "Relation %1 for %2." ).arg( mRelationName ).arg( mReferencingLayer->name() );
672  QString msg = tr( "Identify a feature of %1 to be associated. Press &lt;ESC&gt; to cancel." ).arg( mReferencedLayer->name() );
673  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
674  mMessageBar->pushItem( mMessageBarItem );
675  }
676 }
677 
678 void QgsRelationReferenceWidget::comboReferenceChanged( int index )
679 {
681  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( mFeature );
682  highlightFeature( mFeature );
683  updateAttributeEditorFrame( mFeature );
684  emit foreignKeyChanged( mFeature.attribute( mFkeyFieldIdx ) );
685 }
686 
687 void QgsRelationReferenceWidget::updateAttributeEditorFrame( const QgsFeature feature )
688 {
689  // Check if we're running with an embedded frame we need to update
690  if ( mAttributeEditorFrame )
691  {
692  if ( mReferencedAttributeForm )
693  {
694  mReferencedAttributeForm->setFeature( feature );
695  }
696  }
697 }
698 
699 void QgsRelationReferenceWidget::featureIdentified( const QgsFeature& feature )
700 {
701  if ( mReadOnlySelector )
702  {
703  QgsExpression expr( mReferencedLayer->displayExpression() );
704  QString title = expr.evaluate( &feature ).toString();
705  if ( expr.hasEvalError() )
706  {
707  title = feature.attribute( mFkeyFieldIdx ).toString();
708  }
709  mLineEdit->setText( title );
710  mForeignKey = feature.attribute( mFkeyFieldIdx );
711  mFeature = feature;
712  }
713  else
714  {
715  mComboBox->setCurrentIndex( mComboBox->findData( feature.attribute( mFkeyFieldIdx ), QgsAttributeTableModel::FeatureIdRole ) );
716  mFeature = feature;
717  }
718 
719  mRemoveFKButton->setEnabled( mIsEditable );
720  highlightFeature( feature );
721  updateAttributeEditorFrame( feature );
722  emit foreignKeyChanged( foreignKey() );
723 
724  unsetMapTool();
725 }
726 
727 void QgsRelationReferenceWidget::unsetMapTool()
728 {
729  // deactivate map tool if activated
730  if ( mCanvas && mMapTool )
731  {
732  /* this will call mapToolDeactivated */
733  mCanvas->unsetMapTool( mMapTool );
734  }
735 }
736 
737 void QgsRelationReferenceWidget::mapToolDeactivated()
738 {
739  if ( mWindowWidget )
740  {
741  mWindowWidget->raise();
742  mWindowWidget->activateWindow();
743  }
744 
745  if ( mMessageBar && mMessageBarItem )
746  {
747  mMessageBar->popWidget( mMessageBarItem );
748  }
749  mMessageBarItem = NULL;
750 }
751 
752 void QgsRelationReferenceWidget::filterChanged()
753 {
754  QVariant nullValue = QSettings().value( "qgis/nullValue", "NULL" );
755 
756  QStringList filters;
757  QgsAttributeList attrs;
758 
759  QComboBox* scb = qobject_cast<QComboBox*>( sender() );
760 
761  Q_ASSERT( scb );
762 
763  if ( mChainFilters )
764  {
765  QComboBox* ccb = 0;
766  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
767  {
768  if ( ccb == 0 )
769  {
770  if ( cb != scb )
771  continue;
772  else
773  {
774  ccb = cb;
775  continue;
776  }
777  }
778 
779  if ( ccb->currentIndex() == 0 )
780  {
781  cb->setCurrentIndex( 0 );
782  cb->setEnabled( false );
783  }
784  else
785  {
786  cb->blockSignals( true );
787  cb->clear();
788  cb->addItem( cb->property( "Field" ).toString() );
789 
790  // ccb = scb
791  // cb = scb + 1
792  Q_FOREACH ( const QString& txt, mFilterCache[ccb->property( "Field" ).toString()][ccb->currentText()] )
793  {
794  cb->addItem( txt );
795  }
796 
797  cb->setEnabled( true );
798  cb->blockSignals( false );
799 
800  ccb = cb;
801  }
802  }
803  }
804 
805  Q_FOREACH ( QComboBox* cb, mFilterComboBoxes )
806  {
807  if ( cb->currentIndex() != 0 )
808  {
809  const QString fieldName = cb->property( "Field" ).toString();
810 
811  if ( cb->currentText() == nullValue.toString() )
812  {
813  filters << QString( "\"%1\" IS NULL" ).arg( fieldName );
814  }
815  else
816  {
817  if ( mReferencedLayer->pendingFields().field( fieldName ).type() == QVariant::String )
818  {
819  filters << QString( "\"%1\" = '%2'" ).arg( fieldName ).arg( cb->currentText() );
820  }
821  else
822  {
823  filters << QString( "\"%1\" = %2" ).arg( fieldName ).arg( cb->currentText() );
824  }
825  }
826  attrs << mReferencedLayer->fieldNameIndex( fieldName );
827  }
828  }
829 
830  QString filterExpression = filters.join( " AND " );
831 
832  QgsFeatureIterator it( mMasterModel->layerCache()->getFeatures( QgsFeatureRequest().setFilterExpression( filterExpression ).setSubsetOfAttributes( attrs ) ) );
833 
834  QgsFeature f;
835  QgsFeatureIds featureIds;
836 
837  while ( it.nextFeature( f ) )
838  {
839  featureIds << f.id();
840  }
841 
842  mFilterModel->setFilteredFeatures( featureIds );
843 }
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:51
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.
void setStyleSheet(const QString &styleSheet)
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
const QgsField & field(int fieldIdx) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:308
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:168
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 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:75
void setAlpha(int alpha)
QVariant foreignKey()
returns the related feature foreign key
QString join(const QString &separator) const
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:119
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
bool orderByValue()
If the widget will order the combobox entries by value.
const QString displayExpression()
Get the preview expression, used to create a human readable preview string.
void setIcon(const QIcon &icon)
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the subset of attributes to be cached.
void setAllowMapIdentification(bool allowMapIdentification)
int size() const
bool allowMapIdentification()
determines if the widge offers the possibility to select the related feature on the map (using a dedi...
void setForeignKey(const QVariant &value)
this sets the related feature using from the foreign key
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 ...
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 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
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 ...
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()
T & first()
void setRelation(QgsRelation relation, bool allowNullValue)
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 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:173
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:236
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:68
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.
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...
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
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
QAction * defaultAction() const
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:74
void setSingleShot(bool singleShot)
void scale(double scaleFactor, const QgsPoint *c=0)
Scale the rectangle around its center point.