Skip to content
Permalink
Browse files

Allow prefetching more than a single sort expression

in the attribute table model. One is used for the table, the other one for the feature list in the form view.
  • Loading branch information
m-kuhn committed May 4, 2018
1 parent 487d8d1 commit 2544391a9a2c32c7203ebd5a9441b6fc7e4df262
@@ -28,10 +28,11 @@ Is mostly referred to as "master model" within this doc and the source.
public:
enum Role
{
SortRole,
FeatureIdRole,
FieldIndexRole,
UserRole
UserRole,
// Insert
SortRole,
};

public:
@@ -190,7 +191,7 @@ Specify -1 as column to invalidate the cache
:param column: The column index of the field to catch
%End

void prefetchSortData( const QString &expression );
void prefetchSortData( const QString &expression, unsigned long cacheIndex = 0 );
%Docstring
Prefetches the entire data for one expression. Based on this cached information
the sorting can later be done in a performant way.
@@ -46,9 +46,6 @@
QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache, QObject *parent )
: QAbstractTableModel( parent )
, mLayerCache( layerCache )
, mFieldCount( 0 )
, mSortFieldIndex( -1 )
, mExtraColumns( 0 )
{
mExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layerCache->layer() ) );

@@ -168,7 +165,8 @@ bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &
// clean old references
for ( int i = row; i < row + count; i++ )
{
mSortCache.remove( mRowIdMap[i] );
for ( SortCache &cache : mSortCaches )
cache.sortCache.remove( mRowIdMap[i] );
mIdRowMap.remove( mRowIdMap[i] );
mRowIdMap.remove( i );
}
@@ -214,18 +212,21 @@ void QgsAttributeTableModel::featureAdded( QgsFeatureId fid, bool resettingModel

if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
{
if ( mSortFieldIndex >= 0 )
for ( SortCache &cache : mSortCaches )
{
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 );
if ( cache.sortFieldIndex >= 0 )
{
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( cache.sortFieldIndex ) );
cache.sortCache.insert( mFeat.id(), sortValue );
}
else if ( cache.sortCacheExpression.isValid() )
{
mExpressionContext.setFeature( mFeat );
cache.sortCache[mFeat.id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
}
}

// Skip if the fid is already in the map (do not add twice)!
@@ -258,8 +259,15 @@ void QgsAttributeTableModel::editCommandEnded()

void QgsAttributeTableModel::attributeDeleted( int idx )
{
if ( mSortCacheAttributes.contains( idx ) )
prefetchSortData( QString() );
int cacheIndex = 0;
for ( const SortCache &cache : mSortCaches )
{
if ( cache.sortCacheAttributes.contains( idx ) )
{
prefetchSortData( QString(), cacheIndex );
}
cacheIndex++;
}
}

void QgsAttributeTableModel::layerDeleted()
@@ -287,23 +295,26 @@ void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, c
{
QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );

if ( mSortCacheAttributes.contains( idx ) )
for ( SortCache &cache : mSortCaches )
{
if ( mSortFieldIndex == -1 )
if ( cache.sortCacheAttributes.contains( idx ) )
{
if ( loadFeatureAtId( fid ) )
if ( cache.sortFieldIndex == -1 )
{
mExpressionContext.setFeature( mFeat );
mSortCache[fid] = mSortCacheExpression.evaluate( &mExpressionContext );
if ( loadFeatureAtId( fid ) )
{
mExpressionContext.setFeature( mFeat );
cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
}
}
else
{
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, value );
cache.sortCache.insert( fid, sortValue );
}
}
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
@@ -389,8 +400,11 @@ void QgsAttributeTableModel::loadAttributes()
mFieldCount = attributes.size();
mAttributes = attributes;

if ( mSortFieldIndex >= mAttributes.count() )
mSortFieldIndex = -1;
for ( SortCache &cache : mSortCaches )
{
if ( cache.sortFieldIndex >= mAttributes.count() )
cache.sortFieldIndex = -1;
}

if ( ins )
{
@@ -626,9 +640,13 @@ QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) cons
if ( role == FieldIndexRole )
return fieldId;

if ( role == SortRole )
if ( role >= SortRole )
{
return mSortCache[rowId];
unsigned long cacheIndex = role - SortRole;
if ( cacheIndex < mSortCaches.size() )
return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
else
return QVariant();
}

QgsField field = layer()->fields().at( fieldId );
@@ -819,75 +837,90 @@ void QgsAttributeTableModel::prefetchColumnData( int column )
}
}

void QgsAttributeTableModel::prefetchSortData( const QString &expressionString )
void QgsAttributeTableModel::prefetchSortData( const QString &expressionString, unsigned long cacheIndex )
{
mSortCache.clear();
mSortCacheAttributes.clear();
mSortFieldIndex = -1;
if ( cacheIndex >= mSortCaches.size() )
{
mSortCaches.resize( cacheIndex + 1 );
}
SortCache &cache = mSortCaches[cacheIndex];
cache.sortCache.clear();
cache.sortCacheAttributes.clear();
cache.sortFieldIndex = -1;
if ( !expressionString.isEmpty() )
mSortCacheExpression = QgsExpression( expressionString );
cache.sortCacheExpression = QgsExpression( expressionString );
else
{
// no sorting
mSortCacheExpression = QgsExpression();
cache.sortCacheExpression = QgsExpression();
return;
}

QgsFieldFormatter *fieldFormatter = nullptr;
QVariant widgetCache;
QVariantMap widgetConfig;

if ( mSortCacheExpression.isField() )
if ( cache.sortCacheExpression.isField() )
{
QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( mSortCacheExpression.rootNode() )->name();
mSortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( cache.sortCacheExpression.rootNode() )->name();
cache.sortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
}

if ( mSortFieldIndex == -1 )
if ( cache.sortFieldIndex == -1 )
{
mSortCacheExpression.prepare( &mExpressionContext );
cache.sortCacheExpression.prepare( &mExpressionContext );

const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();

Q_FOREACH ( const QString &col, mSortCacheExpression.referencedColumns() )
for ( const QString &col : referencedColumns )
{
mSortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) );
cache.sortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) );
}
}
else
{
mSortCacheAttributes.append( mSortFieldIndex );
cache.sortCacheAttributes.append( cache.sortFieldIndex );

widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
fieldFormatter = mFieldFormatters.at( mSortFieldIndex );
widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
}

QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
.setFlags( QgsFeatureRequest::NoGeometry )
.setSubsetOfAttributes( mSortCacheAttributes );
.setSubsetOfAttributes( cache.sortCacheAttributes );
QgsFeatureIterator it = mLayerCache->getFeatures( request );

QgsFeature f;
while ( it.nextFeature( f ) )
{
if ( mSortFieldIndex == -1 )
if ( cache.sortFieldIndex == -1 )
{
mExpressionContext.setFeature( f );
mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
cache.sortCache.insert( f.id(), cache.sortCacheExpression.evaluate( &mExpressionContext ) );
}
else
{
QVariant sortValue = fieldFormatter->sortValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, f.attribute( mSortFieldIndex ) );
mSortCache.insert( f.id(), sortValue );
QVariant sortValue = fieldFormatter->sortValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, f.attribute( cache.sortFieldIndex ) );
cache.sortCache.insert( f.id(), sortValue );
}
}
}

QString QgsAttributeTableModel::sortCacheExpression() const
{
if ( mSortCacheExpression.isValid() )
return mSortCacheExpression.expression();
Q_ASSERT( !mSortCaches.empty() );

QString expressionString;

const QgsExpression &expression = mSortCaches.begin()->sortCacheExpression;

if ( expression.isValid() )
expressionString = expression.expression();
else
return QString();
expressionString = QString();

return expressionString;
}

void QgsAttributeTableModel::setRequest( const QgsFeatureRequest &request )
@@ -54,10 +54,11 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
public:
enum Role
{
SortRole = Qt::UserRole + 1, //!< Role used for sorting
FeatureIdRole, //!< Get the feature id of the feature in this row
FieldIndexRole, //!< Get the field index of this column
UserRole //!< Start further roles starting from this role
FeatureIdRole = Qt::UserRole, //!< Get the feature id of the feature in this row
FieldIndexRole, //!< Get the field index of this column
UserRole, //!< Start further roles starting from this role
// Insert new values here, SortRole needs to be the last one
SortRole, //!< Roles used for sorting start here
};

public:
@@ -203,7 +204,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
*
* \param expression The expression to cache
*/
void prefetchSortData( const QString &expression );
void prefetchSortData( const QString &expression, unsigned long cacheIndex = 0 );

/**
* The expression which was used to fill the sorting cache
@@ -329,7 +330,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel

private:
QgsVectorLayerCache *mLayerCache = nullptr;
int mFieldCount;
int mFieldCount = 0;

mutable QgsFeature mFeat;

@@ -363,13 +364,18 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel

QgsFeatureRequest mFeatureRequest;

//! The currently cached column
QgsExpression mSortCacheExpression;
QgsAttributeList mSortCacheAttributes;
//! If it is set, a simple field is used for sorting, if it's -1 it's the mSortCacheExpression
int mSortFieldIndex;
//! Allows caching of one value per column (used for sorting)
QHash<QgsFeatureId, QVariant> mSortCache;
struct SortCache
{
//! If it is set, a simple field is used for sorting, if it's -1 it's the mSortCacheExpression
int sortFieldIndex;
//! The currently cached column
QgsExpression sortCacheExpression;
QgsAttributeList sortCacheAttributes;
//! Allows caching of one value per column (used for sorting)
QHash<QgsFeatureId, QVariant> sortCache;
};

std::vector<SortCache> mSortCaches;

/**
* Holds the bounds of changed cells while an update operation is running
@@ -382,7 +388,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel

QgsAttributeEditorContext mEditorContext;

int mExtraColumns;
int mExtraColumns = 0;

friend class TestQgsAttributeTable;

0 comments on commit 2544391

Please sign in to comment.
You can’t perform that action at this time.