QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposerattributetable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerattributetable.cpp
3  -----------------------------
4  begin : April 2010
5  copyright : (C) 2010 by Marco Hugentobler
6  email : marco at hugis dot net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgscomposermap.h"
20 #include "qgsmaplayerregistry.h"
21 #include "qgsvectorlayer.h"
22 
24  : mCurrentSortColumn( 0 ), mAscending( true )
25 {
26 }
27 
28 
30 {
31  QVariant v1 = m1[mCurrentSortColumn];
32  QVariant v2 = m2[mCurrentSortColumn];
33 
34  bool less = false;
35  if ( v1.type() == QVariant::String && v2.type() == QVariant::String )
36  {
37  less = v1.toString() < v2.toString();
38  }
39  else
40  {
41  less = v1.toDouble() < v2.toDouble();
42  }
43  return ( mAscending ? less : !less );
44 }
45 
46 
48  : QgsComposerTable( composition )
49  , mVectorLayer( 0 )
50  , mComposerMap( 0 )
51  , mMaximumNumberOfFeatures( 5 )
52  , mShowOnlyVisibleFeatures( true )
53  , mFilterFeatures( false )
54  , mFeatureFilter( "" )
55 {
56  //set first vector layer from layer registry as default one
57  QMap<QString, QgsMapLayer*> layerMap = QgsMapLayerRegistry::instance()->mapLayers();
58  QMap<QString, QgsMapLayer*>::const_iterator mapIt = layerMap.constBegin();
59  for ( ; mapIt != layerMap.constEnd(); ++mapIt )
60  {
61  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( mapIt.value() );
62  if ( vl )
63  {
64  mVectorLayer = vl;
65  break;
66  }
67  }
68  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
69 }
70 
72 {
73 }
74 
75 void QgsComposerAttributeTable::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
76 {
78  {
79  return;
80  }
81  QgsComposerTable::paint( painter, itemStyle, pWidget );
82 }
83 
85 {
86  mFieldAliasMap.clear();
87  if ( mVectorLayer )
88  {
89  const QgsFields& fields = mVectorLayer->pendingFields();
90  for ( int idx = 0; idx < fields.count(); ++idx )
91  {
92  QString currentAlias = mVectorLayer->attributeAlias( idx );
93  if ( !currentAlias.isEmpty() )
94  {
95  mFieldAliasMap.insert( idx, currentAlias );
96  }
97  }
98  }
99 }
100 
102 {
103  if ( layer == mVectorLayer )
104  {
105  return;
106  }
107 
108  mDisplayAttributes.clear();
109 
110  if ( mVectorLayer )
111  {
112  //disconnect from previous layer
113  QObject::disconnect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
114  }
115 
116  mVectorLayer = layer;
119 
120  //listen for modifications to layer and refresh table when they occur
121  QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
122 }
123 
125 {
126  if ( map == mComposerMap )
127  {
128  return;
129  }
130 
131  if ( mComposerMap )
132  {
133  //disconnect from previous map
134  QObject::disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
135  }
136  mComposerMap = map;
137  if ( mComposerMap )
138  {
139  //listen out for extent changes in linked map
140  QObject::connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
141  }
143 }
144 
146 {
147  if ( features == mMaximumNumberOfFeatures )
148  {
149  return;
150  }
151 
152  mMaximumNumberOfFeatures = features;
154 }
155 
157 {
158  if ( visibleOnly == mShowOnlyVisibleFeatures )
159  {
160  return;
161  }
162 
163  mShowOnlyVisibleFeatures = visibleOnly;
165 }
166 
168 {
169  if ( filter == mFilterFeatures )
170  {
171  return;
172  }
173 
174  mFilterFeatures = filter;
176 }
177 
178 void QgsComposerAttributeTable::setFeatureFilter( const QString& expression )
179 {
180  if ( expression == mFeatureFilter )
181  {
182  return;
183  }
184 
185  mFeatureFilter = expression;
187 }
188 
189 void QgsComposerAttributeTable::setDisplayAttributes( const QSet<int>& attr, bool refresh )
190 {
191  mDisplayAttributes = attr;
192 
193  if ( refresh )
194  {
196  }
197 }
198 
199 void QgsComposerAttributeTable::setFieldAliasMap( const QMap<int, QString>& map, bool refresh )
200 {
201  mFieldAliasMap = map;
202 
203  if ( refresh )
204  {
206  }
207 }
208 
209 bool QgsComposerAttributeTable::getFeatureAttributes( QList<QgsAttributeMap> &attributeMaps )
210 {
211  if ( !mVectorLayer )
212  {
213  return false;
214  }
215 
216  attributeMaps.clear();
217 
218  //prepare filter expression
219  std::auto_ptr<QgsExpression> filterExpression;
220  bool activeFilter = false;
221  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
222  {
223  filterExpression = std::auto_ptr<QgsExpression>( new QgsExpression( mFeatureFilter ) );
224  if ( !filterExpression->hasParserError() )
225  {
226  activeFilter = true;
227  }
228  }
229 
230  QgsRectangle selectionRect;
232  {
233  selectionRect = *mComposerMap->currentMapExtent();
235  {
236  //transform back to layer CRS
238  try
239  {
240  selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform );
241  }
242  catch ( QgsCsException &cse )
243  {
244  Q_UNUSED( cse );
245  return false;
246  }
247  }
248  }
249 
250  QgsFeatureRequest req;
251  if ( !selectionRect.isEmpty() )
252  req.setFilterRect( selectionRect );
253 
255 
256  if ( !mDisplayAttributes.isEmpty() )
258 
259  QgsFeature f;
260  int counter = 0;
262 
263  while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
264  {
265  //check feature against filter
266  if ( activeFilter )
267  {
268  QVariant result = filterExpression->evaluate( &f, mVectorLayer->pendingFields() );
269  // skip this feature if the filter evaluation if false
270  if ( !result.toBool() )
271  {
272  continue;
273  }
274  }
275 
276  attributeMaps.push_back( QgsAttributeMap() );
277 
278  for ( int i = 0; i < f.attributes().size(); i++ )
279  {
280  if ( !mDisplayAttributes.isEmpty() && !mDisplayAttributes.contains( i ) )
281  continue;
282 
283  attributeMaps.last().insert( i, f.attributes()[i] );
284  }
285 
286  ++counter;
287  }
288 
289  //sort the list, starting with the last attribute
291  for ( int i = mSortInformation.size() - 1; i >= 0; --i )
292  {
293  c.setSortColumn( mSortInformation.at( i ).first );
294  c.setAscending( mSortInformation.at( i ).second );
295  qStableSort( attributeMaps.begin(), attributeMaps.end(), c );
296  }
297  return true;
298 }
299 
301 {
302  QMap<int, QString> header;
303  if ( mVectorLayer )
304  {
305  const QgsFields& vectorFields = mVectorLayer->pendingFields();
306  for ( int idx = 0; idx < vectorFields.count(); ++idx )
307  {
308  if ( mDisplayAttributes.size() > 0 && !mDisplayAttributes.contains( idx ) )
309  {
310  continue;
311  }
312  header.insert( idx, attributeDisplayName( idx, vectorFields[idx].name() ) );
313  }
314  }
315  return header;
316 }
317 
318 QString QgsComposerAttributeTable::attributeDisplayName( int attributeIndex, const QString& name ) const
319 {
320  return mFieldAliasMap.value( attributeIndex, name );
321 }
322 
324 {
325  if ( mVectorLayer )
326  {
327  if ( layerId == mVectorLayer->id() )
328  {
329  mVectorLayer = 0;
330  }
331  }
332 }
333 
334 void QgsComposerAttributeTable::setSceneRect( const QRectF& rectangle )
335 {
336  double titleHeight = 2 * mGridStrokeWidth + 2 * mLineTextDistance + fontAscentMillimeters( mHeaderFont );
337  double attributeHeight = mGridStrokeWidth + 2 * mLineTextDistance + fontAscentMillimeters( mContentFont );
338  if (( rectangle.height() - titleHeight ) > 0 )
339  {
340  mMaximumNumberOfFeatures = ( rectangle.height() - titleHeight ) / attributeHeight;
341  }
342  else
343  {
345  }
346  QgsComposerItem::setSceneRect( rectangle );
347 
348  //refresh table attributes, since number of features has likely changed
350 
352 }
353 
354 void QgsComposerAttributeTable::setSortAttributes( const QList<QPair<int, bool> > att, bool refresh )
355 {
356  mSortInformation = att;
357 
358  if ( refresh )
359  {
361  }
362 }
363 
364 bool QgsComposerAttributeTable::writeXML( QDomElement& elem, QDomDocument & doc ) const
365 {
366  QDomElement composerTableElem = doc.createElement( "ComposerAttributeTable" );
367  composerTableElem.setAttribute( "showOnlyVisibleFeatures", mShowOnlyVisibleFeatures );
368  composerTableElem.setAttribute( "maxFeatures", mMaximumNumberOfFeatures );
369  composerTableElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
370  composerTableElem.setAttribute( "featureFilter", mFeatureFilter );
371 
372  if ( mComposerMap )
373  {
374  composerTableElem.setAttribute( "composerMap", mComposerMap->id() );
375  }
376  else
377  {
378  composerTableElem.setAttribute( "composerMap", -1 );
379  }
380  if ( mVectorLayer )
381  {
382  composerTableElem.setAttribute( "vectorLayer", mVectorLayer->id() );
383  }
384 
385  //display attributes
386  QDomElement displayAttributesElem = doc.createElement( "displayAttributes" );
387  QSet<int>::const_iterator attIt = mDisplayAttributes.constBegin();
388  for ( ; attIt != mDisplayAttributes.constEnd(); ++attIt )
389  {
390  QDomElement attributeIndexElem = doc.createElement( "attributeEntry" );
391  attributeIndexElem.setAttribute( "index", *attIt );
392  displayAttributesElem.appendChild( attributeIndexElem );
393  }
394  composerTableElem.appendChild( displayAttributesElem );
395 
396  //alias map
397  QDomElement aliasMapElem = doc.createElement( "attributeAliasMap" );
398  QMap<int, QString>::const_iterator aliasIt = mFieldAliasMap.constBegin();
399  for ( ; aliasIt != mFieldAliasMap.constEnd(); ++aliasIt )
400  {
401  QDomElement mapEntryElem = doc.createElement( "aliasEntry" );
402  mapEntryElem.setAttribute( "key", aliasIt.key() );
403  mapEntryElem.setAttribute( "value", aliasIt.value() );
404  aliasMapElem.appendChild( mapEntryElem );
405  }
406  composerTableElem.appendChild( aliasMapElem );
407 
408  //sort info
409  QDomElement sortColumnsElem = doc.createElement( "sortColumns" );
410  QList< QPair<int, bool> >::const_iterator sortIt = mSortInformation.constBegin();
411  for ( ; sortIt != mSortInformation.constEnd(); ++sortIt )
412  {
413  QDomElement columnElem = doc.createElement( "column" );
414  columnElem.setAttribute( "index", QString::number( sortIt->first ) );
415  columnElem.setAttribute( "ascending", sortIt->second ? "true" : "false" );
416  sortColumnsElem.appendChild( columnElem );
417  }
418  composerTableElem.appendChild( sortColumnsElem );
419  elem.appendChild( composerTableElem );
420  bool ok = tableWriteXML( composerTableElem, doc );
421  return ok;
422 }
423 
424 bool QgsComposerAttributeTable::readXML( const QDomElement& itemElem, const QDomDocument& doc )
425 {
426  if ( itemElem.isNull() )
427  {
428  return false;
429  }
430 
431  mShowOnlyVisibleFeatures = itemElem.attribute( "showOnlyVisibleFeatures", "1" ).toInt();
432  mFilterFeatures = itemElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;
433  mFeatureFilter = itemElem.attribute( "featureFilter", "" );
434 
435  //composer map
436  int composerMapId = itemElem.attribute( "composerMap", "-1" ).toInt();
437  if ( composerMapId == -1 )
438  {
439  mComposerMap = 0;
440  }
441 
442  if ( composition() )
443  {
444  mComposerMap = composition()->getComposerMapById( composerMapId );
445  }
446  else
447  {
448  mComposerMap = 0;
449  }
450 
451  if ( mComposerMap )
452  {
453  //if we have found a valid map item, listen out to extent changes on it and refresh the table
454  QObject::connect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( refreshAttributes() ) );
455  }
456 
457  //vector layer
458  QString layerId = itemElem.attribute( "vectorLayer", "not_existing" );
459  if ( layerId == "not_existing" )
460  {
461  mVectorLayer = 0;
462  }
463  else
464  {
466  if ( ml )
467  {
468  mVectorLayer = dynamic_cast<QgsVectorLayer*>( ml );
469  if ( mVectorLayer )
470  {
471  //if we have found a valid vector layer, listen for modifications on it and refresh the table
472  QObject::connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
473  }
474  }
475  }
476 
477  //restore display attribute map
478  mDisplayAttributes.clear();
479  QDomNodeList displayAttributeList = itemElem.elementsByTagName( "displayAttributes" );
480  if ( displayAttributeList.size() > 0 )
481  {
482  QDomElement displayAttributesElem = displayAttributeList.at( 0 ).toElement();
483  QDomNodeList attributeEntryList = displayAttributesElem.elementsByTagName( "attributeEntry" );
484  for ( int i = 0; i < attributeEntryList.size(); ++i )
485  {
486  QDomElement attributeEntryElem = attributeEntryList.at( i ).toElement();
487  int index = attributeEntryElem.attribute( "index", "-1" ).toInt();
488  if ( index != -1 )
489  {
490  mDisplayAttributes.insert( index );
491  }
492  }
493  }
494 
495  //restore alias map
496  mFieldAliasMap.clear();
497  QDomNodeList aliasMapNodeList = itemElem.elementsByTagName( "attributeAliasMap" );
498  if ( aliasMapNodeList.size() > 0 )
499  {
500  QDomElement attributeAliasMapElem = aliasMapNodeList.at( 0 ).toElement();
501  QDomNodeList aliasMepEntryList = attributeAliasMapElem.elementsByTagName( "aliasEntry" );
502  for ( int i = 0; i < aliasMepEntryList.size(); ++i )
503  {
504  QDomElement aliasEntryElem = aliasMepEntryList.at( i ).toElement();
505  int key = aliasEntryElem.attribute( "key", "-1" ).toInt();
506  QString value = aliasEntryElem.attribute( "value", "" );
507  mFieldAliasMap.insert( key, value );
508  }
509  }
510 
511  //restore sort columns
512  mSortInformation.clear();
513  QDomElement sortColumnsElem = itemElem.firstChildElement( "sortColumns" );
514  if ( !sortColumnsElem.isNull() )
515  {
516  QDomNodeList columns = sortColumnsElem.elementsByTagName( "column" );
517  for ( int i = 0; i < columns.size(); ++i )
518  {
519  QDomElement columnElem = columns.at( i ).toElement();
520  int attribute = columnElem.attribute( "index" ).toInt();
521  bool ascending = columnElem.attribute( "ascending" ) == "true" ? true : false;
522  mSortInformation.push_back( qMakePair( attribute, ascending ) );
523  }
524  }
525  bool success = tableReadXML( itemElem, doc );
526 
527  //must be done here because tableReadXML->setSceneRect changes mMaximumNumberOfFeatures
528  mMaximumNumberOfFeatures = itemElem.attribute( "maxFeatures", "5" ).toInt();
529 
531 
532  emit itemChanged();
533  return success;
534 }
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.
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:47
QMap< int, QString > mFieldAliasMap
Map of attribute name aliases.
bool isEmpty() const
test if rectangle is empty
void setSortAttributes(const QList< QPair< int, bool > > att, bool refresh=true)
Sets the attributes to use to sort the table's features.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsfeature.h:98
Use exact geometry intersection (slower) instead of bounding boxes.
bool getFeatureAttributes(QList< QgsAttributeMap > &attributeMaps)
Retrieves feature attributes.
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
void setComposerMap(const QgsComposerMap *map)
Sets the composer map to use to limit the extent of features shown in the attribute table...
void maximumNumberOfFeaturesChanged(int n)
This signal is emitted if the maximum number of feature changes (interactively)
bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
sets state from Dom document
bool writeXML(QDomElement &elem, QDomDocument &doc) const
stores state in Dom element
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
Container of fields for a vector layer.
Definition: qgsfield.h:164
void initializeAliasMap()
Inserts aliases from vector layer as starting configuration to the alias map.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
void setDisplayAttributes(const QSet< int > &attr, bool refresh=true)
Sets the attributes to display in the table.
bool operator()(const QgsAttributeMap &m1, const QgsAttributeMap &m2)
void setFilterFeatures(bool filter)
Sets whether the feature filter is active for the attribute table.
QSet< int > mDisplayAttributes
List of attribute indices to display (or all attributes if list is empty)
void setDisplayOnlyVisibleFeatures(bool visibleOnly)
Sets attribute table to only show features which are visible in a composer map item.
const QgsComposerMap * mComposerMap
Associated composer map (used to display the visible features)
A class to display feature attributes in the print composer.
QMap< int, QString > getHeaderLabels() const
bool isDrawing() const
True if a draw is already in progress.
void itemChanged()
Used e.g.
void setVectorLayer(QgsVectorLayer *layer)
Sets the vector layer from which to display feature attributes.
QgsVectorLayer * mVectorLayer
Associated vector layer.
QList< QPair< int, bool > > mSortInformation
Contains information about sort attribute index / ascending (true/false).
void setSceneRect(const QRectF &rectangle)
Adapts mMaximumNumberOfFeatures depending on the rectangle height.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint.
bool tableWriteXML(QDomElement &itemElem, QDomDocument &doc) const
This class wraps a request for features to a vector layer (or directly its vector data provider)...
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint.
void setMaximumNumberOfFeatures(int features)
Sets the maximum number of features shown by the table.
const QgsAttributes & attributes() const
Definition: qgsfeature.h:143
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
Definition: qgsmaplayer.cpp:92
int count() const
Return number of items.
Definition: qgsfield.h:198
QgsComposerAttributeTable(QgsComposition *composition)
QString attributeAlias(int attributeIndex) const
Returns the alias of an attribute name or an empty string if there is no alias.
QgsComposition * mComposition
Graphics scene for map printing.
Object representing map window.
QgsRectangle * currentMapExtent()
Returns a pointer to the current map extent, which is either the original user specified extent or th...
void setFeatureFilter(const QString &expression)
Sets the expression used for filtering features in the table.
int id() const
Get identification number.
virtual void refreshAttributes()
Refreshes the attributes shown in the table by querying the vector layer for new data.
void removeLayer(QString layerId)
Checks if this vector layer will be removed (and sets mVectorLayer to 0 if yes)
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
int mMaximumNumberOfFeatures
Maximum number of features that is displayed.
bool tableReadXML(const QDomElement &itemElem, const QDomDocument &doc)
void setFieldAliasMap(const QMap< int, QString > &map, bool refresh=true)
Sets the attribute field aliases, which control how fields are named in the table's header row...
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
const QgsComposition * composition() const
const QMap< QString, QgsMapLayer * > & mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
Class for doing transforms between two map coordinate systems.
double mLineTextDistance
Distance between table lines and text.
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
Custom exception class for Coordinate Reference System related exceptions.
const QgsFields & pendingFields() const
returns field list in the to-be-committed state
bool nextFeature(QgsFeature &f)
bool mShowOnlyVisibleFeatures
Shows only the features that are visible in the associated composer map (true by default) ...
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QString attributeDisplayName(int attributeIndex, const QString &name) const
Returns the attribute name to display in the table's header row for a specified attribute.
Represents a vector layer which manages a vector based data sets.
const QgsComposerMap * getComposerMapById(int id) const
Returns the composer map with specified id.
QgsFeatureRequest & setFlags(Flags flags)
Set flags that affect how features will be fetched.
Helper class for sorting, takes into account sorting column and ascending / descending.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rect)
Set rectangle from which features will be taken.
double fontAscentMillimeters(const QFont &font) const
Returns the font ascent in Millimeters (considers upscaling and downscaling with FONT_WORKAROUND_SCAL...