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