50 : QAbstractTableModel( parent )
51 , mLayerCache( layerCache )
60 mFeat.
setId( std::numeric_limits<int>::min() );
62 if ( !
layer()->isSpatial() )
82 bool QgsAttributeTableModel::loadFeatureAtId(
QgsFeatureId fid )
const 86 if ( fid == std::numeric_limits<int>::min() )
105 void QgsAttributeTableModel::featuresDeleted(
const QgsFeatureIds &fids )
109 const auto constFids = fids;
112 QgsDebugMsgLevel( QStringLiteral(
"(%2) fid: %1, size: %3" ).arg( fid ).arg( mFeatureRequest.
filterType() ).arg( mIdRowMap.size() ), 4 );
119 std::sort( rows.begin(), rows.end() );
123 int currentRowCount = 0;
127 const auto constRows = rows;
128 for (
int row : constRows )
131 qDebug() <<
"Row: " << row <<
", begin " << beginRow <<
", last " << lastRow <<
", current " << currentRowCount <<
", removed " << removedRows;
138 if ( row != lastRow + 1 && lastRow != -1 )
140 if ( rows.count() > 100 && currentRowCount < 10 )
145 removeRows( beginRow - removedRows, currentRowCount );
148 removedRows += currentRowCount;
158 removeRows( beginRow - removedRows, currentRowCount );
166 if ( row < 0 || count < 1 )
169 if ( !mResettingModel )
170 beginRemoveRows( parent, row, row + count - 1 );
174 QgsDebugMsgLevel( QStringLiteral(
"remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
178 for (
int i = row; i < row + count; i++ )
180 for ( SortCache &cache : mSortCaches )
181 cache.sortCache.remove( mRowIdMap[i] );
182 mIdRowMap.remove( mRowIdMap[i] );
183 mRowIdMap.remove( i );
187 int n = mRowIdMap.size() + count;
188 for (
int i = row + count; i < n; i++ )
191 mIdRowMap[id] -= count;
192 mRowIdMap[i - count] = id;
193 mRowIdMap.remove( i );
199 QgsDebugMsgLevel( QStringLiteral(
"after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
201 for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it )
205 for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it )
210 Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
212 if ( !mResettingModel )
218 void QgsAttributeTableModel::featureAdded(
QgsFeatureId fid )
223 if ( mFeat.
id() != fid )
224 featOk = loadFeatureAtId( fid );
228 for ( SortCache &cache : mSortCaches )
230 if ( cache.sortFieldIndex >= 0 )
233 const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
234 const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
235 QVariant sortValue = fieldFormatter->
representValue(
layer(), cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.
attribute( cache.sortFieldIndex ) );
236 cache.sortCache.insert( mFeat.
id(), sortValue );
238 else if ( cache.sortCacheExpression.isValid() )
241 cache.sortCache[mFeat.
id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
246 if ( ! mIdRowMap.contains( fid ) )
248 int n = mRowIdMap.size();
249 if ( !mResettingModel )
250 beginInsertRows( QModelIndex(), n, n );
251 mIdRowMap.insert( fid, n );
252 mRowIdMap.insert( n, fid );
253 if ( !mResettingModel )
260 void QgsAttributeTableModel::updatedFields()
266 void QgsAttributeTableModel::editCommandEnded()
270 bulkEditCommandEnded( );
273 void QgsAttributeTableModel::attributeDeleted(
int idx )
276 for (
const SortCache &cache : mSortCaches )
278 if ( cache.sortCacheAttributes.contains( idx ) )
286 void QgsAttributeTableModel::layerDeleted()
288 mLayerCache =
nullptr;
291 mAttributeWidgetCaches.clear();
293 mWidgetFactories.clear();
294 mWidgetConfigs.clear();
295 mFieldFormatters.clear();
298 void QgsAttributeTableModel::fieldFormatterRemoved(
QgsFieldFormatter *fieldFormatter )
300 for (
int i = 0; i < mFieldFormatters.size(); ++i )
302 if ( mFieldFormatters.at( i ) == fieldFormatter )
307 void QgsAttributeTableModel::attributeValueChanged(
QgsFeatureId fid,
int idx,
const QVariant &value )
310 if ( mBulkEditCommandRunning )
312 mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
315 QgsDebugMsgLevel( QStringLiteral(
"(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.
filterType() ), 2 );
317 for ( SortCache &cache : mSortCaches )
319 if ( cache.sortCacheAttributes.contains( idx ) )
321 if ( cache.sortFieldIndex == -1 )
323 if ( loadFeatureAtId( fid ) )
326 cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
332 const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
333 const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
334 QVariant sortValue = fieldFormatter->
representValue(
layer(), cache.sortFieldIndex, widgetConfig, widgetCache, value );
335 cache.sortCache.insert( fid, sortValue );
342 if ( loadFeatureAtId( fid ) )
347 if ( loadFeatureAtId( fid ) )
351 if ( !mIdRowMap.contains( fid ) )
364 if ( mIdRowMap.contains( fid ) )
375 void QgsAttributeTableModel::loadAttributes()
382 bool ins =
false, rm =
false;
387 mWidgetFactories.clear();
388 mAttributeWidgetCaches.clear();
389 mWidgetConfigs.clear();
391 for (
int idx = 0; idx < fields.
count(); ++idx )
397 mWidgetFactories.append( widgetFactory );
398 mWidgetConfigs.append( setup.
config() );
400 mFieldFormatters.append( fieldFormatter );
405 if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns )
408 beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 );
410 else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns )
413 beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 );
416 mFieldCount = attributes.size();
417 mAttributes = attributes;
419 for ( SortCache &cache : mSortCaches )
421 if ( cache.sortFieldIndex >= mAttributes.count() )
422 cache.sortFieldIndex = -1;
444 mResettingModel =
true;
466 if ( t.elapsed() > 1000 )
475 featureAdded( mFeat.
id() );
484 mResettingModel =
false;
490 if ( fieldName.isNull() )
492 mRowStylesMap.clear();
498 if ( fieldIndex == -1 )
503 emit dataChanged( index( 0, col ), index(
rowCount() - 1, col ) );
516 mRowIdMap.remove( rowA );
517 mRowIdMap.remove( rowB );
518 mRowIdMap.insert( rowA, b );
519 mRowIdMap.insert( rowB, a );
521 mIdRowMap.remove( a );
522 mIdRowMap.remove( b );
523 mIdRowMap.insert( a, rowB );
524 mIdRowMap.insert( b, rowA );
525 Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
533 if ( !mIdRowMap.contains(
id ) )
535 QgsDebugMsg( QStringLiteral(
"idToRow: id %1 not in the map" ).arg(
id ) );
539 return mIdRowMap[id];
544 return index(
idToRow(
id ), 0 );
549 QModelIndexList indexes;
553 indexes.reserve( columns );
554 for (
int column = 0; column < columns; ++column )
556 indexes.append( index( row, column ) );
564 if ( !mRowIdMap.contains( row ) )
566 QgsDebugMsg( QStringLiteral(
"rowToId: row %1 not in the map" ).arg( row ) );
568 return std::numeric_limits<int>::min();
571 return mRowIdMap[row];
576 return mAttributes[col];
581 return mAttributes.indexOf( idx );
587 return mRowIdMap.size();
593 return std::max( 1, mFieldCount + mExtraColumns );
601 if ( role == Qt::DisplayRole )
603 if ( orientation == Qt::Vertical )
605 return QVariant( section );
607 else if ( section >= 0 && section < mFieldCount )
609 QString attributeName =
layer()->
fields().
at( mAttributes.at( section ) ).displayName();
610 return QVariant( attributeName );
614 return tr(
"extra column" );
617 else if ( role == Qt::ToolTipRole )
619 if ( orientation == Qt::Vertical )
622 return tr(
"Feature ID: %1" ).arg(
rowToId( section ) );
638 if ( !index.isValid() || !
layer() ||
639 ( role != Qt::TextAlignmentRole
640 && role != Qt::DisplayRole
641 && role != Qt::ToolTipRole
642 && role != Qt::EditRole
645 && role != Qt::BackgroundColorRole
646 && role != Qt::TextColorRole
647 && role != Qt::DecorationRole
648 && role != Qt::FontRole
659 if ( index.column() >= mFieldCount )
662 int fieldId = mAttributes.at( index.column() );
669 unsigned long cacheIndex = role -
SortRole;
670 if ( cacheIndex < mSortCaches.size() )
671 return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
678 if ( role == Qt::TextAlignmentRole )
680 return QVariant( mFieldFormatters.at( index.column() )->alignmentFlag(
layer(), fieldId, mWidgetConfigs.at( index.column() ) ) | Qt::AlignVCenter );
683 if ( mFeat.
id() != rowId || !mFeat.
isValid() )
685 if ( !loadFeatureAtId( rowId ) )
686 return QVariant(
"ERROR" );
688 if ( mFeat.
id() != rowId )
689 return QVariant(
"ERROR" );
692 QVariant val = mFeat.
attribute( fieldId );
696 case Qt::DisplayRole:
697 case Qt::ToolTipRole:
698 return mFieldFormatters.at( index.column() )->representValue(
layer(),
700 mWidgetConfigs.at( index.column() ),
701 mAttributeWidgetCaches.at( index.column() ),
707 case Qt::BackgroundRole:
708 case Qt::TextColorRole:
709 case Qt::DecorationRole:
713 QList<QgsConditionalStyle> styles;
714 if ( mRowStylesMap.contains( mFeat.
id() ) )
716 styles = mRowStylesMap[mFeat.
id()];
721 mRowStylesMap.insert( mFeat.
id(), styles );
727 styles.insert( 0, rowstyle );
736 if ( role == Qt::DecorationRole )
738 if ( role == Qt::FontRole )
753 if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !
layer()->
isEditable() )
759 mRowStylesMap.remove( mFeat.
id() );
766 if ( !index.isValid() )
767 return Qt::ItemIsEnabled;
769 if ( index.column() >= mFieldCount || !
layer() )
770 return Qt::NoItemFlags;
772 Qt::ItemFlags
flags = QAbstractTableModel::flags( index );
774 const int fieldIndex = mAttributes[index.column()];
778 flags |= Qt::ItemIsEditable;
783 void QgsAttributeTableModel::bulkEditCommandStarted()
785 mBulkEditCommandRunning =
true;
786 mAttributeValueChanges.clear();
789 void QgsAttributeTableModel::bulkEditCommandEnded()
791 mBulkEditCommandRunning =
false;
794 int changeCount( mAttributeValueChanges.count() );
795 bool fullModelUpdate = changeCount > mLayerCache->
cacheSize() ||
798 QgsDebugMsgLevel( QStringLiteral(
"Bulk edit command ended with %1 modified rows over (%4), cache size is %2, starting %3 update." )
801 .arg( fullModelUpdate ? QStringLiteral(
"full" ) : QStringLiteral(
"incremental" ) )
805 if ( fullModelUpdate )
817 const auto keys = mAttributeValueChanges.keys();
818 for (
const auto &key : keys )
820 attributeValueChanged( key.first, key.second, mAttributeValueChanges.value( key ) );
821 int row(
idToRow( key.first ) );
823 minRow = std::min<int>( row, minRow );
824 minCol = std::min<int>( col, minCol );
825 maxRow = std::max<int>( row, maxRow );
826 maxCol = std::max<int>( col, maxCol );
828 emit dataChanged( createIndex( minRow, minCol ), createIndex( maxRow, maxCol ) );
830 mAttributeValueChanges.clear();
835 mFeat.
setId( std::numeric_limits<int>::min() );
836 emit dataChanged( index1, index2 );
857 for (
int i = 0; i < mAttributes.size(); i++ )
859 f.
setAttribute( mAttributes[i],
data( index( idx.row(), i ), Qt::EditRole ) );
867 if ( column == -1 || column >= mAttributes.count() )
879 if ( cacheIndex >= mSortCaches.size() )
881 mSortCaches.resize( cacheIndex + 1 );
883 SortCache &cache = mSortCaches[cacheIndex];
884 cache.sortCache.clear();
885 cache.sortCacheAttributes.clear();
886 cache.sortFieldIndex = -1;
887 if ( !expressionString.isEmpty() )
888 cache.sortCacheExpression =
QgsExpression( expressionString );
897 QVariant widgetCache;
898 QVariantMap widgetConfig;
900 if ( cache.sortCacheExpression.isField() )
906 if ( cache.sortFieldIndex == -1 )
908 cache.sortCacheExpression.prepare( &mExpressionContext );
910 const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();
912 for (
const QString &col : referencedColumns )
919 cache.sortCacheAttributes.append( cache.sortFieldIndex );
921 widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
922 widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
923 fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
934 if ( cache.sortFieldIndex == -1 )
937 const QVariant cacheValue = cache.sortCacheExpression.evaluate( &mExpressionContext );
938 cache.sortCache.insert( f.
id(), cacheValue );
942 QVariant sortValue = fieldFormatter->
sortValue(
layer(), cache.sortFieldIndex, widgetConfig, widgetCache, f.
attribute( cache.sortFieldIndex ) );
943 cache.sortCache.insert( f.
id(), sortValue );
950 QString expressionString;
952 if ( cacheIndex >= mSortCaches.size() )
953 return expressionString;
955 const QgsExpression &expression = mSortCaches[cacheIndex].sortCacheExpression;
960 expressionString = QString();
962 return expressionString;
974 return mFeatureRequest;
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool isValid() const
Returns the validity of this feature.
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsActionManager * actions()
Returns all layer actions defined on this layer.
Wrapper for iterator of features from vector data provider or vector layer.
void featuresDeleted(const QgsFeatureIds &fids)
Emitted when features have been deleted.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
int extraColumns() const
Empty extra columns to announce from this model.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
void doAction(QUuid actionId, const QgsFeature &feature, int defaultValueIndex=0, const QgsExpressionContextScope &scope=QgsExpressionContextScope())
Does the given action.
void triggerForFeature(QgsMapLayer *layer, const QgsFeature &feature)
Triggers the action with the specified layer and feature.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
QSet< QgsFeatureId > QgsFeatureIds
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
const Flags & flags() const
Get the field index of this column.
void beforeRollBack()
Emitted before changes are rolled back.
void invalidated()
The cache has been invalidated and cleared.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QString sortCacheExpression(unsigned long cacheIndex=0) const
The expression which was used to fill the sorting cache at index cacheIndex.
void prefetchSortData(const QString &expression, unsigned long cacheIndex=0)
Prefetches the entire data for an expression.
static QString fieldToolTip(const QgsField &field)
Returns a HTML formatted tooltip string for a field, containing details like the field name...
void reload(const QModelIndex &index1, const QModelIndex &index2)
Reloads the model data between indices.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
FilterType filterType() const
Returns the filter type which is currently set on this request.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Container of fields for a vector layer.
bool setAttribute(int field, const QVariant &attr)
Set an attribute's value by field index.
bool validBackgroundColor() const
Check if the background color is valid for render.
QPixmap icon() const
The icon set for style generated from the set symbol.
void fieldConditionalStyleChanged(const QString &fieldName)
Handles updating the model when the conditional style for a field changes.
QgsVectorLayer * layer()
Returns the layer to which this cache belongs.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Emitted when an attribute is changed.
int count() const
Returns number of items.
void editCommandEnded()
Signal emitted, when an edit command successfully ended.
void resetModel()
Resets the model.
QModelIndex idToIndex(QgsFeatureId id) const
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
QVariant data(const QModelIndex &index, int role) const override
Returns data on the given index.
Get the feature id of the feature in this row.
QgsFeature feature(const QModelIndex &idx) const
Returns the feature attributes at given model index.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer and this cache.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Role used for sorting start here.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns header data.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Conditional styling for a rule.
#define QgsDebugMsgLevel(str, level)
void setExtraColumns(int extraColumns)
Empty extra columns to announce from this model.
bool isValid() const
Checks if this expression is valid.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories...
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
void afterRollBack()
Emitted after changes are rolled back.
bool isValid() const
isValid Check if this rule is valid.
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
An expression node which takes it value from a feature's field.
QColor backgroundColor() const
The background color for style.
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Remove rows.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
const QgsFeatureRequest & request() const
Gets the the feature request.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Encapsulate a field in an attribute table or data source.
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
QModelIndexList idToIndexList(QgsFeatureId id) const
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
QString expression() const
Returns the original, unmodified expression string.
This class caches features of a given QgsVectorLayer.
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
void progress(int i, bool &cancel)
void modelChanged()
Model has been changed.
QColor textColor() const
The text color set for style.
QgsAttributeTableModel(QgsVectorLayerCache *layerCache, QObject *parent=nullptr)
Constructor.
int fieldCol(int idx) const
Gets column from field index.
void editCommandStarted(const QString &text)
Signal emitted when a new edit command has been started.
int fieldIdx(int col) const
Gets field index from column.
#define FID_TO_STRING(fid)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
bool validTextColor() const
Check if the text color is valid for render.
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx) const
Execute a QgsMapLayerAction.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
void dataChanged()
Data of layer changed.
int cacheSize()
Returns the maximum number of features this cache will hold.
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
QList< int > QgsAttributeList
QFont font() const
The font for the style.
void swapRows(QgsFeatureId a, QgsFeatureId b)
Swaps two rows.
bool nextFeature(QgsFeature &f)
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
int idToRow(QgsFeatureId id) const
Maps feature id to table row.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Updates data on given index.
void executeAction(QUuid action, const QModelIndex &idx) const
Execute an action.
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature)
An action which can run on map layers.
void prefetchColumnData(int column)
Caches the entire data for one column.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns item flags for the index.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.