QGIS API Documentation  2.15.0-Master (af20121)
qgsattributetablefiltermodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsAttributeTableFilterModel.cpp
3  --------------------------------------
4  Date : Feb 2009
5  Copyright : (C) 2009 Vita Cizek
6  Email : weetya (at) gmail.com
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 
16 #include <QItemSelectionModel>
17 
18 #include "qgis.h"
20 #include "qgsattributetablemodel.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsfeature.h"
23 #include "qgsmapcanvas.h"
24 #include "qgslogger.h"
25 #include "qgsrendererv2.h"
28 // Filter Model //
30 
32  : QSortFilterProxyModel( parent )
33  , mCanvas( canvas )
34  , mFilterMode( ShowAll )
35  , mSelectedOnTop( false )
36 {
37  setSourceModel( sourceModel );
38  setDynamicSortFilter( true );
40  connect( layer(), SIGNAL( selectionChanged() ), SLOT( selectionChanged() ) );
41 }
42 
43 bool QgsAttributeTableFilterModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
44 {
45  if ( mSelectedOnTop )
46  {
47  bool leftSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( left.row() ) );
48  bool rightSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( right.row() ) );
49 
50  if ( leftSelected && !rightSelected )
51  {
52  return sortOrder() == Qt::AscendingOrder;
53  }
54  else if ( rightSelected && !leftSelected )
55  {
56  return sortOrder() == Qt::DescendingOrder;
57  }
58  }
59 
62 }
63 
64 void QgsAttributeTableFilterModel::sort( int column, Qt::SortOrder order )
65 {
66  masterModel()->prefetchColumnData( column );
67  QSortFilterProxyModel::sort( column, order );
68 }
69 
71 {
72  if ( mapColumnToSource( index.column() ) == -1 ) // actions
73  {
74  if ( role == TypeRole )
76  else if ( role == QgsAttributeTableModel::FeatureIdRole )
77  {
80  }
81  }
82  else if ( role == TypeRole )
83  return ColumnTypeField;
84 
85  return QSortFilterProxyModel::data( index, role );
86 }
87 
88 QVariant QgsAttributeTableFilterModel::headerData( int section, Qt::Orientation orientation, int role ) const
89 {
90  if ( orientation == Qt::Horizontal )
91  {
92  if ( mColumnMapping.at( section ) == -1 && role == Qt::DisplayRole )
93  return tr( "Actions" );
94  else
95  return QSortFilterProxyModel::headerData( section, orientation, role );
96  }
97  else
98  return QSortFilterProxyModel::headerData( section, orientation, role );
99 }
100 
102 {
103  return mColumnMapping.indexOf( -1 );
104 }
105 
107 {
108  Q_UNUSED( parent );
109  return mColumnMapping.count();
110 }
111 
113 {
114  mConfig = config;
115  mConfig.update( layer()->fields() );
116 
117  QVector<int> newColumnMapping;
118 
119  Q_FOREACH ( const QgsAttributeTableConfig::ColumnConfig& columnConfig, mConfig.columns() )
120  {
121  // Hidden? Forget about this column
122  if ( columnConfig.mHidden )
123  continue;
124 
125  // The new value for the mapping (field index or -1 for action column)
126  int newValue = ( columnConfig.mType == QgsAttributeTableConfig::Action ) ? -1 : layer()->fieldNameIndex( columnConfig.mName );
127  newColumnMapping << newValue;
128  }
129 
130  if ( newColumnMapping != mColumnMapping )
131  {
132  bool requiresReset = false;
133  int firstRemovedColumn = -1;
134  int removedColumnCount = 0;
135 
136  // Check if there have a contiguous set of columns have been removed or if we require a full reset
137  for ( int i = 0; i < qMin( newColumnMapping.size(), mColumnMapping.size() - removedColumnCount ); ++i )
138  {
139  if ( newColumnMapping.at( i ) == mColumnMapping.at( i + removedColumnCount ) )
140  continue;
141 
142  if ( firstRemovedColumn == -1 )
143  {
144  firstRemovedColumn = i;
145 
146  while ( i < mColumnMapping.size() - removedColumnCount && mColumnMapping.at( i + removedColumnCount ) != newColumnMapping.at( i ) )
147  {
148  ++removedColumnCount;
149  }
150  }
151  else
152  {
153  requiresReset = true;
154  break;
155  }
156  }
157 
158  // No difference found so far
159  if ( firstRemovedColumn == -1 )
160  {
161  if ( newColumnMapping.size() > mColumnMapping.size() )
162  {
163  // More columns: appended to the end
164  beginInsertColumns( QModelIndex(), mColumnMapping.size(), newColumnMapping.size() - 1 );
165  mColumnMapping = newColumnMapping;
167  }
168  else
169  {
170  // Less columns: removed from the end
171  beginRemoveColumns( QModelIndex(), newColumnMapping.size(), mColumnMapping.size() - 1 );
172  mColumnMapping = newColumnMapping;
174  }
175  }
176  else
177  {
178  if ( newColumnMapping.size() == mColumnMapping.size() - removedColumnCount )
179  {
180  beginRemoveColumns( QModelIndex(), firstRemovedColumn, firstRemovedColumn + removedColumnCount );
181  mColumnMapping = newColumnMapping;
183  }
184  else
185  {
186  requiresReset = true;
187  }
188  }
189 
190  if ( requiresReset )
191  {
192  beginResetModel();
193  mColumnMapping = newColumnMapping;
194  endResetModel();
195  }
196  }
197 
198  sort( config.sortExpression() );
199 }
200 
201 void QgsAttributeTableFilterModel::sort( QString expression, Qt::SortOrder order )
202 {
204  masterModel()->prefetchSortData( expression );
205  QSortFilterProxyModel::sort( 0, order ) ;
206 }
207 
209 {
210  return masterModel()->sortCacheExpression();
211 }
212 
214 {
215  if ( mSelectedOnTop != selectedOnTop )
216  {
217  mSelectedOnTop = selectedOnTop;
218 
219  if ( sortColumn() == -1 )
220  {
221  sort( 0 );
222  }
223  invalidate();
224  }
225 }
226 
228 {
229  mTableModel = sourceModel;
230 
231  for ( int i = 0; i < mTableModel->columnCount() - mTableModel->extraColumns(); ++i )
232  {
233  mColumnMapping.append( i );
234  }
235 
237 
238  // Disconnect any code to update columns in the parent, we handle this manually
239  disconnect( sourceModel, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsAboutToBeInserted( QModelIndex, int, int ) ) );
240  disconnect( sourceModel, SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsInserted( QModelIndex, int, int ) ) );
241  disconnect( sourceModel, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsAboutToBeRemoved( QModelIndex, int, int ) ) );
242  disconnect( sourceModel, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsRemoved( QModelIndex, int, int ) ) );
243 
244  connect( mTableModel, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), this, SLOT( onColumnsChanged() ) );
245  connect( mTableModel, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), this, SLOT( onColumnsChanged() ) );
246 }
247 
249 {
250  return mSelectedOnTop;
251 }
252 
254 {
255  mFilteredFeatures = ids;
258 }
259 
261 {
262  QgsFeatureIds ids;
263  for ( int i = 0; i < rowCount(); ++i )
264  {
265  QModelIndex row = index( i, 0 );
266  ids << rowToId( row );
267  }
268  return ids;
269 }
270 
272 {
273  if ( filterMode != mFilterMode )
274  {
275  if ( filterMode == ShowVisible )
276  {
277  connect( mCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentsChanged() ) );
279  }
280  else
281  {
282  disconnect( mCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentsChanged() ) );
283  }
284 
285  if ( filterMode == ShowSelected )
286  {
288  }
289 
290  mFilterMode = filterMode;
292  }
293 }
294 
295 bool QgsAttributeTableFilterModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
296 {
297  Q_UNUSED( sourceParent );
298  switch ( mFilterMode )
299  {
300  case ShowAll:
301  return true;
302 
303  case ShowFilteredList:
304  return mFilteredFeatures.contains( masterModel()->rowToId( sourceRow ) );
305 
306  case ShowSelected:
307  return layer()->selectedFeaturesIds().isEmpty() || layer()->selectedFeaturesIds().contains( masterModel()->rowToId( sourceRow ) );
308 
309  case ShowVisible:
310  return mFilteredFeatures.contains( masterModel()->rowToId( sourceRow ) );
311 
312  case ShowEdited:
313  {
314  QgsVectorLayerEditBuffer* editBuffer = layer()->editBuffer();
315  if ( editBuffer )
316  {
317  const QList<QgsFeatureId> addedFeatures = editBuffer->addedFeatures().keys();
318  const QList<QgsFeatureId> changedFeatures = editBuffer->changedAttributeValues().keys();
319  const QList<QgsFeatureId> changedGeometries = editBuffer->changedGeometries().keys();
320  const QgsFeatureId fid = masterModel()->rowToId( sourceRow );
321  return addedFeatures.contains( fid ) || changedFeatures.contains( fid ) || changedGeometries.contains( fid );
322  }
323  return false;
324  }
325 
326  default:
327  Q_ASSERT( false ); // In debug mode complain
328  return true; // In release mode accept row
329  }
330  // returns are handled in their respective case statement above
331 }
332 
334 {
337 }
338 
339 void QgsAttributeTableFilterModel::selectionChanged()
340 {
341  if ( ShowSelected == mFilterMode )
342  {
345  }
346  else if ( mSelectedOnTop )
347  {
348  sort( sortColumn(), sortOrder() );
349  invalidate();
350  }
351 }
352 
353 void QgsAttributeTableFilterModel::onColumnsChanged()
354 {
355  setAttributeTableConfig( mConfig );
356 }
357 
358 int QgsAttributeTableFilterModel::mapColumnToSource( int column ) const
359 {
360  if ( mColumnMapping.isEmpty() )
361  return 0;
362  else
363  return mColumnMapping.at( column );
364 }
365 
367 {
368  if ( !layer() )
369  return;
370 
371  bool filter = false;
372  QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() );
373  QgsRenderContext renderContext;
377  QgsFeatureRendererV2* renderer = layer()->rendererV2();
378 
379  mFilteredFeatures.clear();
380 
381  if ( !renderer )
382  {
383  QgsDebugMsg( "Cannot get renderer" );
384  return;
385  }
386 
387  const QgsMapSettings& ms = mCanvas->mapSettings();
388  if ( !layer()->isInScaleRange( ms.scale() ) )
389  {
390  QgsDebugMsg( "Out of scale limits" );
391  }
392  else
393  {
394  if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
395  {
396  // setup scale
397  // mapRenderer()->renderContext()->scale is not automaticaly updated when
398  // render extent changes (because it's scale is used to identify if changed
399  // since last render) -> use local context
400  renderContext.setExtent( ms.visibleExtent() );
401  renderContext.setMapToPixel( ms.mapToPixel() );
402  renderContext.setRendererScale( ms.scale() );
403  }
404 
405  filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter;
406  }
407 
408  renderer->startRender( renderContext, layer()->fields() );
409 
410  QgsFeatureRequest r( masterModel()->request() );
411  if ( !r.filterRect().isNull() )
412  {
413  r.setFilterRect( r.filterRect().intersect( &rect ) );
414  }
415  else
416  {
417  r.setFilterRect( rect );
418  }
420 
421  QgsFeature f;
422 
423  while ( features.nextFeature( f ) )
424  {
425  renderContext.expressionContext().setFeature( f );
426  if ( !filter || renderer->willRenderFeature( f, renderContext ) )
427  {
428  mFilteredFeatures << f.id();
429  }
430 #if 0
431  if ( t.elapsed() > 5000 )
432  {
433  bool cancel = false;
434  emit progress( i, cancel );
435  if ( cancel )
436  break;
437 
438  t.restart();
439  }
440 #endif
441  }
442 
443  features.close();
444 
445  if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
446  {
447  renderer->stopRender( renderContext );
448  }
449 }
450 
452 {
453  return masterModel()->rowToId( mapToSource( row ).row() );
454 }
455 
457 {
458  return mapFromMaster( masterModel()->idToIndex( fid ) );
459 }
460 
462 {
463  QModelIndexList indexes;
464  Q_FOREACH ( const QModelIndex& idx, masterModel()->idToIndexList( fid ) )
465  {
466  indexes.append( mapFromMaster( idx ) );
467  }
468 
469  return indexes;
470 }
471 
473 {
474  if ( !proxyIndex.isValid() )
475  return QModelIndex();
476 
477  int sourceColumn = mapColumnToSource( proxyIndex.column() );
478 
479  // For the action column there is no matching column in the source model: invalid
480  if ( sourceColumn == -1 )
481  return QModelIndex();
482 
483  return QSortFilterProxyModel::mapToSource( index( proxyIndex.row(), sourceColumn, proxyIndex.parent() ) );
484 }
485 
487 {
488  QModelIndex proxyIndex = QSortFilterProxyModel::mapFromSource( sourceIndex );
489 
490  if ( proxyIndex.column() < 0 )
491  return QModelIndex();
492 
493  return index( proxyIndex.row(), mapColumnToSource( proxyIndex.column() ), proxyIndex.parent() );
494 }
495 
497 {
498  // Handle the action column flags here, the master model doesn't know it
499  if ( mapColumnToSource( index.column() ) == -1 )
500  return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
501 
502  QModelIndex source_index = mapToSource( index );
503  return masterModel()->flags( source_index );
504 }
505 
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
void generateListOfVisibleFeatures()
Updates the list of currently visible features on the map canvas.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
const QgsGeometryMap & changedGeometries()
Changed geometries which are not commited.
QgsFeatureId rowToId(const QModelIndex &row)
Returns the feature id for a given model index.
Wrapper for iterator of features from vector data provider or vector layer.
virtual QVariant data(const QModelIndex &index, int role) const override
void setSortRole(int role)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void update(const QgsFields &fields)
Update the configuration with the given fields.
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
Returns true if the source row will be accepted.
bool selectedOnTop()
Returns if selected features are currently shown on top.
void setAttributeTableConfig(const QgsAttributeTableConfig &config)
Set the attribute table configuration to control which fields are shown, in which order they are show...
double scale() const
Return the calculated scale of the map.
void setSelectedOnTop(bool selectedOnTop)
Changes the sort order of the features.
virtual void sort(int column, Qt::SortOrder order)
int actionColumnIndex() const
Get the index of the first column that contains an action widget.
void setFilterMode(FilterMode filterMode)
Set the filter mode the filter will use.
void append(const T &value)
const QgsRectangle & filterRect() const
Get the rectangle from which features will be taken.
virtual void setSourceModel(QAbstractItemModel *sourceModel)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
int extraColumns() const
Empty extra columns to announce from this model.
int indexOf(const T &value, int from) const
void setRendererScale(double scale)
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
Used by the sorting algorithm.
const QgsChangedAttributesMap & changedAttributeValues()
Changed attributes values which are not commited.
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
void columnsRemoved(const QModelIndex &parent, int start, int end)
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
This column represents an action widget.
const QgsMapToPixel & mapToPixel() const
QString sortCacheExpression() const
The expression which was used to fill the sorting cache.
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
Show only visible features (depends on the map canvas)
QgsPoint mapToLayerCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from output CRS to layer&#39;s CRS
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual Q_DECL_DEPRECATED bool willRenderFeature(QgsFeature &feat)
Returns whether the renderer will render a feature or not.
QString tr(const char *sourceText, const char *disambiguation, int n)
void setExtent(const QgsRectangle &extent)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:108
virtual int rowCount(const QModelIndex &parent) const
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:263
QgsAttributeTableFilterModel(QgsMapCanvas *canvas, QgsAttributeTableModel *sourceModel, QObject *parent=nullptr)
Make sure, the master model is already loaded, so the selection will get synchronized.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)=0
Needs to be called when a new render cycle is started.
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
The QgsMapSettings class contains configuration for rendering of the map.
QString sortExpression() const
Get the expression used for sorting.
virtual void stopRender(QgsRenderContext &context)=0
Needs to be called when a render cycle has finished to clean up.
Get the feature id of the feature in this row.
QString mName
The name of the attribute if this column represents a field.
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
QList< Key > keys() const
bool isValid() const
QgsFeatureRendererV2 * rendererV2()
Return renderer V2.
QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on...
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
FilterMode filterMode()
The current filterModel.
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns.
FilterMode
The filter mode defines how the rows should be filtered.
int row() const
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void beginRemoveColumns(const QModelIndex &parent, int first, int last)
void setDynamicSortFilter(bool enable)
virtual QVariant data(const QModelIndex &index, int role) const =0
Show only features which have unsaved changes.
QModelIndex parent() const
void columnsInserted(const QModelIndex &parent, int start, int end)
QgsVectorLayerCache * layerCache() const
Returns the layer cache this model uses as backend.
bool mHidden
Flag that controls if the column is hidden.
void setSourceModel(QgsAttributeTableModel *sourceModel)
Set the attribute table model that backs this model.
void extentsChanged()
Is called upon every change of the visible extents on the map canvas.
void columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
bool contains(const T &value) const
QgsExpressionContext & expressionContext()
Gets the expression context.
Show only features whose ids are on the filter list. {.
bool contains(const T &value) const
QVector< ColumnConfig > columns() const
Get the list with all columns and their configuration.
const QgsFeatureMap & addedFeatures()
New features which are not commited.
const T & at(int i) const
Contains information about the context of a rendering operation.
QAbstractItemModel * sourceModel() const
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const
QgsAttributeTableModel * masterModel() const
Returns the table model this filter is using.
QModelIndex mapFromMaster(const QModelIndex &sourceIndex) const
Qt::SortOrder sortOrder() const
QVariant data(int role) const
virtual void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
Sort by the given column using the given order.
QString sortExpression() const
The expression which is used to sort the attribute table.
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
bool isEmpty() const
QgsRectangle intersect(const QgsRectangle *rect) const
return the intersection with the given rectangle
int columnCount(const QModelIndex &parent) const override
int count(const T &value) const
void setMapToPixel(const QgsMapToPixel &mtp)
int column() const
bool isEmpty() const
Defines the configuration of a column in the attribute table.
qint64 QgsFeatureId
Definition: qgsfeature.h:31
QgsFeatureIds filteredFeatures()
Get a list of currently filtered feature ids.
QgsRectangle extent() const
Returns the current zoom exent of the map canvas.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
virtual int capabilities()
returns bitwise OR-ed capabilities of the renderer
bool nextFeature(QgsFeature &f)
void clear()
This is a container for configuration of the attribute table.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
int size() const
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.
QModelIndexList fidToIndexList(QgsFeatureId fid)
int sortColumn() const
void beginInsertColumns(const QModelIndex &parent, int first, int last)
void prefetchColumnData(int column)
Caches the entire data for one column.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rect)
Set rectangle from which features will be taken.
void prefetchSortData(const QString &expression)
Prefetches the entire data for one expression.
virtual QVariant data(const QModelIndex &index, int role) const
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns item flags for the index.
QModelIndex fidToIndex(QgsFeatureId fid) override
void columnsAboutToBeInserted(const QModelIndex &parent, int start, int end)
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
typedef ItemFlags