Skip to content

Commit 4010e79

Browse files
authoredOct 18, 2018
Merge pull request #8200 from elpaso/bugfix-20094-followup-096b4ce
Slow field calculator - Bugfix 20094 followup 096b4ce
2 parents 009fcd8 + 7e9c874 commit 4010e79

File tree

10 files changed

+84
-95
lines changed

10 files changed

+84
-95
lines changed
 

‎python/core/auto_generated/qgsapplication.sip.in

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,6 @@ Emits the signal to collect all the strings of .qgs to be included in ts file
824824
.. versionadded:: 3.4
825825
%End
826826

827-
828827
%If (ANDROID)
829828
//dummy method to workaround sip generation issue
830829
bool x11EventFilter( XEvent *event );
@@ -853,7 +852,6 @@ In order to register translatable strings, connect to this signal and register t
853852
.. versionadded:: 3.4
854853
%End
855854

856-
857855
};
858856

859857

‎src/app/qgisapp.cpp

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7118,14 +7118,13 @@ void QgisApp::fieldCalculator()
71187118

71197119
void QgisApp::attributeTable( QgsAttributeTableFilterModel::FilterMode filter )
71207120
{
7121-
QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( activeLayer() );
7122-
if ( !vectorLayer )
7121+
QgsVectorLayer *myLayer = qobject_cast<QgsVectorLayer *>( activeLayer() );
7122+
if ( !myLayer )
71237123
{
71247124
return;
71257125
}
71267126

7127-
QgsAttributeTableDialog *mDialog = new QgsAttributeTableDialog( vectorLayer, filter );
7128-
7127+
QgsAttributeTableDialog *mDialog = new QgsAttributeTableDialog( myLayer, filter );
71297128
mDialog->show();
71307129
// the dialog will be deleted by itself on close
71317130
}
@@ -9217,7 +9216,6 @@ bool QgisApp::toggleEditing( QgsMapLayer *layer, bool allowCancel )
92179216
return res;
92189217
}
92199218

9220-
92219219
void QgisApp::saveActiveLayerEdits()
92229220
{
92239221
saveEdits( activeLayer(), true, true );
@@ -14175,8 +14173,3 @@ void QgisApp::triggerCrashHandler()
1417514173
RaiseException( 0x12345678, 0, 0, nullptr );
1417614174
#endif
1417714175
}
14178-
14179-
void QgisApp::blockAttributeTableUpdates( const QgsVectorLayer *layer, const bool blocked )
14180-
{
14181-
emit attributeTableUpdateBlocked( layer, blocked );
14182-
}

‎src/app/qgisapp.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,13 +1012,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
10121012
*/
10131013
void triggerCrashHandler();
10141014

1015-
/**
1016-
* Emits the signal to set the \a blocked state of attribute tables connected a particular \a layer
1017-
*
1018-
* \since QGIS 3.4
1019-
*/
1020-
void blockAttributeTableUpdates( const QgsVectorLayer *layer, const bool blocked );
1021-
10221015
protected:
10231016

10241017
//! Handle state changes (WindowTitleChange)
@@ -1747,13 +1740,6 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
17471740
*/
17481741
void activeLayerChanged( QgsMapLayer *layer );
17491742

1750-
/**
1751-
* Emitted when \a blocked status of attribute table updates for a particular \a layer must change
1752-
*
1753-
* \since QGIS 3.4
1754-
*/
1755-
void attributeTableUpdateBlocked( const QgsVectorLayer *layer, const bool blocked );
1756-
17571743
private:
17581744
void startProfile( const QString &name );
17591745
void endProfile();

‎src/app/qgsattributetabledialog.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,6 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *layer, QgsAttr
113113
connect( mActionExpressionSelect, &QAction::triggered, this, &QgsAttributeTableDialog::mActionExpressionSelect_triggered );
114114
connect( mMainView, &QgsDualView::showContextMenuExternally, this, &QgsAttributeTableDialog::showContextMenu );
115115

116-
// Block/unblock table updates (feature cache signals)
117-
connect( QgisApp::instance(), &QgisApp::attributeTableUpdateBlocked, this, [ = ]( const QgsVectorLayer * layer, const bool blocked )
118-
{
119-
if ( layer == mLayer )
120-
this->blockCacheUpdateSignals( blocked );
121-
} );
122-
// Massive rollbacks can also freeze the GUI due to the feature cache signals
123-
connect( mLayer, &QgsVectorLayer::beforeRollBack, this, [ = ] { this->blockCacheUpdateSignals( true ); } );
124-
connect( mLayer, &QgsVectorLayer::afterRollBack, this, [ = ] { this->blockCacheUpdateSignals( false ); } );
125-
126116
const QgsFields fields = mLayer->fields();
127117
for ( const QgsField &field : fields )
128118
{
@@ -743,6 +733,7 @@ void QgsAttributeTableDialog::mActionOpenFieldCalculator_triggered()
743733
if ( calc.exec() == QDialog::Accepted )
744734
{
745735
int col = masterModel->fieldCol( calc.changedAttributeId() );
736+
746737
if ( col >= 0 )
747738
{
748739
masterModel->reload( masterModel->index( 0, col ), masterModel->index( masterModel->rowCount() - 1, col ) );
@@ -1154,15 +1145,6 @@ void QgsAttributeTableDialog::setFilterExpression( const QString &filterString,
11541145
mMainView->setFilterMode( QgsAttributeTableFilterModel::ShowFilteredList );
11551146
}
11561147

1157-
void QgsAttributeTableDialog::blockCacheUpdateSignals( const bool block )
1158-
{
1159-
QgsAttributeTableModel *masterModel = mMainView->masterModel();
1160-
1161-
if ( ! masterModel )
1162-
return;
1163-
1164-
masterModel->layerCache()->blockSignals( block );
1165-
}
11661148

11671149
void QgsAttributeTableDialog::deleteFeature( const QgsFeatureId fid )
11681150
{

‎src/app/qgsattributetabledialog.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ class APP_EXPORT QgsAttributeTableDialog : public QDialog, private Ui::QgsAttrib
242242

243243
void updateMultiEditButtonState();
244244
void deleteFeature( QgsFeatureId fid );
245-
void blockCacheUpdateSignals( const bool block );
246245

247246
friend class TestQgsAttributeTable;
248247
};

‎src/app/qgsfieldcalculator.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,8 @@ void QgsFieldCalculator::accept()
159159
{
160160
builder->saveToRecent( QStringLiteral( "fieldcalc" ) );
161161

162-
if ( ! mVectorLayer )
163-
{
162+
if ( !mVectorLayer )
164163
return;
165-
}
166164

167165
// Set up QgsDistanceArea each time we (re-)calculate
168166
QgsDistanceArea myDa;
@@ -262,10 +260,6 @@ void QgsFieldCalculator::accept()
262260
return;
263261
}
264262

265-
// Begin feature modifications, block updates for attr tables
266-
// connected to this layer
267-
QgisApp::instance()->blockAttributeTableUpdates( mVectorLayer, true );
268-
269263
//go through all the features and change the new attribute
270264
QgsFeature feature;
271265
bool calculationSuccess = true;
@@ -326,8 +320,6 @@ void QgsFieldCalculator::accept()
326320
rownum++;
327321
}
328322

329-
QgisApp::instance()->blockAttributeTableUpdates( mVectorLayer, false );
330-
331323
if ( !calculationSuccess )
332324
{
333325
cursorOverride.release();
@@ -336,6 +328,7 @@ void QgsFieldCalculator::accept()
336328
mVectorLayer->destroyEditCommand();
337329
return;
338330
}
331+
339332
mVectorLayer->endEditCommand();
340333
}
341334
QDialog::accept();

‎src/core/qgsapplication.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1528,7 +1528,6 @@ void QgsApplication::collectTranslatableObjects( QgsTranslationContext *translat
15281528
emit requestForTranslatableObjects( translationContext );
15291529
}
15301530

1531-
15321531
QString QgsApplication::nullRepresentation()
15331532
{
15341533
ApplicationMembers *appMembers = members();

‎src/core/qgsapplication.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,6 @@ class CORE_EXPORT QgsApplication : public QApplication
757757
*/
758758
void collectTranslatableObjects( QgsTranslationContext *translationContext );
759759

760-
761760
#ifdef SIP_RUN
762761
SIP_IF_FEATURE( ANDROID )
763762
//dummy method to workaround sip generation issue
@@ -789,7 +788,6 @@ class CORE_EXPORT QgsApplication : public QApplication
789788
*/
790789
void requestForTranslatableObjects( QgsTranslationContext *translationContext );
791790

792-
793791
private:
794792

795793
static void copyPath( const QString &src, const QString &dst );

‎src/gui/attributetable/qgsattributetablemodel.cpp

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,19 @@ QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache,
6262

6363
loadAttributes();
6464

65-
connect( mLayerCache, &QgsVectorLayerCache::attributeValueChanged, this, &QgsAttributeTableModel::attributeValueChanged );
6665
connect( layer(), &QgsVectorLayer::featuresDeleted, this, &QgsAttributeTableModel::featuresDeleted );
6766
connect( layer(), &QgsVectorLayer::attributeDeleted, this, &QgsAttributeTableModel::attributeDeleted );
6867
connect( layer(), &QgsVectorLayer::updatedFields, this, &QgsAttributeTableModel::updatedFields );
68+
69+
connect( layer(), &QgsVectorLayer::editCommandStarted, this, &QgsAttributeTableModel::bulkEditCommandStarted );
70+
connect( layer(), &QgsVectorLayer::beforeRollBack, this, &QgsAttributeTableModel::bulkEditCommandStarted );
71+
connect( layer(), &QgsVectorLayer::afterRollBack, this, &QgsAttributeTableModel::bulkEditCommandEnded );
72+
6973
connect( layer(), &QgsVectorLayer::editCommandEnded, this, &QgsAttributeTableModel::editCommandEnded );
74+
connect( mLayerCache, &QgsVectorLayerCache::attributeValueChanged, this, &QgsAttributeTableModel::attributeValueChanged );
7075
connect( mLayerCache, &QgsVectorLayerCache::featureAdded, this, [ = ]( QgsFeatureId id ) { featureAdded( id ); } );
7176
connect( mLayerCache, &QgsVectorLayerCache::cachedLayerDeleted, this, &QgsAttributeTableModel::layerDeleted );
77+
7278
}
7379

7480
bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid ) const
@@ -255,7 +261,7 @@ void QgsAttributeTableModel::editCommandEnded()
255261
{
256262
// do not do reload(...) due would trigger (dataChanged) row sort
257263
// giving issue: https://issues.qgis.org/issues/15976
258-
mChangedCellBounds = QRect();
264+
bulkEditCommandEnded( );
259265
}
260266

261267
void QgsAttributeTableModel::attributeDeleted( int idx )
@@ -294,7 +300,13 @@ void QgsAttributeTableModel::fieldFormatterRemoved( QgsFieldFormatter *fieldForm
294300

295301
void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
296302
{
297-
QgsDebugMsgLevel( QStringLiteral( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
303+
// Defer all updates if a bulk edit/rollback command is running
304+
if ( mBulkEditCommandRunning )
305+
{
306+
mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
307+
return;
308+
}
309+
QgsDebugMsgLevel( QStringLiteral( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 2 );
298310

299311
for ( SortCache &cache : mSortCaches )
300312
{
@@ -731,30 +743,6 @@ bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &
731743
if ( !layer()->isModified() )
732744
return false;
733745

734-
if ( mChangedCellBounds.isNull() )
735-
{
736-
mChangedCellBounds = QRect( index.column(), index.row(), 1, 1 );
737-
}
738-
else
739-
{
740-
if ( index.column() < mChangedCellBounds.left() )
741-
{
742-
mChangedCellBounds.setLeft( index.column() );
743-
}
744-
if ( index.row() < mChangedCellBounds.top() )
745-
{
746-
mChangedCellBounds.setTop( index.row() );
747-
}
748-
if ( index.column() > mChangedCellBounds.right() )
749-
{
750-
mChangedCellBounds.setRight( index.column() );
751-
}
752-
if ( index.row() > mChangedCellBounds.bottom() )
753-
{
754-
mChangedCellBounds.setBottom( index.row() );
755-
}
756-
}
757-
758746
mRowStylesMap.remove( index.row() );
759747

760748
return true;
@@ -797,6 +785,56 @@ bool QgsAttributeTableModel::fieldIsEditable( const QgsVectorLayer &layer, int f
797785
( ( layer.dataProvider() && layer.dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) || FID_IS_NEW( fid ) ) );
798786
}
799787

788+
void QgsAttributeTableModel::bulkEditCommandStarted()
789+
{
790+
mBulkEditCommandRunning = true;
791+
mAttributeValueChanges.clear();
792+
}
793+
794+
void QgsAttributeTableModel::bulkEditCommandEnded()
795+
{
796+
mBulkEditCommandRunning = false;
797+
// Full model update if the changed rows are more than half the total rows
798+
// or if their count is > layer cache size
799+
int changeCount( mAttributeValueChanges.count() );
800+
bool fullModelUpdate = changeCount > mLayerCache->cacheSize() ||
801+
changeCount > rowCount() * 0.5;
802+
803+
QgsDebugMsgLevel( QStringLiteral( "Bulk edit command ended with %1 modified rows over (%4), cache size is %2, starting %3 update." )
804+
.arg( changeCount )
805+
.arg( mLayerCache->cacheSize() )
806+
.arg( fullModelUpdate ? QStringLiteral( "full" ) : QStringLiteral( "incremental" ) )
807+
.arg( rowCount() ),
808+
3 );
809+
// Invalidates the whole model
810+
if ( fullModelUpdate )
811+
{
812+
// Invalidates the cache (there is no API for doing this directly)
813+
mLayerCache->layer()->dataChanged();
814+
emit dataChanged( createIndex( 0, 0 ), createIndex( rowCount() - 1, columnCount() - 1 ) );
815+
}
816+
else
817+
{
818+
int minRow = rowCount();
819+
int minCol = columnCount();
820+
int maxRow = 0;
821+
int maxCol = 0;
822+
const auto keys = mAttributeValueChanges.keys();
823+
for ( const auto &key : keys )
824+
{
825+
attributeValueChanged( key.first, key.second, mAttributeValueChanges.value( key ) );
826+
int row( idToRow( key.first ) );
827+
int col( fieldCol( key.second ) );
828+
minRow = std::min<int>( row, minRow );
829+
minCol = std::min<int>( col, minCol );
830+
maxRow = std::max<int>( row, maxRow );
831+
maxCol = std::max<int>( col, maxCol );
832+
}
833+
emit dataChanged( createIndex( minRow, minCol ), createIndex( maxRow, maxCol ) );
834+
}
835+
mAttributeValueChanges.clear();
836+
}
837+
800838
void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
801839
{
802840
mFeat.setId( std::numeric_limits<int>::min() );

‎src/gui/attributetable/qgsattributetablemodel.h

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
309309
virtual void attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value );
310310

311311
/**
312-
* Launched when eatures have been deleted
312+
* Launched when features have been deleted
313313
* \param fids feature ids
314314
*/
315315
virtual void featuresDeleted( const QgsFeatureIds &fids );
@@ -378,19 +378,22 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
378378

379379
std::vector<SortCache> mSortCaches;
380380

381-
/**
382-
* Holds the bounds of changed cells while an update operation is running
383-
* top = min row
384-
* left = min column
385-
* bottom = max row
386-
* right = max column
387-
*/
388-
QRect mChangedCellBounds;
389-
390381
QgsAttributeEditorContext mEditorContext;
391382

392383
int mExtraColumns = 0;
393384

385+
//! Flag for massive changes operations, set by edit command or rollback
386+
bool mBulkEditCommandRunning = false;
387+
388+
//! Sets the flag for massive changes operations
389+
void bulkEditCommandStarted();
390+
391+
//! Clears the flag for massive changes operations, updates/rebuilds the layer cache and tells the view to update
392+
void bulkEditCommandEnded();
393+
394+
//! Changed attribute values within a bulk edit command
395+
QMap<QPair<QgsFeatureId, int>, QVariant> mAttributeValueChanges;
396+
394397
friend class TestQgsAttributeTable;
395398

396399
};

0 commit comments

Comments
 (0)
Please sign in to comment.