QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 "qgsattributedialog.h"
24 #include "qgsapplication.h"
25 #include "qgscollapsiblegroupbox.h"
26 #include "qgseditorwidgetfactory.h"
27 #include "qgsexpression.h"
28 #include "qgsfield.h"
29 #include "qgsgeometry.h"
30 #include "qgsmapcanvas.h"
31 #include "qgsmessagebar.h"
33 #include "qgsvectorlayer.h"
34 
35 
37  : QWidget( parent )
38  , mEditorContext( QgsAttributeEditorContext() )
39  , mCanvas( NULL )
40  , mMessageBar( NULL )
41  , mHighlight( NULL )
42  , mInitialValueAssigned( false )
43  , mMapTool( NULL )
44  , mMessageBarItem( NULL )
45  , mRelationName( "" )
46  , mReferencedAttributeDialog( NULL )
47  , mReferencedLayer( NULL )
48  , mReferencingLayer( NULL )
49  , mWindowWidget( NULL )
50  , mEmbedForm( false )
51  , mReadOnlySelector( false )
52  , mAllowMapIdentification( false )
53 {
54  mTopLayout = new QVBoxLayout( this );
55  mTopLayout->setContentsMargins( 0, 0, 0, 0 );
56  setLayout( mTopLayout );
57 
58  QHBoxLayout* editLayout = new QHBoxLayout();
59  editLayout->setContentsMargins( 0, 0, 0, 0 );
60 
61  // combobox (for non-geometric relation)
62  mComboBox = new QComboBox( this );
63  editLayout->addWidget( mComboBox );
64 
65  // read-only line edit
66  mLineEdit = new QLineEdit( this );
67  mLineEdit->setReadOnly( true );
68  editLayout->addWidget( mLineEdit );
69 
70  // open form button
71  mOpenFormButton = new QToolButton( this );
72  mOpenFormAction = new QAction( QgsApplication::getThemeIcon( "/mActionToggleEditing.svg" ), tr( "Open related feature form" ), this );
73  mOpenFormButton->addAction( mOpenFormAction );
74  mOpenFormButton->setDefaultAction( mOpenFormAction );
75  connect( mOpenFormButton, SIGNAL( triggered( QAction* ) ), this, SLOT( openForm() ) );
76  editLayout->addWidget( mOpenFormButton );
77 
78  // highlight button
79  mHighlightFeatureButton = new QToolButton( this );
80  mHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionHighlightFeature.svg" ), tr( "Highlight feature" ), this );
81  mScaleHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionScaleHighlightFeature.svg" ), tr( "Scale and highlight feature" ), this );
82  mPanHighlightFeatureAction = new QAction( QgsApplication::getThemeIcon( "/mActionPanHighlightFeature.svg" ), tr( "Pan and highlight feature" ), this );
87  connect( mHighlightFeatureButton, SIGNAL( triggered( QAction* ) ), this, SLOT( highlightActionTriggered( QAction* ) ) );
88  editLayout->addWidget( mHighlightFeatureButton );
89 
90  // map identification button
91  mMapIdentificationButton = new QToolButton( this );
92  mMapIdentificationAction = new QAction( QgsApplication::getThemeIcon( "/mActionMapIdentification.svg" ), tr( "Select on map" ), this );
95  connect( mMapIdentificationButton, SIGNAL( triggered( QAction* ) ), this, SLOT( mapIdentification() ) );
96  editLayout->addWidget( mMapIdentificationButton );
97 
98  // spacer
99  editLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
100 
101  // add line to top layout
102  mTopLayout->addLayout( editLayout );
103 
104  // embed form
107  mAttributeEditorLayout = new QVBoxLayout( mAttributeEditorFrame );
109  mTopLayout->addWidget( mAttributeEditorFrame );
110 
111  // default mode is combobox, no geometric relation and no embed form
112  mLineEdit->hide();
113  mMapIdentificationButton->hide();
114  mHighlightFeatureButton->hide();
115  mAttributeEditorFrame->hide();
116 }
117 
119 {
120  deleteHighlight();
121  delete mMapTool;
122 }
123 
124 void QgsRelationReferenceWidget::setRelation( QgsRelation relation, bool allowNullValue )
125 {
126  if ( relation.isValid() )
127  {
128  if ( allowNullValue )
129  {
130  const QString nullValue = QSettings().value( "qgis/nullValue", "NULL" ).toString();
131  mComboBox->addItem( nullValue );
132  mComboBox->setItemData( mComboBox->count() - 1, Qt::gray, Qt::ForegroundRole );
133  }
134 
135  mReferencingLayer = relation.referencingLayer();
136  mRelationName = relation.name();
137  mReferencedLayer = relation.referencedLayer();
138  int refFieldIdx = mReferencedLayer->fieldNameIndex( relation.fieldPairs().first().second );
139 
141 
144 
145  QgsFeature f;
146  while ( fit.nextFeature( f ) )
147  {
148  QString txt = exp.evaluate( &f ).toString();
149 
150  mComboBox->addItem( txt, f.id() );
151 
152  mFidFkMap.insert( f.id(), f.attribute( refFieldIdx ) );
153  }
154 
155 
156  // Only connect after iterating, to have only one iterator on the referenced table at once
157  connect( mComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( referenceChanged( int ) ) );
158  }
159  else
160  {
161  QLabel* lbl = new QLabel( tr( "The relation is not valid. Please make sure your relation definitions are ok." ) );
162  QFont font = lbl->font();
163  font.setItalic( true );
164  lbl->setStyleSheet( "QLabel { color: red; } " );
165  lbl->setFont( font );
166  mTopLayout->addWidget( lbl, 1, 0 );
167  }
168 }
169 
171 {
172  mLineEdit->setEnabled( editable );
173  mComboBox->setEnabled( editable );
174  mMapIdentificationButton->setEnabled( editable );
175 }
176 
178 {
179  const QgsFeatureId fid = mFidFkMap.key( value );
180  if ( mReferencedLayer )
181  setRelatedFeature( fid );
182 }
183 
185 {
186  int oldIdx = mComboBox->currentIndex();
187  int newIdx = mComboBox->findData( fid );
188  mComboBox->setCurrentIndex( newIdx );
189 
190  if ( !mInitialValueAssigned )
191  {
192  // In case the default-selected item (first) is the actual item
193  // then no referenceChanged event was triggered automatically:
194  // Do it!
195  if ( oldIdx == mComboBox->currentIndex() )
196  referenceChanged( mComboBox->currentIndex() );
197  mInitialValueAssigned = true;
198  }
199 
200  // update line edit
201  mLineEdit->setText( mFidFkMap.value( fid ).toString() );
202 }
203 
205 {
206  if ( mWindowWidget )
207  {
208  mWindowWidget->show();
209  }
210 
211  if ( mMessageBar && mMessageBarItem )
212  {
214  }
215  mMessageBarItem = NULL;
216 }
217 
219 {
220  QVariant varFid = mComboBox->itemData( mComboBox->currentIndex() );
221  if ( varFid.isNull() )
222  {
223  return QVariant();
224  }
225  else
226  {
227  return mFidFkMap.value( varFid.value<QgsFeatureId>() );
228  }
229 }
230 
232 {
233  mEditorContext = context;
234  mCanvas = canvas;
235  mMessageBar = messageBar;
236 }
237 
239 {
240  mAttributeEditorFrame->setVisible( display );
241  mEmbedForm = display;
242 }
243 
245 {
246  mComboBox->setHidden( readOnly );
247  mLineEdit->setVisible( readOnly );
248  mReadOnlySelector = readOnly;
249 }
250 
251 void QgsRelationReferenceWidget::setAllowMapIdentification( bool allowMapIdentification )
252 {
253  mHighlightFeatureButton->setVisible( allowMapIdentification );
254  mMapIdentificationButton->setVisible( allowMapIdentification );
256 }
257 
259 {
260  if ( action == mHighlightFeatureAction )
261  {
263  }
264  else if ( action == mScaleHighlightFeatureAction )
265  {
267  }
268  else if ( action == mPanHighlightFeatureAction )
269  {
271  }
272 }
273 
275 {
276  QgsFeatureId fid = mComboBox->itemData( mComboBox->currentIndex() ).value<QgsFeatureId>();
277 
278  QgsFeature feat;
279 
280  if ( !mReferencedLayer )
281  return;
282 
283  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( feat );
284 
285  if ( !feat.isValid() )
286  return;
287 
288  // TODO: Get a proper QgsDistanceArea thingie
292 }
293 
295 {
296  QgsFeatureId fid = mComboBox->itemData( mComboBox->currentIndex() ).value<QgsFeatureId>();
297 
298  QgsFeature feat;
299 
300  if ( !mReferencedLayer )
301  return;
302 
303  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( feat );
304 
305  if ( !feat.isValid() )
306  return;
307 
308  if ( !mCanvas )
309  return;
310 
311  QgsGeometry* geom = feat.geometry();
312  if ( !geom )
313  {
314  return;
315  }
316 
317  // scale or pan
318  if ( canvasExtent == Scale )
319  {
320  QgsRectangle featBBox = geom->boundingBox();
321  featBBox = mCanvas->mapSettings().layerToMapCoordinates( mReferencedLayer, featBBox );
322  QgsRectangle extent = mCanvas->extent();
323  if ( !extent.contains( featBBox ) )
324  {
325  extent.combineExtentWith( &featBBox );
326  extent.scale( 1.1 );
327  mCanvas->setExtent( extent );
328  mCanvas->refresh();
329  }
330  }
331  else if ( canvasExtent == Pan )
332  {
333  QgsPoint center = geom->centroid()->asPoint();
335  mCanvas->zoomByFactor( 1.0, &center ); // refresh is done in this method
336  }
337 
338  // highlight
339  deleteHighlight();
341  QSettings settings;
342  QColor color = QColor( settings.value( "/Map/highlight/color", QGis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
343  int alpha = settings.value( "/Map/highlight/colorAlpha", QGis::DEFAULT_HIGHLIGHT_COLOR.alpha() ).toInt();
344  double buffer = settings.value( "/Map/highlight/buffer", QGis::DEFAULT_HIGHLIGHT_BUFFER_MM ).toDouble();
345  double minWidth = settings.value( "/Map/highlight/minWidth", QGis::DEFAULT_HIGHLIGHT_MIN_WIDTH_MM ).toDouble();
346 
347  mHighlight->setColor( color ); // sets also fill with default alpha
348  color.setAlpha( alpha );
349  mHighlight->setFillColor( color ); // sets fill with alpha
350  mHighlight->setBuffer( buffer );
351  mHighlight->setMinWidth( minWidth );
352  mHighlight->show();
353 
354  QTimer* timer = new QTimer( this );
355  timer->setSingleShot( true );
356  connect( timer, SIGNAL( timeout() ), this, SLOT( deleteHighlight() ) );
357  timer->start( 3000 );
358 }
359 
361 {
362  if ( mHighlight )
363  {
364  mHighlight->hide();
365  delete mHighlight;
366  }
367  mHighlight = NULL;
368 }
369 
371 {
372  if ( !mReferencedLayer )
373  return;
374 
376  if ( !tools )
377  return;
378  if ( !mCanvas )
379  return;
380 
383  mWindowWidget = window();
384  mWindowWidget->hide();
385  connect( mMapTool, SIGNAL( featureIdentified( QgsFeatureId ) ), this, SLOT( featureIdentified( QgsFeatureId ) ) );
386  connect( mMapTool, SIGNAL( deactivated() ), this, SLOT( mapToolDeactivated() ) );
387 
388  if ( mMessageBar )
389  {
390  QString title = QString( "Relation %1 for %2." ).arg( mRelationName ).arg( mReferencingLayer->name() );
391  QString msg = tr( "identify a feature of %1 to be associated. Press <ESC> to cancel." ).arg( mReferencedLayer->name() );
394  }
395 }
396 
398 {
399  QgsFeatureId fid = mComboBox->itemData( index ).value<QgsFeatureId>();
400 
402 
403  emit relatedFeatureChanged( mFidFkMap.value( fid ) );
404 
405  // Check if we're running with an embedded frame we need to update
406  if ( mAttributeEditorFrame )
407  {
408  QgsFeature feat;
409 
410  mReferencedLayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ).nextFeature( feat );
411 
412  if ( feat.isValid() )
413  {
415  {
417  }
418 
419  // TODO: Get a proper QgsDistanceArea thingie
421  QWidget* attrDialog = mReferencedAttributeDialog;
422  attrDialog->setWindowFlags( Qt::Widget ); // Embed instead of opening as window
423  mAttributeEditorLayout->addWidget( attrDialog );
424  attrDialog->show();
425  }
426  }
427 }
428 
430 {
431  setRelatedFeature( fid );
432 
433  // deactivate map tool if activate
434  if ( mCanvas && mMapTool )
435  {
437  }
438 
439  if ( mWindowWidget )
440  mWindowWidget->show();
441 }
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.
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:89
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:285
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:281
QgsPoint layerToMapCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from layer's CRS to output CRS
void zoomByFactor(double scaleFactor, const QgsPoint *center=0)
Zoom with the factor supplied.
bool isValid() const
Return the validity of this feature.
Definition: qgsfeature.cpp:171
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
This class contains context information for attribute editor widgets.
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 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
QgsCollapsibleGroupBox * mAttributeEditorFrame
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:43
QHash< QgsFeatureId, QVariant > mFidFkMap
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.
void highlightFeature(CanvasExtent canvasExtent=Fixed)
QgsMapToolIdentifyFeature * mMapTool
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
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:104
void setRelatedFeature(const QVariant &value)
void setAllowMapIdentification(bool allowMapIdentification)
QgsGeometry * centroid()
Returns the center of mass of a geometry.
QgsAttributeDialog * mReferencedAttributeDialog
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 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
bool popWidget(QgsMessageBarItem *item)
QgsVectorLayerTools * vectorLayerTools()
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
void setEditorContext(QgsAttributeEditorContext context, QgsMapCanvas *canvas, QgsMessageBar *messageBar)
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 setRelation(QgsRelation relation, bool allowNullValue)
QgsAttributeEditorContext mEditorContext
void featureIdentified(const QgsFeatureId &fid)
void relatedFeatureChanged(QVariant)
A class to represent a point geometry.
Definition: qgspoint.h:63
The QgsMapToolIdentifyFeature class is a map tool to be used to identify a feature on a chosen layer...
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.
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.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
void highlightActionTriggered(QAction *action)
bool nextFeature(QgsFeature &f)
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.
static double DEFAULT_HIGHLIGHT_MIN_WIDTH_MM
Default highlight line/outline minimum width in mm.
Definition: qgis.h:289
void setMinWidth(double width)
Set minimum line / outline width in millimeters.
Definition: qgshighlight.h:67
#define tr(sourceText)
void scale(double scaleFactor, const QgsPoint *c=0)
Scale the rectangle around its center point.