Skip to content

Commit e792576

Browse files
committed
Improve QgsVectorLayer::countSymbolFeatures() responsiveness.
The method can display a progress dialog when it takes a long time to complete. - Attach this dialog to the QGIS main window. - Use the interruption checker mechanism of iterators so that the to-be-committed improved WFS feature iterator can use it - Call QCoreApplication::processEvents() for better UI responsiveness.
1 parent 114c108 commit e792576

File tree

1 file changed

+84
-8
lines changed

1 file changed

+84
-8
lines changed

src/core/qgsvectorlayer.cpp

+84-8
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,44 @@ long QgsVectorLayer::featureCount( QgsSymbolV2* symbol )
697697
return mSymbolFeatureCountMap.value( symbol );
698698
}
699699

700+
/** Used by QgsVectorLayer::countSymbolFeatures() to provide an interruption checker
701+
* @note not available in Python bindings
702+
*/
703+
class QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures: public QgsInterruptionChecker
704+
{
705+
public:
706+
707+
/** Constructor */
708+
QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures( QProgressDialog* dialog )
709+
: mDialog( dialog )
710+
{
711+
}
712+
713+
virtual bool mustStop() const
714+
{
715+
if ( mDialog->isVisible() )
716+
{
717+
// So that we get a chance of hitting the Abort button
718+
#ifdef Q_OS_LINUX
719+
// For some reason on Windows hasPendingEvents() always return true,
720+
// but one iteration is actually enough on Windows to get good interactivity
721+
// whereas on Linux we must allow for far more iterations.
722+
// For safety limit the number of iterations
723+
int nIters = 0;
724+
while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 )
725+
#endif
726+
{
727+
QCoreApplication::processEvents();
728+
}
729+
return mDialog->wasCanceled();
730+
}
731+
return false;
732+
}
733+
734+
private:
735+
QProgressDialog* mDialog;
736+
};
737+
700738
bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
701739
{
702740
if ( mSymbolFeatureCounted )
@@ -729,12 +767,34 @@ bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
729767
}
730768

731769
long nFeatures = featureCount();
732-
QProgressDialog progressDialog( tr( "Updating feature count for layer %1" ).arg( name() ), tr( "Abort" ), 0, nFeatures );
770+
771+
QWidget* mainWindow = nullptr;
772+
Q_FOREACH ( QWidget* widget, qApp->topLevelWidgets() )
773+
{
774+
if ( widget->objectName() == "QgisApp" )
775+
{
776+
mainWindow = widget;
777+
break;
778+
}
779+
}
780+
781+
QProgressDialog progressDialog( tr( "Updating feature count for layer %1" ).arg( name() ), tr( "Abort" ), 0, nFeatures, mainWindow );
733782
progressDialog.setWindowTitle( tr( "QGIS" ) );
734783
progressDialog.setWindowModality( Qt::WindowModal );
784+
if ( showProgress )
785+
{
786+
// Properly initialize to 0 as recommended in doc so that the evaluation
787+
// of the total time properly works
788+
progressDialog.setValue( 0 );
789+
}
735790
int featuresCounted = 0;
736791

737792
QgsFeatureIterator fit = getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) );
793+
QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures interruptionCheck( &progressDialog );
794+
if ( showProgress )
795+
{
796+
fit.setInterruptionChecker( &interruptionCheck );
797+
}
738798

739799
// Renderer (rule based) may depend on context scale, with scale is ignored if 0
740800
QgsRenderContext renderContext;
@@ -746,6 +806,8 @@ bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
746806
mRendererV2->startRender( renderContext, fields() );
747807

748808
QgsFeature f;
809+
QTime time;
810+
time.start();
749811
while ( fit.nextFeature( f ) )
750812
{
751813
renderContext.expressionContext().setFeature( f );
@@ -758,19 +820,33 @@ bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
758820

759821
if ( showProgress )
760822
{
761-
if ( featuresCounted % 50 == 0 )
823+
// Refresh progress every 50 features or second
824+
if (( featuresCounted % 50 == 0 ) || time.elapsed() > 1000 )
762825
{
826+
time.restart();
763827
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
764828
{
765829
progressDialog.setMaximum( 0 );
766830
}
767831
progressDialog.setValue( featuresCounted );
768-
if ( progressDialog.wasCanceled() )
769-
{
770-
mSymbolFeatureCountMap.clear();
771-
mRendererV2->stopRender( renderContext );
772-
return false;
773-
}
832+
}
833+
// So that we get a chance of hitting the Abort button
834+
#ifdef Q_OS_LINUX
835+
// For some reason on Windows hasPendingEvents() always return true,
836+
// but one iteration is actually enough on Windows to get good interactivity
837+
// whereas on Linux we must allow for far more iterations.
838+
// For safety limit the number of iterations
839+
int nIters = 0;
840+
while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 )
841+
#endif
842+
{
843+
QCoreApplication::processEvents();
844+
}
845+
if ( progressDialog.wasCanceled() )
846+
{
847+
mSymbolFeatureCountMap.clear();
848+
mRendererV2->stopRender( renderContext );
849+
return false;
774850
}
775851
}
776852
}

0 commit comments

Comments
 (0)