Send feature counting to background task

This commit is contained in:
Matthias Kuhn 2017-05-09 23:54:59 +02:00
parent f264370130
commit 964d9ac70a
8 changed files with 196 additions and 98 deletions

View File

@ -165,6 +165,7 @@
%Include qgsvectorlayercache.sip %Include qgsvectorlayercache.sip
%Include qgsvectorlayereditbuffer.sip %Include qgsvectorlayereditbuffer.sip
%Include qgsvectorlayereditpassthrough.sip %Include qgsvectorlayereditpassthrough.sip
%Include qgsvectorlayerfeaturecounter.sip
%Include qgsvectorlayerimport.sip %Include qgsvectorlayerimport.sip
%Include qgsvectorlayerjoinbuffer.sip %Include qgsvectorlayerjoinbuffer.sip
%Include qgsvectorlayerjoininfo.sip %Include qgsvectorlayerjoininfo.sip

View File

@ -841,7 +841,7 @@ Return the provider type for this layer
.. versionadded:: 2.10 .. versionadded:: 2.10
%End %End
bool countSymbolFeatures( bool showProgress = true ); bool countSymbolFeatures();
%Docstring %Docstring
Count features for symbols. Feature counts may be get by featureCount(). Count features for symbols. Feature counts may be get by featureCount().
\param showProgress show progress dialog \param showProgress show progress dialog
@ -2000,6 +2000,12 @@ Signal emitted when setLayerTransparency() is called
.. versionadded:: 3.0 .. versionadded:: 3.0
%End %End
void symbolFeatureCountMapChanged();
%Docstring
Emitted when the feature count for symbols on this layer has been recalculated.
.. versionadded:: 3.0
%End
protected: protected:
virtual void setExtent( const QgsRectangle &rect ); virtual void setExtent( const QgsRectangle &rect );

View File

@ -0,0 +1,45 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsvectorlayerfeaturecounter.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
class QgsVectorLayerFeatureCounter : QgsTask
{
%Docstring
Counts the features in a QgsVectorLayer in task.
You should most likely not use this directly and instead call
QgsVectorLayer.countSymbolFeatures() and connect to the signal
QgsVectorLayer.symbolFeatureCountMapChanged().
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsvectorlayerfeaturecounter.h"
%End
public:
QgsVectorLayerFeatureCounter( QgsVectorLayer *layer );
%Docstring
Create a new feature counter for ``layer``.
%End
virtual bool run();
signals:
void symbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
};
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/qgsvectorlayerfeaturecounter.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/

View File

@ -263,6 +263,7 @@ SET(QGIS_CORE_SRCS
qgsvectorfilewriter.cpp qgsvectorfilewriter.cpp
qgsvectorfilewritertask.cpp qgsvectorfilewritertask.cpp
qgsvectorlayer.cpp qgsvectorlayer.cpp
qgsvectorlayerfeaturecounter.cpp
qgsvectorlayercache.cpp qgsvectorlayercache.cpp
qgsvectorlayerdiagramprovider.cpp qgsvectorlayerdiagramprovider.cpp
qgsvectorlayereditbuffer.cpp qgsvectorlayereditbuffer.cpp
@ -569,6 +570,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsvectorlayereditbuffer.h qgsvectorlayereditbuffer.h
qgsvectorlayereditpassthrough.h qgsvectorlayereditpassthrough.h
qgsvectorlayer.h qgsvectorlayer.h
qgsvectorlayerfeaturecounter.h
qgsvectorlayerjoinbuffer.h qgsvectorlayerjoinbuffer.h
qgsvectorlayertools.h qgsvectorlayertools.h
qgsmapthemecollection.h qgsmapthemecollection.h

View File

@ -71,6 +71,7 @@
#include "qgsvectorlayerlabeling.h" #include "qgsvectorlayerlabeling.h"
#include "qgsvectorlayerrenderer.h" #include "qgsvectorlayerrenderer.h"
#include "qgsvectorlayerundocommand.h" #include "qgsvectorlayerundocommand.h"
#include "qgsvectorlayerfeaturecounter.h"
#include "qgspointv2.h" #include "qgspointv2.h"
#include "qgsrenderer.h" #include "qgsrenderer.h"
#include "qgssymbollayer.h" #include "qgssymbollayer.h"
@ -83,6 +84,7 @@
#include "qgsfeedback.h" #include "qgsfeedback.h"
#include "qgsxmlutils.h" #include "qgsxmlutils.h"
#include "qgsunittypes.h" #include "qgsunittypes.h"
#include "qgstaskmanager.h"
#include "diagram/qgsdiagram.h" #include "diagram/qgsdiagram.h"
@ -126,6 +128,7 @@ typedef bool deleteStyleById_t(
QString &errCause QString &errCause
); );
QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath, QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath,
const QString &baseName, const QString &baseName,
const QString &providerKey, const QString &providerKey,
@ -674,7 +677,7 @@ class QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures: public QgsInte
QProgressDialog *mDialog = nullptr; QProgressDialog *mDialog = nullptr;
}; };
bool QgsVectorLayer::countSymbolFeatures( bool showProgress ) bool QgsVectorLayer::countSymbolFeatures()
{ {
if ( mSymbolFeatureCounted ) if ( mSymbolFeatureCounted )
return true; return true;
@ -697,103 +700,16 @@ bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
return false; return false;
} }
QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems(); if ( !mFeatureCounter )
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
{ {
mSymbolFeatureCountMap.insert( symbolIt->first, 0 ); mFeatureCounter.reset( new QgsVectorLayerFeatureCounter( this ) );
connect( mFeatureCounter.get(), &QgsVectorLayerFeatureCounter::symbolsCounted, this, &QgsVectorLayer::onSymbolsCounted );
connect( mFeatureCounter.get(), &QgsTask::taskCompleted, [ = ]() { mFeatureCounter.reset(); } );
connect( mFeatureCounter.get(), &QgsTask::taskTerminated, [ = ]() { mFeatureCounter.reset(); } );
QgsApplication::taskManager()->addTask( mFeatureCounter.get() );
} }
long nFeatures = featureCount();
QWidget *mainWindow = nullptr;
Q_FOREACH ( QWidget *widget, qApp->topLevelWidgets() )
{
if ( widget->objectName() == QLatin1String( "QgisApp" ) )
{
mainWindow = widget;
break;
}
}
QProgressDialog progressDialog( tr( "Updating feature count for layer %1" ).arg( name() ), tr( "Abort" ), 0, nFeatures, mainWindow );
progressDialog.setWindowTitle( tr( "QGIS" ) );
progressDialog.setWindowModality( Qt::WindowModal );
if ( showProgress )
{
// Properly initialize to 0 as recommended in doc so that the evaluation
// of the total time properly works
progressDialog.setValue( 0 );
}
int featuresCounted = 0;
// Renderer (rule based) may depend on context scale, with scale is ignored if 0
QgsRenderContext renderContext;
renderContext.setRendererScale( 0 );
renderContext.expressionContext().appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( this ) );
QgsFeatureRequest request;
if ( !mRenderer->filterNeedsGeometry() )
request.setFlags( QgsFeatureRequest::NoGeometry );
request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mFields );
QgsFeatureIterator fit = getFeatures( request );
QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures interruptionCheck( &progressDialog );
if ( showProgress )
{
fit.setInterruptionChecker( &interruptionCheck );
}
mRenderer->startRender( renderContext, fields() );
QgsFeature f;
QTime time;
time.start();
while ( fit.nextFeature( f ) )
{
renderContext.expressionContext().setFeature( f );
QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext );
Q_FOREACH ( const QString &key, featureKeyList )
{
mSymbolFeatureCountMap[key] += 1;
}
++featuresCounted;
if ( showProgress )
{
// Refresh progress every 50 features or second
if ( ( featuresCounted % 50 == 0 ) || time.elapsed() > 1000 )
{
time.restart();
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
{
progressDialog.setMaximum( 0 );
}
progressDialog.setValue( featuresCounted );
}
// So that we get a chance of hitting the Abort button
#ifdef Q_OS_LINUX
// For some reason on Windows hasPendingEvents() always return true,
// but one iteration is actually enough on Windows to get good interactivity
// whereas on Linux we must allow for far more iterations.
// For safety limit the number of iterations
int nIters = 0;
while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 )
#endif
{
QCoreApplication::processEvents();
}
if ( progressDialog.wasCanceled() )
{
mSymbolFeatureCountMap.clear();
mRenderer->stopRender( renderContext );
return false;
}
}
}
mRenderer->stopRender( renderContext );
progressDialog.setValue( nFeatures );
mSymbolFeatureCounted = true;
return true; return true;
} }
@ -3983,6 +3899,13 @@ void QgsVectorLayer::onRelationsLoaded()
mEditFormConfig.onRelationsLoaded(); mEditFormConfig.onRelationsLoaded();
} }
void QgsVectorLayer::onSymbolsCounted( const QHash<QString, long> &symbolFeatureCountMap )
{
mSymbolFeatureCountMap = symbolFeatureCountMap;
mSymbolFeatureCounted = true;
emit symbolFeatureCountMapChanged();
}
QList<QgsRelation> QgsVectorLayer::referencingRelations( int idx ) const QList<QgsRelation> QgsVectorLayer::referencingRelations( int idx ) const
{ {
return QgsProject::instance()->relationManager()->referencingRelations( this, idx ); return QgsProject::instance()->relationManager()->referencingRelations( this, idx );

View File

@ -65,6 +65,7 @@ class QgsSymbol;
class QgsVectorLayerJoinInfo; class QgsVectorLayerJoinInfo;
class QgsVectorLayerEditBuffer; class QgsVectorLayerEditBuffer;
class QgsVectorLayerJoinBuffer; class QgsVectorLayerJoinBuffer;
class QgsVectorLayerFeatureCounter;
class QgsAbstractVectorLayerLabeling; class QgsAbstractVectorLayerLabeling;
class QgsPointV2; class QgsPointV2;
class QgsFeedback; class QgsFeedback;
@ -810,7 +811,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* \param showProgress show progress dialog * \param showProgress show progress dialog
* \returns true if calculated, false if failed or was canceled by user * \returns true if calculated, false if failed or was canceled by user
*/ */
bool countSymbolFeatures( bool showProgress = true ); bool countSymbolFeatures();
/** /**
* Set the string (typically sql) used to define a subset of the layer * Set the string (typically sql) used to define a subset of the layer
@ -1846,11 +1847,18 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/ */
void readOnlyChanged(); void readOnlyChanged();
/**
* Emitted when the feature count for symbols on this layer has been recalculated.
*
* \since QGIS 3.0
*/
void symbolFeatureCountMapChanged();
private slots: private slots:
void onJoinedFieldsChanged(); void onJoinedFieldsChanged();
void onFeatureDeleted( QgsFeatureId fid ); void onFeatureDeleted( QgsFeatureId fid );
void onRelationsLoaded(); void onRelationsLoaded();
void onSymbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
protected: protected:
//! Set the extent //! Set the extent
@ -1883,7 +1891,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
#endif #endif
private: // Private attributes private: // Private attributes
QgsConditionalLayerStyles *mConditionalStyles = nullptr; QgsConditionalLayerStyles *mConditionalStyles = nullptr;
//! Pointer to data provider derived from the abastract base class QgsDataProvider //! Pointer to data provider derived from the abastract base class QgsDataProvider
@ -2001,6 +2008,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
mutable QMutex mFeatureSourceConstructorMutex; mutable QMutex mFeatureSourceConstructorMutex;
std::unique_ptr<QgsVectorLayerFeatureCounter> mFeatureCounter;
friend class QgsVectorLayerFeatureSource; friend class QgsVectorLayerFeatureSource;
}; };

View File

@ -0,0 +1,69 @@
#include "qgsvectorlayerfeaturecounter.h"
QgsVectorLayerFeatureCounter::QgsVectorLayerFeatureCounter( QgsVectorLayer *layer )
: mSource( new QgsVectorLayerFeatureSource( layer ) )
, mRenderer( layer->renderer()->clone() )
, mExpressionContextScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) )
, mFeatureCount( layer->featureCount() )
{
}
bool QgsVectorLayerFeatureCounter::run()
{
QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems();
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
{
mSymbolFeatureCountMap.insert( symbolIt->first, 0 );
}
int featuresCounted = 0;
// Renderer (rule based) may depend on context scale, with scale is ignored if 0
QgsRenderContext renderContext;
renderContext.setRendererScale( 0 );
renderContext.expressionContext().appendScopes( mExpressionContextScopes );
QgsFeatureRequest request;
if ( !mRenderer->filterNeedsGeometry() )
request.setFlags( QgsFeatureRequest::NoGeometry );
request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mSource->fields() );
QgsFeatureIterator fit = mSource->getFeatures( request );
// TODO: replace QgsInterruptionChecker with QgsFeedback
// fit.setInterruptionChecker( mFeedback );
mRenderer->startRender( renderContext, mSource->fields() );
double progress = 0;
QgsFeature f;
while ( fit.nextFeature( f ) )
{
renderContext.expressionContext().setFeature( f );
QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext );
Q_FOREACH ( const QString &key, featureKeyList )
{
mSymbolFeatureCountMap[key] += 1;
}
++featuresCounted;
double p = ( featuresCounted / mFeatureCount ) * 100;
if ( p - progress > 1 )
{
progress = p;
setProgress( progress );
}
if ( isCanceled() )
{
mRenderer->stopRender( renderContext );
return false;
}
}
mRenderer->stopRender( renderContext );
setProgress( 100 );
emit symbolsCounted( mSymbolFeatureCountMap );
return true;
}

View File

@ -0,0 +1,43 @@
#ifndef QGSVECTORLAYERFEATURECOUNTER_H
#define QGSVECTORLAYERFEATURECOUNTER_H
#include "qgsvectorlayer.h"
#include "qgsvectorlayerfeatureiterator.h"
#include "qgsrenderer.h"
#include "qgstaskmanager.h"
/** \ingroup core
*
* Counts the features in a QgsVectorLayer in task.
* You should most likely not use this directly and instead call
* QgsVectorLayer::countSymbolFeatures() and connect to the signal
* QgsVectorLayer::symbolFeatureCountMapChanged().
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsVectorLayerFeatureCounter : public QgsTask
{
Q_OBJECT
public:
/**
* Create a new feature counter for \a layer.
*/
QgsVectorLayerFeatureCounter( QgsVectorLayer *layer );
virtual bool run() override;
signals:
void symbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
private:
std::unique_ptr<QgsVectorLayerFeatureSource> mSource;
std::unique_ptr<QgsFeatureRenderer> mRenderer;
QList<QgsExpressionContextScope *> mExpressionContextScopes;
QHash<QString, long> mSymbolFeatureCountMap;
int mFeatureCount;
};
#endif // QGSVECTORLAYERFEATURECOUNTER_H