QGIS API Documentation  3.23.0-Master (dd0cd13a00)
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 #include "qgsvectorlayercache.h"
23 
24 #include <QItemSelection>
25 #include <QSettings>
26 
28  : QSortFilterProxyModel( parent )
29 {
30  setSourceModel( sourceModel );
31 }
32 
34 {
35  if ( mSourceLayer )
36  disconnect( mSourceLayer->conditionalStyles(), &QgsConditionalLayerStyles::changed, this, &QgsFeatureListModel::conditionalStylesChanged );
37 
38  QSortFilterProxyModel::setSourceModel( sourceModel );
39  mExpressionContext = sourceModel->layer()->createExpressionContext();
40  mFilterModel = sourceModel;
41 
42  mSourceLayer = sourceModel->layer();
43  connect( mSourceLayer->conditionalStyles(), &QgsConditionalLayerStyles::changed, this, &QgsFeatureListModel::conditionalStylesChanged );
44 }
45 
47 {
48  return mFilterModel->layerCache();
49 }
50 
51 QgsFeatureId QgsFeatureListModel::idxToFid( const QModelIndex &index ) const
52 {
53  return mFilterModel->masterModel()->rowToId( mapToMaster( index ).row() );
54 }
55 
56 QModelIndex QgsFeatureListModel::fidToIdx( const QgsFeatureId fid ) const
57 {
58  return mapFromMaster( mFilterModel->masterModel()->idToIndex( fid ) );
59 }
60 
61 QVariant QgsFeatureListModel::data( const QModelIndex &index, int role ) const
62 {
63  if ( mInjectNull && index.row() == 0 )
64  {
65  if ( role == Qt::DisplayRole )
66  {
68  }
69  else
70  {
71  return QVariant( QVariant::Invalid );
72  }
73  }
74 
75  if ( role == Qt::DisplayRole || role == Qt::EditRole )
76  {
77  QgsFeature feat;
78 
79  mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
80 
81  mExpressionContext.setFeature( feat );
82  return mDisplayExpression.evaluate( &mExpressionContext );
83  }
84 
85  if ( role == FeatureInfoRole )
86  {
87  FeatureInfo featInfo;
88 
89  QgsFeature feat;
90 
91  mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
92 
93  QgsVectorLayerEditBuffer *editBuffer = mFilterModel->layer()->editBuffer();
94 
95  if ( editBuffer )
96  {
97  if ( editBuffer->isFeatureAdded( feat.id() ) )
98  {
99  featInfo.isNew = true;
100  }
101  if ( editBuffer->isFeatureAttributesChanged( feat.id() ) )
102  {
103  featInfo.isEdited = true;
104  }
105  }
106 
107  return QVariant::fromValue( featInfo );
108  }
109  else if ( role == FeatureRole )
110  {
111  QgsFeature feat;
112 
113  mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
114 
115  return QVariant::fromValue( feat );
116  }
117  else if ( role == Qt::TextAlignmentRole )
118  {
119  return static_cast<Qt::Alignment::Int>( Qt::AlignLeft );
120  }
121 
122 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
123  if ( role == Qt::BackgroundColorRole
124  || role == Qt::TextColorRole
125 #else
126  if ( role == Qt::BackgroundRole
127  || role == Qt::ForegroundRole
128 #endif
129  || role == Qt::DecorationRole
130  || role == Qt::FontRole )
131  {
132  QgsVectorLayer *layer = mFilterModel->layer();
133  QgsFeature feat;
134  const QgsFeatureId fid = idxToFid( index );
135  mFilterModel->layerCache()->featureAtId( fid, feat );
136  mExpressionContext.setFeature( feat );
137  QList<QgsConditionalStyle> styles;
138 
139  if ( mRowStylesMap.contains( fid ) )
140  {
141  styles = mRowStylesMap.value( fid );
142  }
143  else
144  {
145  styles = QgsConditionalStyle::matchingConditionalStyles( layer->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext );
146  mRowStylesMap.insert( fid, styles );
147  }
148 
149  const QgsConditionalStyle rowstyle = QgsConditionalStyle::compressStyles( styles );
150 
151  if ( mDisplayExpression.isField() )
152  {
153  const QString fieldName = *mDisplayExpression.referencedColumns().constBegin();
154  styles = layer->conditionalStyles()->fieldStyles( fieldName );
155  styles = QgsConditionalStyle::matchingConditionalStyles( styles, feat.attribute( fieldName ), mExpressionContext );
156  }
157 
158  styles.insert( 0, rowstyle );
159 
161 
162  if ( style.isValid() )
163  {
164 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
165  if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() )
166 #else
167  if ( role == Qt::BackgroundRole && style.validBackgroundColor() )
168 #endif
169  return style.backgroundColor().isValid() ? style.backgroundColor() : QVariant();
170 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
171  if ( role == Qt::TextColorRole && style.validTextColor() )
172 #else
173  if ( role == Qt::ForegroundRole && style.validTextColor() )
174 #endif
175  return style.textColor().isValid() ? style.textColor() : QVariant();
176  if ( role == Qt::DecorationRole )
177  return style.icon().isNull() ? QVariant() : style.icon();
178  if ( role == Qt::FontRole )
179  return style.font();
180  }
181 
182  return QVariant();
183  }
184 
185  return sourceModel()->data( mapToSource( index ), role );
186 }
187 
188 Qt::ItemFlags QgsFeatureListModel::flags( const QModelIndex &index ) const
189 {
190  if ( mInjectNull && index.row() == 0 )
191  {
192  return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
193  }
194  else
195  {
196  return sourceModel()->flags( mapToSource( index ) ) & ~Qt::ItemIsEditable;
197  }
198 }
199 
200 void QgsFeatureListModel::setInjectNull( bool injectNull )
201 {
202  if ( mInjectNull == injectNull )
203  return;
204 
205  if ( injectNull )
207 
208  beginResetModel();
209  mInjectNull = injectNull;
210  endResetModel();
211 }
212 
214 {
215  return mInjectNull;
216 }
217 
219 {
220  return mFilterModel->masterModel();
221 }
222 
223 bool QgsFeatureListModel::setDisplayExpression( const QString &expression )
224 {
225  QgsExpression exp = QgsExpression( expression );
226 
227  exp.prepare( &mExpressionContext );
228 
229  if ( exp.hasParserError() )
230  {
231  mParserErrorString = exp.parserErrorString();
232  return false;
233  }
234 
235  mDisplayExpression = exp;
236 
237  if ( mSortByDisplayExpression )
238  masterModel()->prefetchSortData( expression, 1 );
239 
240  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, 0 ) );
241 
242  invalidate();
243  return true;
244 }
245 
247 {
248  return mParserErrorString;
249 }
250 
252 {
253  return mDisplayExpression.expression();
254 }
255 
256 bool QgsFeatureListModel::featureByIndex( const QModelIndex &index, QgsFeature &feat )
257 {
258  return mFilterModel->layerCache()->featureAtId( idxToFid( index ), feat );
259 }
260 
261 void QgsFeatureListModel::onBeginRemoveRows( const QModelIndex &parent, int first, int last )
262 {
263  beginRemoveRows( parent, first, last );
264 }
265 
266 void QgsFeatureListModel::onEndRemoveRows( const QModelIndex &parent, int first, int last )
267 {
268  Q_UNUSED( parent )
269  Q_UNUSED( first )
270  Q_UNUSED( last )
271  endRemoveRows();
272 }
273 
274 void QgsFeatureListModel::onBeginInsertRows( const QModelIndex &parent, int first, int last )
275 {
276  beginInsertRows( parent, first, last );
277 }
278 
279 void QgsFeatureListModel::onEndInsertRows( const QModelIndex &parent, int first, int last )
280 {
281  Q_UNUSED( parent )
282  Q_UNUSED( first )
283  Q_UNUSED( last )
284  endInsertRows();
285 }
286 
287 void QgsFeatureListModel::conditionalStylesChanged()
288 {
289  mRowStylesMap.clear();
290  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
291 }
292 
294 {
295  return mSortByDisplayExpression;
296 }
297 
298 void QgsFeatureListModel::setSortByDisplayExpression( bool sortByDisplayExpression, Qt::SortOrder order )
299 {
300  mSortByDisplayExpression = sortByDisplayExpression;
301 
302  // If we are sorting by display expression, we do not support injected null
303  if ( mSortByDisplayExpression )
304  setInjectNull( false );
305 
306  setSortRole( QgsAttributeTableModel::SortRole + 1 );
307  setDynamicSortFilter( mSortByDisplayExpression );
308  sort( 0, order );
309 }
310 
311 QModelIndex QgsFeatureListModel::mapToMaster( const QModelIndex &proxyIndex ) const
312 {
313  QModelIndex masterIndex;
314 
315  if ( proxyIndex.isValid() )
316  {
317  if ( mSortByDisplayExpression )
318  {
319  masterIndex = mFilterModel->mapToMaster( mapToSource( proxyIndex ) );
320  }
321  else
322  {
323  const int offset = mInjectNull ? 1 : 0;
324 
325  masterIndex = mFilterModel->mapToMaster( mFilterModel->index( proxyIndex.row() - offset, proxyIndex.column() ) );
326  }
327  }
328  return masterIndex;
329 }
330 
331 QModelIndex QgsFeatureListModel::mapFromMaster( const QModelIndex &masterIndex ) const
332 {
333  QModelIndex proxyIndex;
334 
335  if ( masterIndex.isValid() )
336  {
337  if ( mSortByDisplayExpression )
338  {
339  proxyIndex = mapFromSource( mFilterModel->mapFromMaster( masterIndex ) );
340  }
341  else
342  {
343  const int offset = mInjectNull ? 1 : 0;
344 
345  return createIndex( mFilterModel->mapFromMaster( masterIndex ).row() + offset, 0 );
346  }
347  }
348 
349  return proxyIndex;
350 }
351 
352 QItemSelection QgsFeatureListModel::mapSelectionFromMaster( const QItemSelection &selection ) const
353 {
354  return mapSelectionFromSource( mFilterModel->mapSelectionFromSource( selection ) );
355 }
356 
357 QItemSelection QgsFeatureListModel::mapSelectionToMaster( const QItemSelection &selection ) const
358 {
359  return mFilterModel->mapSelectionToSource( mapSelectionToSource( selection ) );
360 }
361 
362 // Override some methods from QAbstractProxyModel, not that interesting
363 
364 QModelIndex QgsFeatureListModel::mapToSource( const QModelIndex &proxyIndex ) const
365 {
366  QModelIndex sourceIndex;
367 
368  if ( mSortByDisplayExpression )
369  {
370  sourceIndex = QSortFilterProxyModel::mapToSource( proxyIndex );
371  }
372  else
373  {
374  if ( !proxyIndex.isValid() )
375  return QModelIndex();
376 
377  const int offset = mInjectNull ? 1 : 0;
378 
379  sourceIndex = sourceModel()->index( proxyIndex.row() - offset, proxyIndex.column() );
380  }
381 
382  return sourceIndex;
383 }
384 
385 QModelIndex QgsFeatureListModel::mapFromSource( const QModelIndex &sourceIndex ) const
386 {
387  QModelIndex proxyIndex;
388 
389  if ( mSortByDisplayExpression )
390  {
391  proxyIndex = QSortFilterProxyModel::mapFromSource( sourceIndex );
392  }
393  else
394  {
395  if ( sourceIndex.isValid() )
396  proxyIndex = createIndex( sourceIndex.row(), 0 );
397  }
398 
399  return proxyIndex;
400 }
401 
402 QModelIndex QgsFeatureListModel::parent( const QModelIndex &child ) const
403 {
404  Q_UNUSED( child )
405  return QModelIndex();
406 }
407 
408 int QgsFeatureListModel::columnCount( const QModelIndex &parent ) const
409 {
410  Q_UNUSED( parent )
411  return 1;
412 }
413 
414 int QgsFeatureListModel::rowCount( const QModelIndex &parent ) const
415 {
416  Q_UNUSED( parent )
417 
418  const int offset = mInjectNull ? 1 : 0;
419 
420  return sourceModel()->rowCount() + offset;
421 }
422 
424 {
425  return mapFromMaster( masterModel()->idToIndex( fid ) );
426 }
427 
429 {
430  return QModelIndexList() << fidToIndex( fid );
431 }
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 unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
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.