QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsfeaturelistmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfeaturelistmodel.cpp
3  ---------------------
4  begin : February 2013
5  copyright : (C) 2013 by Matthias Kuhn
6  email : matthias at opengis 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 #include "qgsexception.h"
16 #include "qgsvectordataprovider.h"
17 #include "qgsfeaturelistmodel.h"
18 #include "qgsattributetablemodel.h"
21 #include "qgsapplication.h"
22 
23 #include <QItemSelection>
24 #include <QSettings>
25 
27  : QSortFilterProxyModel( parent )
28 {
29  setSourceModel( sourceModel );
30 }
31 
33 {
34  if ( mSourceLayer )
35  disconnect( mSourceLayer->conditionalStyles(), &QgsConditionalLayerStyles::changed, this, &QgsFeatureListModel::conditionalStylesChanged );
36 
37  QSortFilterProxyModel::setSourceModel( sourceModel );
38  mExpressionContext = sourceModel->layer()->createExpressionContext();
39  mFilterModel = sourceModel;
40 
41  mSourceLayer = sourceModel->layer();
42  connect( mSourceLayer->conditionalStyles(), &QgsConditionalLayerStyles::changed, this, &QgsFeatureListModel::conditionalStylesChanged );
43 }
44 
46 {
47  return mFilterModel->layerCache();
48 }
49 
50 QgsFeatureId QgsFeatureListModel::idxToFid( const QModelIndex &index ) const
51 {
52  return mFilterModel->masterModel()->rowToId( mapToMaster( index ).row() );
53 }
54 
55 QModelIndex QgsFeatureListModel::fidToIdx( const QgsFeatureId fid ) const
56 {
57  return mapFromMaster( mFilterModel->masterModel()->idToIndex( fid ) );
58 }
59 
60 QVariant QgsFeatureListModel::data( const QModelIndex &index, int role ) const
61 {
62  if ( mInjectNull && index.row() == 0 )
63  {
64  if ( role == Qt::DisplayRole )
65  {
67  }
68  else
69  {
70  return QVariant( QVariant::Invalid );
71  }
72  }
73 
74  if ( role == Qt::DisplayRole || role == Qt::EditRole )
75  {
76  QgsFeature feat;
77 
78  mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
79 
80  mExpressionContext.setFeature( feat );
81  return mDisplayExpression.evaluate( &mExpressionContext );
82  }
83 
84  if ( role == FeatureInfoRole )
85  {
86  FeatureInfo featInfo;
87 
88  QgsFeature feat;
89 
90  mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
91 
92  QgsVectorLayerEditBuffer *editBuffer = mFilterModel->layer()->editBuffer();
93 
94  if ( editBuffer )
95  {
96  if ( editBuffer->isFeatureAdded( feat.id() ) )
97  {
98  featInfo.isNew = true;
99  }
100  if ( editBuffer->isFeatureAttributesChanged( feat.id() ) )
101  {
102  featInfo.isEdited = true;
103  }
104  }
105 
106  return QVariant::fromValue( featInfo );
107  }
108  else if ( role == FeatureRole )
109  {
110  QgsFeature feat;
111 
112  mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
113 
114  return QVariant::fromValue( feat );
115  }
116  else if ( role == Qt::TextAlignmentRole )
117  {
118  return Qt::AlignLeft;
119  }
120 
121 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
122  if ( role == Qt::BackgroundColorRole
123  || role == Qt::TextColorRole
124 #else
125  if ( role == Qt::BackgroundRole
126  || role == Qt::ForegroundRole
127 #endif
128  || role == Qt::DecorationRole
129  || role == Qt::FontRole )
130  {
131  QgsVectorLayer *layer = mFilterModel->layer();
132  QgsFeature feat;
133  QgsFeatureId fid = idxToFid( index );
134  mFilterModel->layerCache()->featureAtId( fid, feat );
135  mExpressionContext.setFeature( feat );
136  QList<QgsConditionalStyle> styles;
137 
138  if ( mRowStylesMap.contains( fid ) )
139  {
140  styles = mRowStylesMap.value( fid );
141  }
142  else
143  {
144  styles = QgsConditionalStyle::matchingConditionalStyles( layer->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext );
145  mRowStylesMap.insert( fid, styles );
146  }
147 
149 
150  if ( mDisplayExpression.isField() )
151  {
152  QString fieldName = *mDisplayExpression.referencedColumns().constBegin();
153  styles = layer->conditionalStyles()->fieldStyles( fieldName );
154  styles = QgsConditionalStyle::matchingConditionalStyles( styles, feat.attribute( fieldName ), mExpressionContext );
155  }
156 
157  styles.insert( 0, rowstyle );
158 
160 
161  if ( style.isValid() )
162  {
163 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
164  if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() )
165 #else
166  if ( role == Qt::BackgroundRole && style.validBackgroundColor() )
167 #endif
168  return style.backgroundColor().isValid() ? style.backgroundColor() : QVariant();
169 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
170  if ( role == Qt::TextColorRole && style.validTextColor() )
171 #else
172  if ( role == Qt::ForegroundRole && style.validTextColor() )
173 #endif
174  return style.textColor().isValid() ? style.textColor() : QVariant();
175  if ( role == Qt::DecorationRole )
176  return style.icon().isNull() ? QVariant() : style.icon();
177  if ( role == Qt::FontRole )
178  return style.font();
179  }
180 
181  return QVariant();
182  }
183 
184  return sourceModel()->data( mapToSource( index ), role );
185 }
186 
187 Qt::ItemFlags QgsFeatureListModel::flags( const QModelIndex &index ) const
188 {
189  if ( mInjectNull && index.row() == 0 )
190  {
191  return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
192  }
193  else
194  {
195  return sourceModel()->flags( mapToSource( index ) ) & ~Qt::ItemIsEditable;
196  }
197 }
198 
199 void QgsFeatureListModel::setInjectNull( bool injectNull )
200 {
201  if ( mInjectNull == injectNull )
202  return;
203 
204  if ( injectNull )
206 
207  beginResetModel();
208  mInjectNull = injectNull;
209  endResetModel();
210 }
211 
213 {
214  return mInjectNull;
215 }
216 
218 {
219  return mFilterModel->masterModel();
220 }
221 
222 bool QgsFeatureListModel::setDisplayExpression( const QString &expression )
223 {
224  QgsExpression exp = QgsExpression( expression );
225 
226  exp.prepare( &mExpressionContext );
227 
228  if ( exp.hasParserError() )
229  {
230  mParserErrorString = exp.parserErrorString();
231  return false;
232  }
233 
234  mDisplayExpression = exp;
235 
236  if ( mSortByDisplayExpression )
237  masterModel()->prefetchSortData( expression, 1 );
238 
239  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ) );
240 
241  invalidate();
242  return true;
243 }
244 
246 {
247  return mParserErrorString;
248 }
249 
251 {
252  return mDisplayExpression.expression();
253 }
254 
255 bool QgsFeatureListModel::featureByIndex( const QModelIndex &index, QgsFeature &feat )
256 {
257  return mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
258 }
259 
260 void QgsFeatureListModel::onBeginRemoveRows( const QModelIndex &parent, int first, int last )
261 {
262  beginRemoveRows( parent, first, last );
263 }
264 
265 void QgsFeatureListModel::onEndRemoveRows( const QModelIndex &parent, int first, int last )
266 {
267  Q_UNUSED( parent )
268  Q_UNUSED( first )
269  Q_UNUSED( last )
270  endRemoveRows();
271 }
272 
273 void QgsFeatureListModel::onBeginInsertRows( const QModelIndex &parent, int first, int last )
274 {
275  beginInsertRows( parent, first, last );
276 }
277 
278 void QgsFeatureListModel::onEndInsertRows( const QModelIndex &parent, int first, int last )
279 {
280  Q_UNUSED( parent )
281  Q_UNUSED( first )
282  Q_UNUSED( last )
283  endInsertRows();
284 }
285 
286 void QgsFeatureListModel::conditionalStylesChanged()
287 {
288  mRowStylesMap.clear();
289  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
290 }
291 
293 {
294  return mSortByDisplayExpression;
295 }
296 
297 void QgsFeatureListModel::setSortByDisplayExpression( bool sortByDisplayExpression, Qt::SortOrder order )
298 {
299  mSortByDisplayExpression = sortByDisplayExpression;
300 
301  // If we are sorting by display expression, we do not support injected null
302  if ( mSortByDisplayExpression )
303  setInjectNull( false );
304 
305  setSortRole( QgsAttributeTableModel::SortRole + 1 );
306  setDynamicSortFilter( mSortByDisplayExpression );
307  sort( 0, order );
308 }
309 
310 QModelIndex QgsFeatureListModel::mapToMaster( const QModelIndex &proxyIndex ) const
311 {
312  QModelIndex masterIndex;
313 
314  if ( proxyIndex.isValid() )
315  {
316  if ( mSortByDisplayExpression )
317  {
318  masterIndex = mFilterModel->mapToMaster( mapToSource( proxyIndex ) );
319  }
320  else
321  {
322  int offset = mInjectNull ? 1 : 0;
323 
324  masterIndex = mFilterModel->mapToMaster( mFilterModel->index( proxyIndex.row() - offset, proxyIndex.column() ) );
325  }
326  }
327  return masterIndex;
328 }
329 
330 QModelIndex QgsFeatureListModel::mapFromMaster( const QModelIndex &masterIndex ) const
331 {
332  QModelIndex proxyIndex;
333 
334  if ( masterIndex.isValid() )
335  {
336  if ( mSortByDisplayExpression )
337  {
338  proxyIndex = mapFromSource( mFilterModel->mapFromMaster( masterIndex ) );
339  }
340  else
341  {
342  int offset = mInjectNull ? 1 : 0;
343 
344  return createIndex( mFilterModel->mapFromMaster( masterIndex ).row() + offset, 0 );
345  }
346  }
347 
348  return proxyIndex;
349 }
350 
351 QItemSelection QgsFeatureListModel::mapSelectionFromMaster( const QItemSelection &selection ) const
352 {
353  return mapSelectionFromSource( mFilterModel->mapSelectionFromSource( selection ) );
354 }
355 
356 QItemSelection QgsFeatureListModel::mapSelectionToMaster( const QItemSelection &selection ) const
357 {
358  return mFilterModel->mapSelectionToSource( mapSelectionToSource( selection ) );
359 }
360 
361 // Override some methods from QAbstractProxyModel, not that interesting
362 
363 QModelIndex QgsFeatureListModel::mapToSource( const QModelIndex &proxyIndex ) const
364 {
365  QModelIndex sourceIndex;
366 
367  if ( mSortByDisplayExpression )
368  {
369  sourceIndex = QSortFilterProxyModel::mapToSource( proxyIndex );
370  }
371  else
372  {
373  if ( !proxyIndex.isValid() )
374  return QModelIndex();
375 
376  int offset = mInjectNull ? 1 : 0;
377 
378  sourceIndex = sourceModel()->index( proxyIndex.row() - offset, proxyIndex.column() );
379  }
380 
381  return sourceIndex;
382 }
383 
384 QModelIndex QgsFeatureListModel::mapFromSource( const QModelIndex &sourceIndex ) const
385 {
386  QModelIndex proxyIndex;
387 
388  if ( mSortByDisplayExpression )
389  {
390  proxyIndex = QSortFilterProxyModel::mapFromSource( sourceIndex );
391  }
392  else
393  {
394  if ( sourceIndex.isValid() )
395  proxyIndex = createIndex( sourceIndex.row(), 0 );
396  }
397 
398  return proxyIndex;
399 }
400 
401 QModelIndex QgsFeatureListModel::parent( const QModelIndex &child ) const
402 {
403  Q_UNUSED( child )
404  return QModelIndex();
405 }
406 
407 int QgsFeatureListModel::columnCount( const QModelIndex &parent ) const
408 {
409  Q_UNUSED( parent )
410  return 1;
411 }
412 
413 int QgsFeatureListModel::rowCount( const QModelIndex &parent ) const
414 {
415  Q_UNUSED( parent )
416 
417  int offset = mInjectNull ? 1 : 0;
418 
419  return sourceModel()->rowCount() + offset;
420 }
421 
423 {
424  return mapFromMaster( masterModel()->idToIndex( fid ) );
425 }
426 
428 {
429  return QModelIndexList() << fidToIndex( fid );
430 }
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
QModelIndex mapFromMaster(const QModelIndex &sourceIndex) const
QModelIndex mapToMaster(const QModelIndex &proxyIndex) const
QgsVectorLayerCache * layerCache() const
Returns the layerCache this filter acts on.
QgsAttributeTableModel * masterModel() const
Returns the table model this filter is using.
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
QModelIndex idToIndex(QgsFeatureId id) const
void prefetchSortData(const QString &expression, unsigned long cacheIndex=0)
Prefetches the entire data for an expression.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
@ SortRole
Role used for sorting start here.
void changed()
Emitted when the conditional styles are changed.
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
Conditional styling for a rule.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
QColor backgroundColor() const
The background color for style.
QColor textColor() const
The text color set for style.
QFont font() const
The font for the style.
bool validTextColor() const
Check if the text color is valid for render.
bool isValid() const
isValid Check if this rule is valid.
QPixmap icon() const
The icon set for style generated from the set symbol.
bool validBackgroundColor() const
Check if the background color is valid for render.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QString expression() const
Returns the original, unmodified expression string.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString parserErrorString() const
Returns parser error.
bool isField() const
Checks whether an expression consists only of a single field reference.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
QVariant evaluate()
Evaluate the feature and return the result.
QgsFeatureId idxToFid(const QModelIndex &index) const
Returns the feature ID corresponding to an index from the model.
virtual void setSourceModel(QgsAttributeTableFilterModel *sourceModel)
void setInjectNull(bool injectNull)
If true is specified, a NULL value will be injected.
Q_DECL_DEPRECATED void onEndRemoveRows(const QModelIndex &parent, int first, int last)
Does nothing except for calling endRemoveRows()
Q_DECL_DEPRECATED void onBeginInsertRows(const QModelIndex &parent, int first, int last)
Does nothing except for calling beginInsertRows()
Qt::ItemFlags flags(const QModelIndex &index) const override
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QModelIndexList fidToIndexList(QgsFeatureId fid)
bool featureByIndex(const QModelIndex &index, QgsFeature &feat)
virtual QModelIndex mapToMaster(const QModelIndex &proxyIndex) const
void setSortByDisplayExpression(bool sortByDisplayExpression, Qt::SortOrder order=Qt::AscendingOrder)
Sort this model by its display expression.
QModelIndex fidToIdx(QgsFeatureId fid) const
Returns the model index corresponding to a feature ID.
QString parserErrorString()
Returns a detailed message about errors while parsing a QgsExpression.
Q_DECL_DEPRECATED void onBeginRemoveRows(const QModelIndex &parent, int first, int last)
Does nothing except for calling beginRemoveRows()
QVariant data(const QModelIndex &index, int role) const override
bool injectNull()
Returns the current state of null value injection.
virtual QItemSelection mapSelectionToMaster(const QItemSelection &selection) const
bool sortByDisplayExpression() const
Sort this model by its display expression.
QModelIndex parent(const QModelIndex &child) const override
bool setDisplayExpression(const QString &expression)
virtual QModelIndex mapFromMaster(const QModelIndex &sourceIndex) const
QString displayExpression() const
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
QgsVectorLayerCache * layerCache()
Returns the vector layer cache which is being used to populate the model.
QModelIndex fidToIndex(QgsFeatureId fid) override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QgsFeatureListModel(QgsAttributeTableFilterModel *sourceModel, QObject *parent=nullptr)
Constructor for QgsFeatureListModel.
QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
Q_DECL_DEPRECATED void onEndInsertRows(const QModelIndex &parent, int first, int last)
Does nothing except for calling endInsertRows()
virtual QItemSelection mapSelectionFromMaster(const QItemSelection &selection) const
QgsAttributeTableModel * masterModel()
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:287
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
This class caches features of a given QgsVectorLayer.
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
bool isFeatureAdded(QgsFeatureId id) const
Returns true if the specified feature ID has been added but not committed.
bool isFeatureAttributesChanged(QgsFeatureId id) const
Returns true if the specified feature ID has had an attribute changed but not committed.
Represents a vector layer which manages a vector based data sets.
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
Q_INVOKABLE QgsVectorLayerEditBuffer * editBuffer()
Buffer with uncommitted editing operations. Only valid after editing has been turned on.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
bool isEdited
True if feature has been edited.
bool isNew
True if feature is a newly added feature.