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 qgsvectorlayereditbuffer.sip
%Include qgsvectorlayereditpassthrough.sip
%Include qgsvectorlayerfeaturecounter.sip
%Include qgsvectorlayerimport.sip
%Include qgsvectorlayerjoinbuffer.sip
%Include qgsvectorlayerjoininfo.sip

View File

@ -841,7 +841,7 @@ Return the provider type for this layer
.. versionadded:: 2.10
%End
bool countSymbolFeatures( bool showProgress = true );
bool countSymbolFeatures();
%Docstring
Count features for symbols. Feature counts may be get by featureCount().
\param showProgress show progress dialog
@ -2000,6 +2000,12 @@ Signal emitted when setLayerTransparency() is called
.. versionadded:: 3.0
%End
void symbolFeatureCountMapChanged();
%Docstring
Emitted when the feature count for symbols on this layer has been recalculated.
.. versionadded:: 3.0
%End
protected:
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
qgsvectorfilewritertask.cpp
qgsvectorlayer.cpp
qgsvectorlayerfeaturecounter.cpp
qgsvectorlayercache.cpp
qgsvectorlayerdiagramprovider.cpp
qgsvectorlayereditbuffer.cpp
@ -569,6 +570,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsvectorlayereditbuffer.h
qgsvectorlayereditpassthrough.h
qgsvectorlayer.h
qgsvectorlayerfeaturecounter.h
qgsvectorlayerjoinbuffer.h
qgsvectorlayertools.h
qgsmapthemecollection.h

View File

@ -71,6 +71,7 @@
#include "qgsvectorlayerlabeling.h"
#include "qgsvectorlayerrenderer.h"
#include "qgsvectorlayerundocommand.h"
#include "qgsvectorlayerfeaturecounter.h"
#include "qgspointv2.h"
#include "qgsrenderer.h"
#include "qgssymbollayer.h"
@ -83,6 +84,7 @@
#include "qgsfeedback.h"
#include "qgsxmlutils.h"
#include "qgsunittypes.h"
#include "qgstaskmanager.h"
#include "diagram/qgsdiagram.h"
@ -126,6 +128,7 @@ typedef bool deleteStyleById_t(
QString &errCause
);
QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath,
const QString &baseName,
const QString &providerKey,
@ -674,7 +677,7 @@ class QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures: public QgsInte
QProgressDialog *mDialog = nullptr;
};
bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
bool QgsVectorLayer::countSymbolFeatures()
{
if ( mSymbolFeatureCounted )
return true;
@ -697,103 +700,16 @@ bool QgsVectorLayer::countSymbolFeatures( bool showProgress )
return false;
}
QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems();
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
if ( !mFeatureCounter )
{
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;
}
@ -3983,6 +3899,13 @@ void QgsVectorLayer::onRelationsLoaded()
mEditFormConfig.onRelationsLoaded();
}
void QgsVectorLayer::onSymbolsCounted( const QHash<QString, long> &symbolFeatureCountMap )
{
mSymbolFeatureCountMap = symbolFeatureCountMap;
mSymbolFeatureCounted = true;
emit symbolFeatureCountMapChanged();
}
QList<QgsRelation> QgsVectorLayer::referencingRelations( int idx ) const
{
return QgsProject::instance()->relationManager()->referencingRelations( this, idx );

View File

@ -65,6 +65,7 @@ class QgsSymbol;
class QgsVectorLayerJoinInfo;
class QgsVectorLayerEditBuffer;
class QgsVectorLayerJoinBuffer;
class QgsVectorLayerFeatureCounter;
class QgsAbstractVectorLayerLabeling;
class QgsPointV2;
class QgsFeedback;
@ -810,7 +811,7 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
* \param showProgress show progress dialog
* \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
@ -1846,11 +1847,18 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/
void readOnlyChanged();
/**
* Emitted when the feature count for symbols on this layer has been recalculated.
*
* \since QGIS 3.0
*/
void symbolFeatureCountMapChanged();
private slots:
void onJoinedFieldsChanged();
void onFeatureDeleted( QgsFeatureId fid );
void onRelationsLoaded();
void onSymbolsCounted( const QHash<QString, long> &symbolFeatureCountMap );
protected:
//! Set the extent
@ -1883,7 +1891,6 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
#endif
private: // Private attributes
QgsConditionalLayerStyles *mConditionalStyles = nullptr;
//! 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;
std::unique_ptr<QgsVectorLayerFeatureCounter> mFeatureCounter;
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