QGIS API Documentation  2.99.0-Master (6a61179)
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 "qgsfeatureiterator.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsfeature.h"
24 #include "qgsmapcanvas.h"
25 #include "qgslogger.h"
26 #include "qgsrenderer.h"
29 // Filter Model //
31 
33  : QSortFilterProxyModel( parent )
34  , mCanvas( canvas )
35  , mFilterMode( ShowAll )
36  , mSelectedOnTop( false )
37 {
38  setSourceModel( sourceModel );
39  setDynamicSortFilter( true );
40  setSortRole( QgsAttributeTableModel::SortRole );
41  connect( layer(), &QgsVectorLayer::selectionChanged, this, &QgsAttributeTableFilterModel::selectionChanged );
42 }
43 
44 bool QgsAttributeTableFilterModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
45 {
46  if ( mSelectedOnTop )
47  {
48  bool leftSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( left.row() ) );
49  bool rightSelected = layer()->selectedFeaturesIds().contains( masterModel()->rowToId( right.row() ) );
50 
51  if ( leftSelected && !rightSelected )
52  {
53  return sortOrder() == Qt::AscendingOrder;
54  }
55  else if ( rightSelected && !leftSelected )
56  {
57  return sortOrder() == Qt::DescendingOrder;
58  }
59  }
60 
62  right.data( QgsAttributeTableModel::SortRole ) );
63 }
64 
65 void QgsAttributeTableFilterModel::sort( int column, Qt::SortOrder order )
66 {
67  int myColumn = mColumnMapping.at( column );
68  masterModel()->prefetchColumnData( myColumn );
69  QSortFilterProxyModel::sort( myColumn, order );
70  emit sortColumnChanged( column, order );
71 }
72 
73 QVariant QgsAttributeTableFilterModel::data( const QModelIndex& index, int role ) const
74 {
75  if ( mapColumnToSource( index.column() ) == -1 ) // actions
76  {
77  if ( role == TypeRole )
79  else if ( role == QgsAttributeTableModel::FeatureIdRole )
80  {
81  QModelIndex fieldIndex = QSortFilterProxyModel::mapToSource( QSortFilterProxyModel::index( index.row(), 0, index.parent() ) );
82  return sourceModel()->data( fieldIndex, QgsAttributeTableModel::FeatureIdRole );
83  }
84  }
85  else if ( role == TypeRole )
86  return ColumnTypeField;
87 
88  return QSortFilterProxyModel::data( index, role );
89 }
90 
91 QVariant QgsAttributeTableFilterModel::headerData( int section, Qt::Orientation orientation, int role ) const
92 {
93  if ( orientation == Qt::Horizontal )
94  {
95  if ( mColumnMapping.at( section ) == -1 && role == Qt::DisplayRole )
96  return tr( "Actions" );
97  else
98  return QSortFilterProxyModel::headerData( section, orientation, role );
99  }
100  else
101  {
102  if ( role == Qt::DisplayRole )
103  return section + 1;
104  else
105  {
106  int sourceSection = mapToSource( index( section, ( !mColumnMapping.isEmpty() && mColumnMapping.at( 0 ) == -1 ) ? 1 : 0 ) ).row();
107  return sourceModel()->headerData( sourceSection, orientation, role );
108  }
109  }
110 }
111 
113 {
114  return mColumnMapping.indexOf( -1 );
115 }
116 
117 int QgsAttributeTableFilterModel::columnCount( const QModelIndex& parent ) const
118 {
119  Q_UNUSED( parent );
120  return mColumnMapping.count();
121 }
122 
124 {
125  mConfig = config;
126  mConfig.update( layer()->fields() );
127 
128  QVector<int> newColumnMapping;
129 
130  Q_FOREACH ( const QgsAttributeTableConfig::ColumnConfig& columnConfig, mConfig.columns() )
131  {
132  // Hidden? Forget about this column
133  if ( columnConfig.hidden )
134  continue;
135 
136  // The new value for the mapping (field index or -1 for action column)
137  int newValue = ( columnConfig.type == QgsAttributeTableConfig::Action ) ? -1 : layer()->fields().lookupField( columnConfig.name );
138  newColumnMapping << newValue;
139  }
140 
141  if ( newColumnMapping != mColumnMapping )
142  {
143  bool requiresReset = false;
144  int firstRemovedColumn = -1;
145  int removedColumnCount = 0;
146 
147  // Check if there have a contiguous set of columns have been removed or if we require a full reset
148  for ( int i = 0; i < qMin( newColumnMapping.size(), mColumnMapping.size() - removedColumnCount ); ++i )
149  {
150  if ( newColumnMapping.at( i ) == mColumnMapping.at( i + removedColumnCount ) )
151  continue;
152 
153  if ( firstRemovedColumn == -1 )
154  {
155  firstRemovedColumn = i;
156 
157  while ( i < mColumnMapping.size() - removedColumnCount && mColumnMapping.at( i + removedColumnCount ) != newColumnMapping.at( i ) )
158  {
159  ++removedColumnCount;
160  }
161  }
162  else
163  {
164  requiresReset = true;
165  break;
166  }
167  }
168 
169  // No difference found so far
170  if ( firstRemovedColumn == -1 )
171  {
172  if ( newColumnMapping.size() > mColumnMapping.size() )
173  {
174  // More columns: appended to the end
175  beginInsertColumns( QModelIndex(), mColumnMapping.size(), newColumnMapping.size() - 1 );
176  mColumnMapping = newColumnMapping;
177  endInsertColumns();
178  }
179  else
180  {
181  // Less columns: removed from the end
182  beginRemoveColumns( QModelIndex(), newColumnMapping.size(), mColumnMapping.size() - 1 );
183  mColumnMapping = newColumnMapping;
184  endRemoveColumns();
185  }
186  }
187  else
188  {
189  if ( newColumnMapping.size() == mColumnMapping.size() - removedColumnCount )
190  {
191  beginRemoveColumns( QModelIndex(), firstRemovedColumn, firstRemovedColumn );
192  mColumnMapping = newColumnMapping;
193  endRemoveColumns();
194  }
195  else
196  {
197  requiresReset = true;
198  }
199  }
200 
201  if ( requiresReset )
202  {
203  beginResetModel();
204  mColumnMapping = newColumnMapping;
205  endResetModel();
206  }
207  }
208 
209  sort( config.sortExpression(), config.sortOrder() );
210 }
211 
212 void QgsAttributeTableFilterModel::sort( const QString &expression, Qt::SortOrder order )
213 {
214  QSortFilterProxyModel::sort( -1 );
215  masterModel()->prefetchSortData( expression );
216  QSortFilterProxyModel::sort( 0, order ) ;
217 }
218 
220 {
221  return masterModel()->sortCacheExpression();
222 }
223 
225 {
226  if ( mSelectedOnTop != selectedOnTop )
227  {
228  mSelectedOnTop = selectedOnTop;
229 
230  if ( sortColumn() == -1 )
231  {
232  sort( 0 );
233  }
234  invalidate();
235  }
236 }
237 
239 {
240  mTableModel = sourceModel;
241 
242  for ( int i = 0; i < mTableModel->columnCount() - mTableModel->extraColumns(); ++i )
243  {
244  mColumnMapping.append( i );
245  }
246 
247  QSortFilterProxyModel::setSourceModel( sourceModel );
248 
249  // Disconnect any code to update columns in the parent, we handle this manually
250  disconnect( sourceModel, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsAboutToBeInserted( QModelIndex, int, int ) ) );
251  disconnect( sourceModel, SIGNAL( columnsInserted( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsInserted( QModelIndex, int, int ) ) );
252  disconnect( sourceModel, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsAboutToBeRemoved( QModelIndex, int, int ) ) );
253  disconnect( sourceModel, SIGNAL( columnsRemoved( QModelIndex, int, int ) ), this, SLOT( _q_sourceColumnsRemoved( QModelIndex, int, int ) ) );
254 
255  connect( mTableModel, SIGNAL( columnsAboutToBeInserted( QModelIndex, int, int ) ), this, SLOT( onColumnsChanged() ) );
256  connect( mTableModel, SIGNAL( columnsAboutToBeRemoved( QModelIndex, int, int ) ), this, SLOT( onColumnsChanged() ) );
257 }
258 
260 {
261  return mSelectedOnTop;
262 }
263 
265 {
266  mFilteredFeatures = ids;
268  invalidateFilter();
269 }
270 
272 {
273  QgsFeatureIds ids;
274  ids.reserve( rowCount() );
275  for ( int i = 0; i < rowCount(); ++i )
276  {
277  QModelIndex row = index( i, 0 );
278  ids << rowToId( row );
279  }
280  return ids;
281 }
282 
284 {
285  if ( filterMode != mFilterMode )
286  {
287  if ( filterMode == ShowVisible )
288  {
289  connect( mCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentsChanged() ) );
291  }
292  else
293  {
294  disconnect( mCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentsChanged() ) );
295  }
296 
297  mFilterMode = filterMode;
298  invalidateFilter();
299  }
300 }
301 
302 bool QgsAttributeTableFilterModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
303 {
304  Q_UNUSED( sourceParent );
305  switch ( mFilterMode )
306  {
307  case ShowAll:
308  return true;
309 
310  case ShowFilteredList:
311  return mFilteredFeatures.contains( masterModel()->rowToId( sourceRow ) );
312 
313  case ShowSelected:
314  return layer()->selectedFeaturesIds().isEmpty() || layer()->selectedFeaturesIds().contains( masterModel()->rowToId( sourceRow ) );
315 
316  case ShowVisible:
317  return mFilteredFeatures.contains( masterModel()->rowToId( sourceRow ) );
318 
319  case ShowEdited:
320  {
321  QgsVectorLayerEditBuffer* editBuffer = layer()->editBuffer();
322  if ( editBuffer )
323  {
324  QgsFeatureId fid = masterModel()->rowToId( sourceRow );
325 
326  if ( editBuffer->isFeatureAdded( fid ) )
327  return true;
328 
329  if ( editBuffer->isFeatureAttributesChanged( fid ) )
330  return true;
331 
332  if ( editBuffer->isFeatureGeometryChanged( fid ) )
333  return true;
334 
335  return false;
336  }
337  return false;
338  }
339 
340  default:
341  Q_ASSERT( false ); // In debug mode complain
342  return true; // In release mode accept row
343  }
344  // returns are handled in their respective case statement above
345 }
346 
348 {
350  invalidateFilter();
351 }
352 
353 void QgsAttributeTableFilterModel::selectionChanged()
354 {
355  if ( ShowSelected == mFilterMode )
356  {
357  invalidateFilter();
358  }
359  else if ( mSelectedOnTop )
360  {
361  sort( sortColumn(), sortOrder() );
362  invalidate();
363  }
364 }
365 
366 void QgsAttributeTableFilterModel::onColumnsChanged()
367 {
368  setAttributeTableConfig( mConfig );
369 }
370 
371 int QgsAttributeTableFilterModel::mapColumnToSource( int column ) const
372 {
373  if ( mColumnMapping.isEmpty() )
374  return column;
375  if ( column < 0 || column >= mColumnMapping.size() )
376  return -1;
377  else
378  return mColumnMapping.at( column );
379 }
380 
382 {
383  if ( !layer() )
384  return;
385 
386  bool filter = false;
387  QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() );
388  QgsRenderContext renderContext;
392  QgsFeatureRenderer* renderer = layer()->renderer();
393 
394  mFilteredFeatures.clear();
395 
396  if ( !renderer )
397  {
398  QgsDebugMsg( "Cannot get renderer" );
399  return;
400  }
401 
402  const QgsMapSettings& ms = mCanvas->mapSettings();
403  if ( !layer()->isInScaleRange( ms.scale() ) )
404  {
405  QgsDebugMsg( "Out of scale limits" );
406  }
407  else
408  {
409  if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
410  {
411  // setup scale
412  // mapRenderer()->renderContext()->scale is not automaticaly updated when
413  // render extent changes (because it's scale is used to identify if changed
414  // since last render) -> use local context
415  renderContext.setExtent( ms.visibleExtent() );
416  renderContext.setMapToPixel( ms.mapToPixel() );
417  renderContext.setRendererScale( ms.scale() );
418  }
419 
420  filter = renderer && renderer->capabilities() & QgsFeatureRenderer::Filter;
421  }
422 
423  renderer->startRender( renderContext, layer()->fields() );
424 
425  QgsFeatureRequest r( masterModel()->request() );
426  if ( !r.filterRect().isNull() )
427  {
428  r.setFilterRect( r.filterRect().intersect( &rect ) );
429  }
430  else
431  {
432  r.setFilterRect( rect );
433  }
435 
436  QgsFeature f;
437 
438  while ( features.nextFeature( f ) )
439  {
440  renderContext.expressionContext().setFeature( f );
441  if ( !filter || renderer->willRenderFeature( f, renderContext ) )
442  {
443  mFilteredFeatures << f.id();
444  }
445 #if 0
446  if ( t.elapsed() > 5000 )
447  {
448  bool cancel = false;
449  emit progress( i, cancel );
450  if ( cancel )
451  break;
452 
453  t.restart();
454  }
455 #endif
456  }
457 
458  features.close();
459 
460  if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
461  {
462  renderer->stopRender( renderContext );
463  }
464 }
465 
467 {
468  return masterModel()->rowToId( mapToSource( row ).row() );
469 }
470 
472 {
473  return mapFromMaster( masterModel()->idToIndex( fid ) );
474 }
475 
477 {
478  QModelIndexList indexes;
479  Q_FOREACH ( const QModelIndex& idx, masterModel()->idToIndexList( fid ) )
480  {
481  indexes.append( mapFromMaster( idx ) );
482  }
483 
484  return indexes;
485 }
486 
487 QModelIndex QgsAttributeTableFilterModel::mapToSource( const QModelIndex& proxyIndex ) const
488 {
489  if ( !proxyIndex.isValid() )
490  return QModelIndex();
491 
492  int sourceColumn = mapColumnToSource( proxyIndex.column() );
493 
494  // For the action column there is no matching column in the source model, just return the first one
495  // so we are still able to query for the feature id, the feature...
496  if ( sourceColumn == -1 )
497  sourceColumn = 0;
498 
499  return QSortFilterProxyModel::mapToSource( index( proxyIndex.row(), sourceColumn, proxyIndex.parent() ) );
500 }
501 
502 QModelIndex QgsAttributeTableFilterModel::mapFromSource( const QModelIndex& sourceIndex ) const
503 {
504  QModelIndex proxyIndex = QSortFilterProxyModel::mapFromSource( sourceIndex );
505 
506  if ( proxyIndex.column() < 0 )
507  return QModelIndex();
508 
509  int col = mapColumnToSource( proxyIndex.column() );
510  if ( col == -1 )
511  col = 0;
512 
513  return index( proxyIndex.row(), col , proxyIndex.parent() );
514 }
515 
516 Qt::ItemFlags QgsAttributeTableFilterModel::flags( const QModelIndex& index ) const
517 {
518  // Handle the action column flags here, the master model doesn't know it
519  if ( mapColumnToSource( index.column() ) == -1 )
520  return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
521 
522  QModelIndex source_index = mapToSource( index );
523  return masterModel()->flags( source_index );
524 }
525 
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:291
virtual bool willRenderFeature(QgsFeature &feat, QgsRenderContext &context)
Returns whether the renderer will render a feature or not.
void generateListOfVisibleFeatures()
Updates the list of currently visible features on the map canvas.
QgsFeatureId id
Definition: qgsfeature.h:139
QgsFeatureId rowToId(const QModelIndex &row)
Returns the feature id for a given model index.
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
Wrapper for iterator of features from vector data provider or vector layer.
virtual QVariant data(const QModelIndex &index, int role) const override
int extraColumns() const
Empty extra columns to announce from this model.
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Depends on scale if feature will be rendered (rule based )
Definition: qgsrenderer.h:192
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...
QgsAttributeTableModel * masterModel() const
Returns the table model this filter is using.
void setSelectedOnTop(bool selectedOnTop)
Changes the sort order of the features.
void setFilterMode(FilterMode filterMode)
Set the filter mode the filter will use.
QModelIndex mapFromMaster(const QModelIndex &sourceIndex) const
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:355
Features may be filtered, i.e. some features may not be rendered (categorized, rule based ...
Definition: qgsrenderer.h:191
void setRendererScale(double scale)
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
Used by the sorting algorithm.
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
QgsRectangle intersect(const QgsRectangle *rect) const
return the intersection with the given rectangle
This column represents an action widget.
bool isFeatureAdded(QgsFeatureId id) const
Returns true if the specified feature ID has been added but not committed.
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:135
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)
const QgsRectangle & filterRect() const
Get the rectangle from which features will be taken.
void setExtent(const QgsRectangle &extent)
QgsRectangle visibleExtent() const
Return the actual extent derived from requested extent that takes takes output image size into accoun...
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:106
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:140
QgsAttributeTableFilterModel(QgsMapCanvas *canvas, QgsAttributeTableModel *sourceModel, QObject *parent=nullptr)
Make sure, the master model is already loaded, so the selection will get synchronized.
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
QgsFields fields() const
Returns the list of fields of this layer.
The QgsMapSettings class contains configuration for rendering of the map.
Get the feature id of the feature in this row.
int actionColumnIndex() const
Get the index of the first column that contains an action widget.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)=0
Needs to be called when a new render cycle is started.
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.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns.
FilterMode
The filter mode defines how the rows should be filtered.
double scale() const
Return the calculated scale of the map.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect)
This signal is emitted when selection was changed.
QgsRectangle extent() const
Returns the current zoom exent of the map canvas.
bool hidden
Flag that controls if the column is hidden.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsVectorLayerCache * layerCache() const
Returns the layer cache this model uses as backend.
QgsFeatureRenderer * renderer()
Return renderer.
Show only features which have unsaved changes.
bool isFeatureAttributesChanged(QgsFeatureId id) const
Returns true if the specified feature ID has had an attribute changed but not committed.
const QgsMapToPixel & mapToPixel() const
QString name
The name of the attribute if this column represents a field.
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.
QString sortExpression() const
The expression which is used to sort the attribute table.
QgsExpressionContext & expressionContext()
Gets the expression context.
Show only features whose ids are on the filter list. {.
QVector< ColumnConfig > columns() const
Get the list with all columns and their configuration.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
Contains information about the context of a rendering operation.
virtual void stopRender(QgsRenderContext &context)=0
Needs to be called when a render cycle has finished to clean up.
QgsPoint mapToLayerCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from output CRS to layer&#39;s CRS
virtual void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
Sort by the given column using the given order.
int columnCount(const QModelIndex &parent) const override
void setMapToPixel(const QgsMapToPixel &mtp)
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Defines the configuration of a column in the attribute table.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
qint64 QgsFeatureId
Definition: qgsfeature.h:32
QString sortCacheExpression() const
The expression which was used to fill the sorting cache.
QgsFeatureIds filteredFeatures()
Get a list of currently filtered feature ids.
virtual Capabilities capabilities()
Returns details about internals of this renderer.
Definition: qgsrenderer.h:209
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.
bool nextFeature(QgsFeature &f)
This is a container for configuration of the attribute table.
Qt::SortOrder sortOrder() const
Get the sort order.
QString sortExpression() const
Get the expression used for sorting.
void sortColumnChanged(int column, Qt::SortOrder order)
Is emitted whenever the sort column is changed.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
QModelIndexList fidToIndexList(QgsFeatureId fid)
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.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns item flags for the index.
QModelIndex fidToIndex(QgsFeatureId fid) override
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
bool isFeatureGeometryChanged(QgsFeatureId id) const
Returns true if the specified feature ID has had its geometry changed but not committed.