Skip to content

Commit 2544391

Browse files
committed
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.
1 parent 487d8d1 commit 2544391

File tree

3 files changed

+116
-76
lines changed

3 files changed

+116
-76
lines changed

python/gui/auto_generated/attributetable/qgsattributetablemodel.sip.in

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ Is mostly referred to as "master model" within this doc and the source.
2828
public:
2929
enum Role
3030
{
31-
SortRole,
3231
FeatureIdRole,
3332
FieldIndexRole,
34-
UserRole
33+
UserRole,
34+
// Insert
35+
SortRole,
3536
};
3637

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

193-
void prefetchSortData( const QString &expression );
194+
void prefetchSortData( const QString &expression, unsigned long cacheIndex = 0 );
194195
%Docstring
195196
Prefetches the entire data for one expression. Based on this cached information
196197
the sorting can later be done in a performant way.

src/gui/attributetable/qgsattributetablemodel.cpp

Lines changed: 92 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@
4646
QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache, QObject *parent )
4747
: QAbstractTableModel( parent )
4848
, mLayerCache( layerCache )
49-
, mFieldCount( 0 )
50-
, mSortFieldIndex( -1 )
51-
, mExtraColumns( 0 )
5249
{
5350
mExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layerCache->layer() ) );
5451

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

215213
if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
216214
{
217-
if ( mSortFieldIndex >= 0 )
215+
for ( SortCache &cache : mSortCaches )
218216
{
219-
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( mSortFieldIndex );
220-
const QVariant &widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
221-
const QVariantMap &widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
222-
QVariant sortValue = fieldFormatter->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( mSortFieldIndex ) );
223-
mSortCache.insert( mFeat.id(), sortValue );
224-
}
225-
else if ( mSortCacheExpression.isValid() )
226-
{
227-
mExpressionContext.setFeature( mFeat );
228-
mSortCache[mFeat.id()] = mSortCacheExpression.evaluate( &mExpressionContext );
217+
if ( cache.sortFieldIndex >= 0 )
218+
{
219+
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
220+
const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
221+
const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
222+
QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( cache.sortFieldIndex ) );
223+
cache.sortCache.insert( mFeat.id(), sortValue );
224+
}
225+
else if ( cache.sortCacheExpression.isValid() )
226+
{
227+
mExpressionContext.setFeature( mFeat );
228+
cache.sortCache[mFeat.id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
229+
}
229230
}
230231

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

259260
void QgsAttributeTableModel::attributeDeleted( int idx )
260261
{
261-
if ( mSortCacheAttributes.contains( idx ) )
262-
prefetchSortData( QString() );
262+
int cacheIndex = 0;
263+
for ( const SortCache &cache : mSortCaches )
264+
{
265+
if ( cache.sortCacheAttributes.contains( idx ) )
266+
{
267+
prefetchSortData( QString(), cacheIndex );
268+
}
269+
cacheIndex++;
270+
}
263271
}
264272

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

290-
if ( mSortCacheAttributes.contains( idx ) )
298+
for ( SortCache &cache : mSortCaches )
291299
{
292-
if ( mSortFieldIndex == -1 )
300+
if ( cache.sortCacheAttributes.contains( idx ) )
293301
{
294-
if ( loadFeatureAtId( fid ) )
302+
if ( cache.sortFieldIndex == -1 )
295303
{
296-
mExpressionContext.setFeature( mFeat );
297-
mSortCache[fid] = mSortCacheExpression.evaluate( &mExpressionContext );
304+
if ( loadFeatureAtId( fid ) )
305+
{
306+
mExpressionContext.setFeature( mFeat );
307+
cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
308+
}
309+
}
310+
else
311+
{
312+
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
313+
const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
314+
const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
315+
QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, value );
316+
cache.sortCache.insert( fid, sortValue );
298317
}
299-
}
300-
else
301-
{
302-
QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( mSortFieldIndex );
303-
const QVariant &widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
304-
const QVariantMap &widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
305-
QVariant sortValue = fieldFormatter->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, value );
306-
mSortCache.insert( fid, sortValue );
307318
}
308319
}
309320
// No filter request: skip all possibly heavy checks
@@ -389,8 +400,11 @@ void QgsAttributeTableModel::loadAttributes()
389400
mFieldCount = attributes.size();
390401
mAttributes = attributes;
391402

392-
if ( mSortFieldIndex >= mAttributes.count() )
393-
mSortFieldIndex = -1;
403+
for ( SortCache &cache : mSortCaches )
404+
{
405+
if ( cache.sortFieldIndex >= mAttributes.count() )
406+
cache.sortFieldIndex = -1;
407+
}
394408

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

629-
if ( role == SortRole )
643+
if ( role >= SortRole )
630644
{
631-
return mSortCache[rowId];
645+
unsigned long cacheIndex = role - SortRole;
646+
if ( cacheIndex < mSortCaches.size() )
647+
return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
648+
else
649+
return QVariant();
632650
}
633651

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

822-
void QgsAttributeTableModel::prefetchSortData( const QString &expressionString )
840+
void QgsAttributeTableModel::prefetchSortData( const QString &expressionString, unsigned long cacheIndex )
823841
{
824-
mSortCache.clear();
825-
mSortCacheAttributes.clear();
826-
mSortFieldIndex = -1;
842+
if ( cacheIndex >= mSortCaches.size() )
843+
{
844+
mSortCaches.resize( cacheIndex + 1 );
845+
}
846+
SortCache &cache = mSortCaches[cacheIndex];
847+
cache.sortCache.clear();
848+
cache.sortCacheAttributes.clear();
849+
cache.sortFieldIndex = -1;
827850
if ( !expressionString.isEmpty() )
828-
mSortCacheExpression = QgsExpression( expressionString );
851+
cache.sortCacheExpression = QgsExpression( expressionString );
829852
else
830853
{
831854
// no sorting
832-
mSortCacheExpression = QgsExpression();
855+
cache.sortCacheExpression = QgsExpression();
833856
return;
834857
}
835858

836859
QgsFieldFormatter *fieldFormatter = nullptr;
837860
QVariant widgetCache;
838861
QVariantMap widgetConfig;
839862

840-
if ( mSortCacheExpression.isField() )
863+
if ( cache.sortCacheExpression.isField() )
841864
{
842-
QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( mSortCacheExpression.rootNode() )->name();
843-
mSortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
865+
QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( cache.sortCacheExpression.rootNode() )->name();
866+
cache.sortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
844867
}
845868

846-
if ( mSortFieldIndex == -1 )
869+
if ( cache.sortFieldIndex == -1 )
847870
{
848-
mSortCacheExpression.prepare( &mExpressionContext );
871+
cache.sortCacheExpression.prepare( &mExpressionContext );
872+
873+
const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();
849874

850-
Q_FOREACH ( const QString &col, mSortCacheExpression.referencedColumns() )
875+
for ( const QString &col : referencedColumns )
851876
{
852-
mSortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) );
877+
cache.sortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) );
853878
}
854879
}
855880
else
856881
{
857-
mSortCacheAttributes.append( mSortFieldIndex );
882+
cache.sortCacheAttributes.append( cache.sortFieldIndex );
858883

859-
widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
860-
widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
861-
fieldFormatter = mFieldFormatters.at( mSortFieldIndex );
884+
widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
885+
widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
886+
fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
862887
}
863888

864889
QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
865890
.setFlags( QgsFeatureRequest::NoGeometry )
866-
.setSubsetOfAttributes( mSortCacheAttributes );
891+
.setSubsetOfAttributes( cache.sortCacheAttributes );
867892
QgsFeatureIterator it = mLayerCache->getFeatures( request );
868893

869894
QgsFeature f;
870895
while ( it.nextFeature( f ) )
871896
{
872-
if ( mSortFieldIndex == -1 )
897+
if ( cache.sortFieldIndex == -1 )
873898
{
874899
mExpressionContext.setFeature( f );
875-
mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
900+
cache.sortCache.insert( f.id(), cache.sortCacheExpression.evaluate( &mExpressionContext ) );
876901
}
877902
else
878903
{
879-
QVariant sortValue = fieldFormatter->sortValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, f.attribute( mSortFieldIndex ) );
880-
mSortCache.insert( f.id(), sortValue );
904+
QVariant sortValue = fieldFormatter->sortValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, f.attribute( cache.sortFieldIndex ) );
905+
cache.sortCache.insert( f.id(), sortValue );
881906
}
882907
}
883908
}
884909

885910
QString QgsAttributeTableModel::sortCacheExpression() const
886911
{
887-
if ( mSortCacheExpression.isValid() )
888-
return mSortCacheExpression.expression();
912+
Q_ASSERT( !mSortCaches.empty() );
913+
914+
QString expressionString;
915+
916+
const QgsExpression &expression = mSortCaches.begin()->sortCacheExpression;
917+
918+
if ( expression.isValid() )
919+
expressionString = expression.expression();
889920
else
890-
return QString();
921+
expressionString = QString();
922+
923+
return expressionString;
891924
}
892925

893926
void QgsAttributeTableModel::setRequest( const QgsFeatureRequest &request )

src/gui/attributetable/qgsattributetablemodel.h

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
5454
public:
5555
enum Role
5656
{
57-
SortRole = Qt::UserRole + 1, //!< Role used for sorting
58-
FeatureIdRole, //!< Get the feature id of the feature in this row
59-
FieldIndexRole, //!< Get the field index of this column
60-
UserRole //!< Start further roles starting from this role
57+
FeatureIdRole = Qt::UserRole, //!< Get the feature id of the feature in this row
58+
FieldIndexRole, //!< Get the field index of this column
59+
UserRole, //!< Start further roles starting from this role
60+
// Insert new values here, SortRole needs to be the last one
61+
SortRole, //!< Roles used for sorting start here
6162
};
6263

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

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

330331
private:
331332
QgsVectorLayerCache *mLayerCache = nullptr;
332-
int mFieldCount;
333+
int mFieldCount = 0;
333334

334335
mutable QgsFeature mFeat;
335336

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

364365
QgsFeatureRequest mFeatureRequest;
365366

366-
//! The currently cached column
367-
QgsExpression mSortCacheExpression;
368-
QgsAttributeList mSortCacheAttributes;
369-
//! If it is set, a simple field is used for sorting, if it's -1 it's the mSortCacheExpression
370-
int mSortFieldIndex;
371-
//! Allows caching of one value per column (used for sorting)
372-
QHash<QgsFeatureId, QVariant> mSortCache;
367+
struct SortCache
368+
{
369+
//! If it is set, a simple field is used for sorting, if it's -1 it's the mSortCacheExpression
370+
int sortFieldIndex;
371+
//! The currently cached column
372+
QgsExpression sortCacheExpression;
373+
QgsAttributeList sortCacheAttributes;
374+
//! Allows caching of one value per column (used for sorting)
375+
QHash<QgsFeatureId, QVariant> sortCache;
376+
};
377+
378+
std::vector<SortCache> mSortCaches;
373379

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

383389
QgsAttributeEditorContext mEditorContext;
384390

385-
int mExtraColumns;
391+
int mExtraColumns = 0;
386392

387393
friend class TestQgsAttributeTable;
388394

0 commit comments

Comments
 (0)