/*************************************************************************** QgsAttributeTableModel.cpp -------------------------------------- Date : Feb 2009 Copyright : (C) 2009 Vita Cizek Email : weetya (at) gmail.com *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "qgsapplication.h" #include "qgsattributetablemodel.h" #include "qgsattributetablefiltermodel.h" #include "qgsactionmanager.h" #include "qgseditorwidgetregistry.h" #include "qgseditorwidgetfactory.h" #include "qgsexpression.h" #include "qgsfeatureiterator.h" #include "qgsconditionalstyle.h" #include "qgsfields.h" #include "qgsfieldformatter.h" #include "qgslogger.h" #include "qgsmapcanvas.h" #include "qgsmaplayeractionregistry.h" #include "qgsrenderer.h" #include "qgsvectorlayer.h" #include "qgsvectordataprovider.h" #include "qgssymbollayerutils.h" #include "qgsfieldformatterregistry.h" #include "qgsgui.h" #include "qgsexpressionnodeimpl.h" #include "qgsvectorlayerjoininfo.h" #include "qgsvectorlayerjoinbuffer.h" #include <QVariant> #include <limits> QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache, QObject *parent ) : QAbstractTableModel( parent ) , mLayerCache( layerCache ) , mFieldCount( 0 ) , mSortFieldIndex( -1 ) , mExtraColumns( 0 ) { mExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layerCache->layer() ) ); if ( layerCache->layer()->geometryType() == QgsWkbTypes::NullGeometry ) { mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry ); } mFeat.setId( std::numeric_limits<int>::min() ); if ( !layer()->isSpatial() ) mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry ); loadAttributes(); connect( mLayerCache, &QgsVectorLayerCache::attributeValueChanged, this, &QgsAttributeTableModel::attributeValueChanged ); connect( layer(), &QgsVectorLayer::featuresDeleted, this, &QgsAttributeTableModel::featuresDeleted ); connect( layer(), &QgsVectorLayer::attributeDeleted, this, &QgsAttributeTableModel::attributeDeleted ); connect( layer(), &QgsVectorLayer::updatedFields, this, &QgsAttributeTableModel::updatedFields ); connect( layer(), &QgsVectorLayer::editCommandEnded, this, &QgsAttributeTableModel::editCommandEnded ); connect( mLayerCache, &QgsVectorLayerCache::featureAdded, this, [ = ]( QgsFeatureId id ) { featureAdded( id ); } ); connect( mLayerCache, &QgsVectorLayerCache::cachedLayerDeleted, this, &QgsAttributeTableModel::layerDeleted ); } bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid ) const { QgsDebugMsgLevel( QString( "loading feature %1" ).arg( fid ), 3 ); if ( fid == std::numeric_limits<int>::min() ) { return false; } return mLayerCache->featureAtId( fid, mFeat ); } int QgsAttributeTableModel::extraColumns() const { return mExtraColumns; } void QgsAttributeTableModel::setExtraColumns( int extraColumns ) { mExtraColumns = extraColumns; loadAttributes(); } void QgsAttributeTableModel::featuresDeleted( const QgsFeatureIds &fids ) { QList<int> rows; Q_FOREACH ( QgsFeatureId fid, fids ) { QgsDebugMsgLevel( QString( "(%2) fid: %1, size: %3" ).arg( fid ).arg( mFeatureRequest.filterType() ).arg( mIdRowMap.size() ), 4 ); int row = idToRow( fid ); if ( row != -1 ) rows << row; } std::sort( rows.begin(), rows.end() ); int lastRow = -1; int beginRow = -1; int currentRowCount = 0; int removedRows = 0; bool reset = false; Q_FOREACH ( int row, rows ) { #if 0 qDebug() << "Row: " << row << ", begin " << beginRow << ", last " << lastRow << ", current " << currentRowCount << ", removed " << removedRows; #endif if ( lastRow == -1 ) { beginRow = row; } if ( row != lastRow + 1 && lastRow != -1 ) { if ( rows.count() > 100 && currentRowCount < 10 ) { reset = true; break; } removeRows( beginRow - removedRows, currentRowCount ); beginRow = row; removedRows += currentRowCount; currentRowCount = 0; } currentRowCount++; lastRow = row; } if ( !reset ) removeRows( beginRow - removedRows, currentRowCount ); else resetModel(); } bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent ) { if ( row < 0 || count < 1 ) return false; beginRemoveRows( parent, row, row + count - 1 ); #ifdef QGISDEBUG if ( 3 <= QgsLogger::debugLevel() ) QgsDebugMsgLevel( QString( "remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 ); #endif // clean old references for ( int i = row; i < row + count; i++ ) { mSortCache.remove( mRowIdMap[i] ); mIdRowMap.remove( mRowIdMap[i] ); mRowIdMap.remove( i ); } // update maps int n = mRowIdMap.size() + count; for ( int i = row + count; i < n; i++ ) { QgsFeatureId id = mRowIdMap[i]; mIdRowMap[id] -= count; mRowIdMap[i - count] = id; mRowIdMap.remove( i ); } #ifdef QGISDEBUG if ( 4 <= QgsLogger::debugLevel() ) { QgsDebugMsgLevel( QString( "after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 ); QgsDebugMsgLevel( "id->row", 4 ); for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it ) QgsDebugMsgLevel( QString( "%1->%2" ).arg( FID_TO_STRING( it.key() ) ).arg( *it ), 4 ); QgsDebugMsgLevel( "row->id", 4 ); for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it ) QgsDebugMsgLevel( QString( "%1->%2" ).arg( it.key() ).arg( FID_TO_STRING( *it ) ), 4 ); } #endif Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() ); endRemoveRows(); return true; } void QgsAttributeTableModel::featureAdded( QgsFeatureId fid, bool resettingModel ) { QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 ); bool featOk = true; if ( mFeat.id() != fid ) featOk = loadFeatureAtId( fid ); if ( featOk && mFeatureRequest.acceptFeature( mFeat ) ) { if ( mSortFieldIndex >= 0 ) { QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( mSortFieldIndex ); const QVariant &widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex ); const QVariantMap &widgetConfig = mWidgetConfigs.at( mSortFieldIndex ); QVariant sortValue = fieldFormatter->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( mSortFieldIndex ) ); mSortCache.insert( mFeat.id(), sortValue ); } else if ( mSortCacheExpression.isValid() ) { mExpressionContext.setFeature( mFeat ); mSortCache[mFeat.id()] = mSortCacheExpression.evaluate( &mExpressionContext ); } // Skip if the fid is already in the map (do not add twice)! if ( ! mIdRowMap.contains( fid ) ) { int n = mRowIdMap.size(); if ( !resettingModel ) beginInsertRows( QModelIndex(), n, n ); mIdRowMap.insert( fid, n ); mRowIdMap.insert( n, fid ); if ( !resettingModel ) endInsertRows(); reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) ); } } } void QgsAttributeTableModel::updatedFields() { loadAttributes(); emit modelChanged(); } void QgsAttributeTableModel::editCommandEnded() { // do not do reload(...) due would trigger (dataChanged) row sort // giving issue: https://issues.qgis.org/issues/15976 mChangedCellBounds = QRect(); } void QgsAttributeTableModel::attributeDeleted( int idx ) { if ( mSortCacheAttributes.contains( idx ) ) prefetchSortData( QString() ); } void QgsAttributeTableModel::layerDeleted() { removeRows( 0, rowCount() ); mAttributeWidgetCaches.clear(); mAttributes.clear(); mWidgetFactories.clear(); mWidgetConfigs.clear(); mFieldFormatters.clear(); } void QgsAttributeTableModel::fieldFormatterRemoved( QgsFieldFormatter *fieldFormatter ) { for ( int i = 0; i < mFieldFormatters.size(); ++i ) { if ( mFieldFormatters.at( i ) == fieldFormatter ) mFieldFormatters[i] = QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter(); } } void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value ) { QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 ); if ( mSortCacheAttributes.contains( idx ) ) { if ( mSortFieldIndex == -1 ) { loadFeatureAtId( fid ); mExpressionContext.setFeature( mFeat ); mSortCache[fid] = mSortCacheExpression.evaluate( &mExpressionContext ); } else { QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( mSortFieldIndex ); const QVariant &widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex ); const QVariantMap &widgetConfig = mWidgetConfigs.at( mSortFieldIndex ); QVariant sortValue = fieldFormatter->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, value ); mSortCache.insert( fid, sortValue ); } } // No filter request: skip all possibly heavy checks if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone ) { if ( loadFeatureAtId( fid ) ) setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole ); } else { if ( loadFeatureAtId( fid ) ) { if ( mFeatureRequest.acceptFeature( mFeat ) ) { if ( !mIdRowMap.contains( fid ) ) { // Feature changed in such a way, it will be shown now featureAdded( fid ); } else { // Update representation setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole ); } } else { if ( mIdRowMap.contains( fid ) ) { // Feature changed such, that it is no longer shown featuresDeleted( QgsFeatureIds() << fid ); } // else: we don't care } } } } void QgsAttributeTableModel::loadAttributes() { if ( !layer() ) { return; } bool ins = false, rm = false; QgsAttributeList attributes; const QgsFields &fields = layer()->fields(); mWidgetFactories.clear(); mAttributeWidgetCaches.clear(); mWidgetConfigs.clear(); for ( int idx = 0; idx < fields.count(); ++idx ) { const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer(), fields[idx].name() ); QgsEditorWidgetFactory *widgetFactory = QgsGui::editorWidgetRegistry()->factory( setup.type() ); QgsFieldFormatter *fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() ); if ( widgetFactory ) { mWidgetFactories.append( widgetFactory ); mWidgetConfigs.append( setup.config() ); mAttributeWidgetCaches.append( fieldFormatter->createCache( layer(), idx, setup.config() ) ); mFieldFormatters.append( fieldFormatter ); attributes << idx; } } if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns ) { ins = true; beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 ); } else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns ) { rm = true; beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 ); } mFieldCount = attributes.size(); mAttributes = attributes; if ( mSortFieldIndex >= mAttributes.count() ) mSortFieldIndex = -1; if ( ins ) { endInsertColumns(); } else if ( rm ) { endRemoveColumns(); } } void QgsAttributeTableModel::loadLayer() { // make sure attributes are properly updated before caching the data // (emit of progress() signal may enter event loop and thus attribute // table view may be updated with inconsistent model which may assume // wrong number of attributes) loadAttributes(); beginResetModel(); if ( rowCount() != 0 ) { removeRows( 0, rowCount() ); } QgsFeatureIterator features = mLayerCache->getFeatures( mFeatureRequest ); int i = 0; QTime t; t.start(); while ( features.nextFeature( mFeat ) ) { ++i; if ( t.elapsed() > 1000 ) { bool cancel = false; emit progress( i, cancel ); if ( cancel ) break; t.restart(); } featureAdded( mFeat.id(), true ); } emit finished(); connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsAttributeTableModel::loadLayer, Qt::UniqueConnection ); endResetModel(); } void QgsAttributeTableModel::fieldConditionalStyleChanged( const QString &fieldName ) { if ( fieldName.isNull() ) { mRowStylesMap.clear(); emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) ); return; } int fieldIndex = mLayerCache->layer()->fields().lookupField( fieldName ); if ( fieldIndex == -1 ) return; //whole column has changed int col = fieldCol( fieldIndex ); emit dataChanged( index( 0, col ), index( rowCount() - 1, col ) ); } void QgsAttributeTableModel::swapRows( QgsFeatureId a, QgsFeatureId b ) { if ( a == b ) return; int rowA = idToRow( a ); int rowB = idToRow( b ); //emit layoutAboutToBeChanged(); mRowIdMap.remove( rowA ); mRowIdMap.remove( rowB ); mRowIdMap.insert( rowA, b ); mRowIdMap.insert( rowB, a ); mIdRowMap.remove( a ); mIdRowMap.remove( b ); mIdRowMap.insert( a, rowB ); mIdRowMap.insert( b, rowA ); Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() ); //emit layoutChanged(); } int QgsAttributeTableModel::idToRow( QgsFeatureId id ) const { if ( !mIdRowMap.contains( id ) ) { QgsDebugMsg( QString( "idToRow: id %1 not in the map" ).arg( id ) ); return -1; } return mIdRowMap[id]; } QModelIndex QgsAttributeTableModel::idToIndex( QgsFeatureId id ) const { return index( idToRow( id ), 0 ); } QModelIndexList QgsAttributeTableModel::idToIndexList( QgsFeatureId id ) const { QModelIndexList indexes; int row = idToRow( id ); int columns = columnCount(); indexes.reserve( columns ); for ( int column = 0; column < columns; ++column ) { indexes.append( index( row, column ) ); } return indexes; } QgsFeatureId QgsAttributeTableModel::rowToId( const int row ) const { if ( !mRowIdMap.contains( row ) ) { QgsDebugMsg( QString( "rowToId: row %1 not in the map" ).arg( row ) ); // return negative infinite (to avoid collision with newly added features) return std::numeric_limits<int>::min(); } return mRowIdMap[row]; } int QgsAttributeTableModel::fieldIdx( int col ) const { return mAttributes[col]; } int QgsAttributeTableModel::fieldCol( int idx ) const { return mAttributes.indexOf( idx ); } int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const { Q_UNUSED( parent ); return mRowIdMap.size(); } int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const { Q_UNUSED( parent ); return std::max( 1, mFieldCount + mExtraColumns ); // if there are zero columns all model indices will be considered invalid } QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( !layer() ) return QVariant(); if ( role == Qt::DisplayRole ) { if ( orientation == Qt::Vertical ) //row { return QVariant( section ); } else if ( section >= 0 && section < mFieldCount ) { QString attributeName = layer()->fields().at( mAttributes.at( section ) ).displayName(); return QVariant( attributeName ); } else { return tr( "extra column" ); } } else if ( role == Qt::ToolTipRole ) { if ( orientation == Qt::Vertical ) { // TODO show DisplayExpression return tr( "Feature ID: %1" ).arg( rowToId( section ) ); } else { QgsField field = layer()->fields().at( mAttributes.at( section ) ); return field.name(); } } else { return QVariant(); } } QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const { if ( !index.isValid() || ( role != Qt::TextAlignmentRole && role != Qt::DisplayRole && role != Qt::EditRole && role != SortRole && role != FeatureIdRole && role != FieldIndexRole && role != Qt::BackgroundColorRole && role != Qt::TextColorRole && role != Qt::DecorationRole && role != Qt::FontRole ) ) return QVariant(); QgsFeatureId rowId = rowToId( index.row() ); if ( role == FeatureIdRole ) return rowId; if ( index.column() >= mFieldCount ) return QVariant(); int fieldId = mAttributes.at( index.column() ); if ( role == FieldIndexRole ) return fieldId; if ( role == SortRole ) { return mSortCache[rowId]; } QgsField field = layer()->fields().at( fieldId ); if ( role == Qt::TextAlignmentRole ) { return QVariant( mFieldFormatters.at( index.column() )->alignmentFlag( layer(), fieldId, mWidgetConfigs.at( index.column() ) ) | Qt::AlignVCenter ); } if ( mFeat.id() != rowId || !mFeat.isValid() ) { if ( !loadFeatureAtId( rowId ) ) return QVariant( "ERROR" ); if ( mFeat.id() != rowId ) return QVariant( "ERROR" ); } QVariant val = mFeat.attribute( fieldId ); switch ( role ) { case Qt::DisplayRole: return mFieldFormatters.at( index.column() )->representValue( layer(), fieldId, mWidgetConfigs.at( index.column() ), mAttributeWidgetCaches.at( index.column() ), val ); case Qt::EditRole: return val; case Qt::BackgroundColorRole: case Qt::TextColorRole: case Qt::DecorationRole: case Qt::FontRole: { mExpressionContext.setFeature( mFeat ); QList<QgsConditionalStyle> styles; if ( mRowStylesMap.contains( index.row() ) ) { styles = mRowStylesMap[index.row()]; } else { styles = QgsConditionalStyle::matchingConditionalStyles( layer()->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext ); mRowStylesMap.insert( index.row(), styles ); } QgsConditionalStyle rowstyle = QgsConditionalStyle::compressStyles( styles ); styles = layer()->conditionalStyles()->fieldStyles( field.name() ); styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, mExpressionContext ); styles.insert( 0, rowstyle ); QgsConditionalStyle style = QgsConditionalStyle::compressStyles( styles ); if ( style.isValid() ) { if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() ) return style.backgroundColor(); if ( role == Qt::TextColorRole && style.validTextColor() ) return style.textColor(); if ( role == Qt::DecorationRole ) return style.icon(); if ( role == Qt::FontRole ) return style.font(); } return QVariant(); } } return QVariant(); } bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role ) { Q_UNUSED( value ) if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !layer()->isEditable() ) return false; if ( !layer()->isModified() ) return false; if ( mChangedCellBounds.isNull() ) { mChangedCellBounds = QRect( index.column(), index.row(), 1, 1 ); } else { if ( index.column() < mChangedCellBounds.left() ) { mChangedCellBounds.setLeft( index.column() ); } if ( index.row() < mChangedCellBounds.top() ) { mChangedCellBounds.setTop( index.row() ); } if ( index.column() > mChangedCellBounds.right() ) { mChangedCellBounds.setRight( index.column() ); } if ( index.row() > mChangedCellBounds.bottom() ) { mChangedCellBounds.setBottom( index.row() ); } } return true; } Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const { if ( !index.isValid() ) return Qt::ItemIsEnabled; if ( index.column() >= mFieldCount ) return Qt::NoItemFlags; Qt::ItemFlags flags = QAbstractItemModel::flags( index ); bool editable = false; const int fieldIndex = mAttributes[index.column()]; const QgsFeatureId fid = rowToId( index.row() ); if ( layer()->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin ) { int srcFieldIndex; const QgsVectorLayerJoinInfo *info = layer()->joinBuffer()->joinForFieldIndex( fieldIndex, layer()->fields(), srcFieldIndex ); if ( info && info->isEditable() ) editable = fieldIsEditable( *info->joinLayer(), srcFieldIndex, fid ); } else editable = fieldIsEditable( *layer(), fieldIndex, fid ); if ( editable ) flags |= Qt::ItemIsEditable; return flags; } bool QgsAttributeTableModel::fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const { return ( layer.isEditable() && !layer.editFormConfig().readOnly( fieldIndex ) && ( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) ) ); } void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 ) { mFeat.setId( std::numeric_limits<int>::min() ); emit dataChanged( index1, index2 ); } void QgsAttributeTableModel::executeAction( const QUuid &action, const QModelIndex &idx ) const { QgsFeature f = feature( idx ); layer()->actions()->doAction( action, f, fieldIdx( idx.column() ) ); } void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction *action, const QModelIndex &idx ) const { QgsFeature f = feature( idx ); action->triggerForFeature( layer(), &f ); } QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const { QgsFeature f; f.initAttributes( mAttributes.size() ); f.setId( rowToId( idx.row() ) ); for ( int i = 0; i < mAttributes.size(); i++ ) { f.setAttribute( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) ); } return f; } void QgsAttributeTableModel::prefetchColumnData( int column ) { if ( column == -1 || column >= mAttributes.count() ) { prefetchSortData( QString() ); } else { prefetchSortData( QgsExpression::quotedColumnRef( mLayerCache->layer()->fields().at( mAttributes.at( column ) ).name() ) ); } } void QgsAttributeTableModel::prefetchSortData( const QString &expressionString ) { mSortCache.clear(); mSortCacheAttributes.clear(); mSortFieldIndex = -1; if ( !expressionString.isEmpty() ) mSortCacheExpression = QgsExpression( expressionString ); else { // no sorting mSortCacheExpression = QgsExpression(); return; } QgsFieldFormatter *fieldFormatter = nullptr; QVariant widgetCache; QVariantMap widgetConfig; if ( mSortCacheExpression.isField() ) { QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( mSortCacheExpression.rootNode() )->name(); mSortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName ); } if ( mSortFieldIndex == -1 ) { mSortCacheExpression.prepare( &mExpressionContext ); Q_FOREACH ( const QString &col, mSortCacheExpression.referencedColumns() ) { mSortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) ); } } else { mSortCacheAttributes.append( mSortFieldIndex ); widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex ); widgetConfig = mWidgetConfigs.at( mSortFieldIndex ); fieldFormatter = mFieldFormatters.at( mSortFieldIndex ); } QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest ) .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( mSortCacheAttributes ); QgsFeatureIterator it = mLayerCache->getFeatures( request ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( mSortFieldIndex == -1 ) { mExpressionContext.setFeature( f ); mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) ); } else { QVariant sortValue = fieldFormatter->sortValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, f.attribute( mSortFieldIndex ) ); mSortCache.insert( f.id(), sortValue ); } } } QString QgsAttributeTableModel::sortCacheExpression() const { if ( mSortCacheExpression.isValid() ) return mSortCacheExpression.expression(); else return QString(); } void QgsAttributeTableModel::setRequest( const QgsFeatureRequest &request ) { mFeatureRequest = request; if ( layer() && !layer()->isSpatial() ) mFeatureRequest.setFlags( mFeatureRequest.flags() | QgsFeatureRequest::NoGeometry ); } const QgsFeatureRequest &QgsAttributeTableModel::request() const { return mFeatureRequest; }