51 : QAbstractTableModel( parent )
52 , mLayerCache( layerCache )
61 mFeat.
setId( std::numeric_limits<int>::min() );
63 if ( !
layer()->isSpatial() )
83 bool QgsAttributeTableModel::loadFeatureAtId(
QgsFeatureId fid )
const
87 if ( fid == std::numeric_limits<int>::min() )
106 void QgsAttributeTableModel::featuresDeleted(
const QgsFeatureIds &fids )
110 const auto constFids = fids;
113 QgsDebugMsgLevel( QStringLiteral(
"(%2) fid: %1, size: %3" ).arg( fid ).arg( mFeatureRequest.
filterType() ).arg( mIdRowMap.size() ), 4 );
120 std::sort( rows.begin(), rows.end() );
124 int currentRowCount = 0;
128 const auto constRows = rows;
129 for (
int row : constRows )
132 qDebug() <<
"Row: " << row <<
", begin " << beginRow <<
", last " << lastRow <<
", current " << currentRowCount <<
", removed " << removedRows;
139 if ( row != lastRow + 1 && lastRow != -1 )
141 if ( rows.count() > 100 && currentRowCount < 10 )
146 removeRows( beginRow - removedRows, currentRowCount );
149 removedRows += currentRowCount;
159 removeRows( beginRow - removedRows, currentRowCount );
167 if ( row < 0 || count < 1 )
170 if ( !mResettingModel )
171 beginRemoveRows( parent, row, row + count - 1 );
175 QgsDebugMsgLevel( QStringLiteral(
"remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
179 for (
int i = row; i < row + count; i++ )
181 for ( SortCache &cache : mSortCaches )
182 cache.sortCache.remove( mRowIdMap[i] );
183 mIdRowMap.remove( mRowIdMap[i] );
184 mRowIdMap.remove( i );
188 int n = mRowIdMap.size() + count;
189 for (
int i = row + count; i < n; i++ )
192 mIdRowMap[id] -= count;
193 mRowIdMap[i - count] = id;
194 mRowIdMap.remove( i );
200 QgsDebugMsgLevel( QStringLiteral(
"after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
202 for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it )
206 for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it )
211 Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
213 if ( !mResettingModel )
219 void QgsAttributeTableModel::featureAdded(
QgsFeatureId fid )
224 if ( mFeat.
id() != fid )
225 featOk = loadFeatureAtId( fid );
229 for ( SortCache &cache : mSortCaches )
231 if ( cache.sortFieldIndex >= 0 )
234 const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
235 const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
236 QVariant sortValue = fieldFormatter->
representValue(
layer(), cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.
attribute( cache.sortFieldIndex ) );
237 cache.sortCache.insert( mFeat.
id(), sortValue );
239 else if ( cache.sortCacheExpression.isValid() )
242 cache.sortCache[mFeat.
id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
247 if ( ! mIdRowMap.contains( fid ) )
249 int n = mRowIdMap.size();
250 if ( !mResettingModel )
251 beginInsertRows( QModelIndex(), n, n );
252 mIdRowMap.insert( fid, n );
253 mRowIdMap.insert( n, fid );
254 if ( !mResettingModel )
261 void QgsAttributeTableModel::updatedFields()
267 void QgsAttributeTableModel::editCommandEnded()
271 bulkEditCommandEnded( );
274 void QgsAttributeTableModel::attributeDeleted(
int idx )
277 for (
const SortCache &cache : mSortCaches )
279 if ( cache.sortCacheAttributes.contains( idx ) )
287 void QgsAttributeTableModel::layerDeleted()
289 mLayerCache =
nullptr;
292 mAttributeWidgetCaches.clear();
294 mWidgetFactories.clear();
295 mWidgetConfigs.clear();
296 mFieldFormatters.clear();
299 void QgsAttributeTableModel::fieldFormatterRemoved(
QgsFieldFormatter *fieldFormatter )
301 for (
int i = 0; i < mFieldFormatters.size(); ++i )
303 if ( mFieldFormatters.at( i ) == fieldFormatter )
308 void QgsAttributeTableModel::attributeValueChanged(
QgsFeatureId fid,
int idx,
const QVariant &value )
311 if ( mBulkEditCommandRunning )
313 mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
316 QgsDebugMsgLevel( QStringLiteral(
"(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.
filterType() ), 2 );
318 for ( SortCache &cache : mSortCaches )
320 if ( cache.sortCacheAttributes.contains( idx ) )
322 if ( cache.sortFieldIndex == -1 )
324 if ( loadFeatureAtId( fid ) )
327 cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
333 const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
334 const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
335 QVariant sortValue = fieldFormatter->
representValue(
layer(), cache.sortFieldIndex, widgetConfig, widgetCache, value );
336 cache.sortCache.insert( fid, sortValue );
343 if ( loadFeatureAtId( fid ) )
348 if ( loadFeatureAtId( fid ) )
352 if ( !mIdRowMap.contains( fid ) )
365 if ( mIdRowMap.contains( fid ) )
376 void QgsAttributeTableModel::loadAttributes()
383 bool ins =
false, rm =
false;
388 mWidgetFactories.clear();
389 mAttributeWidgetCaches.clear();
390 mWidgetConfigs.clear();
392 for (
int idx = 0; idx < fields.
count(); ++idx )
398 mWidgetFactories.append( widgetFactory );
399 mWidgetConfigs.append( setup.
config() );
401 mFieldFormatters.append( fieldFormatter );
406 if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns )
409 beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 );
411 else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns )
414 beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 );
417 mFieldCount = attributes.size();
418 mAttributes = attributes;
420 for ( SortCache &cache : mSortCaches )
422 if ( cache.sortFieldIndex >= mAttributes.count() )
423 cache.sortFieldIndex = -1;
445 mResettingModel =
true;
467 if ( t.elapsed() > 1000 )
476 featureAdded( mFeat.
id() );
485 mResettingModel =
false;
491 if ( fieldName.isNull() )
493 mRowStylesMap.clear();
499 if ( fieldIndex == -1 )
504 emit dataChanged( index( 0, col ), index(
rowCount() - 1, col ) );
517 mRowIdMap.remove( rowA );
518 mRowIdMap.remove( rowB );
519 mRowIdMap.insert( rowA, b );
520 mRowIdMap.insert( rowB, a );
522 mIdRowMap.remove( a );
523 mIdRowMap.remove( b );
524 mIdRowMap.insert( a, rowB );
525 mIdRowMap.insert( b, rowA );
526 Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
534 if ( !mIdRowMap.contains(
id ) )
536 QgsDebugMsg( QStringLiteral(
"idToRow: id %1 not in the map" ).arg(
id ) );
540 return mIdRowMap[id];
545 return index(
idToRow(
id ), 0 );
550 QModelIndexList indexes;
554 indexes.reserve( columns );
555 for (
int column = 0; column < columns; ++column )
557 indexes.append( index( row, column ) );
565 if ( !mRowIdMap.contains( row ) )
567 QgsDebugMsg( QStringLiteral(
"rowToId: row %1 not in the map" ).arg( row ) );
569 return std::numeric_limits<int>::min();
572 return mRowIdMap[row];
577 return mAttributes[col];
582 return mAttributes.indexOf( idx );
588 return mRowIdMap.size();
594 return std::max( 1, mFieldCount + mExtraColumns );
602 if ( role == Qt::DisplayRole )
604 if ( orientation == Qt::Vertical )
606 return QVariant( section );
608 else if ( section >= 0 && section < mFieldCount )
611 return QVariant( attributeName );
615 return tr(
"extra column" );
618 else if ( role == Qt::ToolTipRole )
620 if ( orientation == Qt::Vertical )
623 return tr(
"Feature ID: %1" ).arg(
rowToId( section ) );
639 if ( !index.isValid() || !
layer() ||
640 ( role != Qt::TextAlignmentRole
641 && role != Qt::DisplayRole
642 && role != Qt::ToolTipRole
643 && role != Qt::EditRole
646 #
if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
647 && role != Qt::BackgroundColorRole
648 && role != Qt::TextColorRole
650 && role != Qt::BackgroundRole
651 && role != Qt::ForegroundRole
653 && role != Qt::DecorationRole
654 && role != Qt::FontRole
665 if ( index.column() >= mFieldCount )
668 int fieldId = mAttributes.at( index.column() );
675 unsigned long cacheIndex = role -
SortRole;
676 if ( cacheIndex < mSortCaches.size() )
677 return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
684 if ( role == Qt::TextAlignmentRole )
686 return QVariant( mFieldFormatters.at( index.column() )->alignmentFlag(
layer(), fieldId, mWidgetConfigs.at( index.column() ) ) | Qt::AlignVCenter );
689 if ( mFeat.
id() != rowId || !mFeat.
isValid() )
691 if ( !loadFeatureAtId( rowId ) )
692 return QVariant(
"ERROR" );
694 if ( mFeat.
id() != rowId )
695 return QVariant(
"ERROR" );
698 QVariant val = mFeat.
attribute( fieldId );
702 case Qt::DisplayRole:
703 case Qt::ToolTipRole:
704 return mFieldFormatters.at( index.column() )->representValue(
layer(),
706 mWidgetConfigs.at( index.column() ),
707 mAttributeWidgetCaches.at( index.column() ),
713 case Qt::BackgroundRole:
714 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
715 case Qt::TextColorRole:
717 case Qt::ForegroundRole:
719 case Qt::DecorationRole:
723 QList<QgsConditionalStyle> styles;
724 if ( mRowStylesMap.contains( mFeat.
id() ) )
726 styles = mRowStylesMap[mFeat.
id()];
731 mRowStylesMap.insert( mFeat.
id(), styles );
737 styles.insert( 0, rowstyle );
744 #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
750 if ( role == Qt::DecorationRole )
752 if ( role == Qt::FontRole )
767 if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !
layer()->isEditable() )
770 mRowStylesMap.remove( mFeat.
id() );
772 if ( !
layer()->isModified() )
780 if ( !index.isValid() )
781 return Qt::ItemIsEnabled;
783 if ( index.column() >= mFieldCount || !
layer() )
784 return Qt::NoItemFlags;
786 Qt::ItemFlags
flags = QAbstractTableModel::flags( index );
788 const int fieldIndex = mAttributes[index.column()];
792 flags |= Qt::ItemIsEditable;
797 void QgsAttributeTableModel::bulkEditCommandStarted()
799 mBulkEditCommandRunning =
true;
800 mAttributeValueChanges.clear();
803 void QgsAttributeTableModel::bulkEditCommandEnded()
805 mBulkEditCommandRunning =
false;
808 int changeCount( mAttributeValueChanges.count() );
809 bool fullModelUpdate = changeCount > mLayerCache->
cacheSize() ||
812 QgsDebugMsgLevel( QStringLiteral(
"Bulk edit command ended with %1 modified rows over (%4), cache size is %2, starting %3 update." )
815 .arg( fullModelUpdate ? QStringLiteral(
"full" ) : QStringLiteral(
"incremental" ) )
819 if ( fullModelUpdate )
831 const auto keys = mAttributeValueChanges.keys();
832 for (
const auto &key : keys )
834 attributeValueChanged( key.first, key.second, mAttributeValueChanges.value( key ) );
835 int row(
idToRow( key.first ) );
837 minRow = std::min<int>( row, minRow );
838 minCol = std::min<int>( col, minCol );
839 maxRow = std::max<int>( row, maxRow );
840 maxCol = std::max<int>( col, maxCol );
842 emit dataChanged( createIndex( minRow, minCol ), createIndex( maxRow, maxCol ) );
844 mAttributeValueChanges.clear();
849 mFeat.
setId( std::numeric_limits<int>::min() );
850 emit dataChanged( index1, index2 );
871 for (
int i = 0; i < mAttributes.size(); i++ )
873 f.
setAttribute( mAttributes[i],
data( index( idx.row(), i ), Qt::EditRole ) );
881 if ( column == -1 || column >= mAttributes.count() )
893 if ( cacheIndex >= mSortCaches.size() )
895 mSortCaches.resize( cacheIndex + 1 );
897 SortCache &cache = mSortCaches[cacheIndex];
898 cache.sortCache.clear();
899 cache.sortCacheAttributes.clear();
900 cache.sortFieldIndex = -1;
901 if ( !expressionString.isEmpty() )
902 cache.sortCacheExpression =
QgsExpression( expressionString );
911 QVariant widgetCache;
912 QVariantMap widgetConfig;
914 if ( cache.sortCacheExpression.isField() )
920 if ( cache.sortFieldIndex == -1 )
922 cache.sortCacheExpression.prepare( &mExpressionContext );
924 const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();
926 for (
const QString &col : referencedColumns )
933 cache.sortCacheAttributes.append( cache.sortFieldIndex );
935 widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
936 widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
937 fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
948 if ( cache.sortFieldIndex == -1 )
951 const QVariant cacheValue = cache.sortCacheExpression.evaluate( &mExpressionContext );
952 cache.sortCache.insert( f.
id(), cacheValue );
956 QVariant sortValue = fieldFormatter->
sortValue(
layer(), cache.sortFieldIndex, widgetConfig, widgetCache, f.
attribute( cache.sortFieldIndex ) );
957 cache.sortCache.insert( f.
id(), sortValue );
964 QString expressionString;
966 if ( cacheIndex >= mSortCaches.size() )
967 return expressionString;
969 const QgsExpression &expression = mSortCaches[cacheIndex].sortCacheExpression;
974 expressionString = QString();
976 return expressionString;
988 return mFeatureRequest;
void doAction(QUuid actionId, const QgsFeature &feature, int defaultValueIndex=0, const QgsExpressionContextScope &scope=QgsExpressionContextScope())
Does the given action.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
const QgsFeatureRequest & request() const
Gets the the feature request.
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Remove rows.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns item flags for the index.
QgsFeature feature(const QModelIndex &idx) const
Returns the feature attributes at given model index.
void resetModel()
Resets the model.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
void fieldConditionalStyleChanged(const QString &fieldName)
Handles updating the model when the conditional style for a field changes.
QString sortCacheExpression(unsigned long cacheIndex=0) const
The expression which was used to fill the sorting cache at index cacheIndex.
int fieldIdx(int col) const
Gets field index from column.
QgsAttributeTableModel(QgsVectorLayerCache *layerCache, QObject *parent=nullptr)
Constructor.
void swapRows(QgsFeatureId a, QgsFeatureId b)
Swaps two rows.
void modelChanged()
Model has been changed.
void progress(int i, bool &cancel)
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Updates data on given index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
QModelIndex idToIndex(QgsFeatureId id) const
QgsVectorLayerCache * layerCache() const
Returns the layer cache this model uses as backend.
int extraColumns() const
Empty extra columns to announce from this model.
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx) const
Execute a QgsMapLayerAction.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns header data.
QModelIndexList idToIndexList(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.
int idToRow(QgsFeatureId id) const
Maps feature id to table row.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
@ SortRole
Role used for sorting start here.
@ FeatureIdRole
Get the feature id of the feature in this row.
@ FieldIndexRole
Get the field index of this column.
void setExtraColumns(int extraColumns)
Empty extra columns to announce from this model.
void prefetchColumnData(int column)
Caches the entire data for one column.
int fieldCol(int idx) const
Gets column from field index.
void reload(const QModelIndex &index1, const QModelIndex &index2)
Reloads the model data between indices.
void executeAction(QUuid action, const QModelIndex &idx) const
Execute an action.
QVariant data(const QModelIndex &index, int role) const override
Returns data on the given index.
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.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
An expression node which takes it value from a feature's field.
Class for parsing and evaluation of expressions (formerly called "search strings").
QString expression() const
Returns the original, unmodified expression string.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
bool isValid() const
Checks if this expression is valid.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
FilterType filterType() const
Returns the filter type which is currently set on this request.
@ FilterNone
No filter is applied.
const Flags & flags() const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
bool isValid() const
Returns the validity of this feature.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
static QString fieldToolTipExtended(const QgsField &field, const QgsVectorLayer *layer)
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
Encapsulate a field in an attribute table or data source.
QString displayName() const
Returns the name to use when displaying this field.
Container of fields for a vector layer.
int count() const
Returns number of items.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
An action which can run on map layers.
void triggerForFeature(QgsMapLayer *layer, const QgsFeature &feature)
Triggers the action with the specified layer and feature.
void dataChanged()
Data of layer changed.
This class caches features of a given QgsVectorLayer.
void invalidated()
The cache has been invalidated and cleared.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer and this cache.
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Emitted when an attribute is changed.
QgsVectorLayer * layer()
Returns the layer to which this cache belongs.
int cacheSize()
Returns the maximum number of features this cache will hold.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
Tests whether a field is editable for a particular feature.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void editCommandStarted(const QString &text)
Signal emitted when a new edit command has been started.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void featuresDeleted(const QgsFeatureIds &fids)
Emitted when features have been deleted.
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.
QgsActionManager * actions()
Returns all layer actions defined on this layer.
void editCommandEnded()
Signal emitted, when an edit command successfully ended.
void afterRollBack()
Emitted after changes are rolled back.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
void beforeRollBack()
Emitted before changes are rolled back.
QSet< QgsFeatureId > QgsFeatureIds
#define FID_TO_STRING(fid)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
#define QgsDebugMsgLevel(str, level)