Skip to content

Commit

Permalink
Feature iterators: add a mechanism to check if it must be interrupted.
Browse files Browse the repository at this point in the history
Add a setInterruptionChecker() method on iterators that provides an interface
that can be called by iterators to check if they must return as soon as
possible. This is set by QgsVectorLayerRenderer::render() to connect to
QgsRenderContext::renderingStopped(). This is useful for some iterators,
like the to be-committed QgsWFSFeatureIterator that can wait for a long
time before returning. By regularly checking if it must be interrupted,
this improves the GUI responsiveness a lot.
This is an enhancement and existing iterators do not need to be modified.
  • Loading branch information
rouault committed Apr 5, 2016
1 parent 9fe6a9f commit 114c108
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/core/qgsfeatureiterator.cpp
Expand Up @@ -224,6 +224,10 @@ bool QgsAbstractFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::
return false;
}

void QgsAbstractFeatureIterator::setInterruptionChecker( QgsInterruptionChecker* )
{
}

///////

QgsFeatureIterator& QgsFeatureIterator::operator=( const QgsFeatureIterator & other )
Expand Down
40 changes: 38 additions & 2 deletions src/core/qgsfeatureiterator.h
Expand Up @@ -21,6 +21,19 @@

class QgsAbstractGeometrySimplifier;

/** \ingroup core
* Interface that can be optionaly attached to an iterator so its
* nextFeature() implementaton can check if it must stop as soon as possible.
* @note Added in QGIS 2.16
* @note not available in Python bindings
*/
class CORE_EXPORT QgsInterruptionChecker
{
public:
//! return true if the iterator must stop as soon as possible
virtual bool mustStop() const = 0;
};

/** \ingroup core
* Internal feature iterator to be implemented within data providers
*/
Expand All @@ -41,6 +54,16 @@ class CORE_EXPORT QgsAbstractFeatureIterator
//! end of iterating: free the resources / lock
virtual bool close() = 0;

/** Attach an object that can be queried regularly by the iterator to check
* if it must stopped. This is mostly useful for iterators where a single
* nextFeature()/fetchFeature() iteration might be very long. A typical use case is the
* WFS provider. When nextFeature()/fetchFeature() is reasonably fast, it is not necessary
* to implement this method. The default implementation does nothing.
* @note added in QGIS 2.16
* @note not available in Python bindings
*/
virtual void setInterruptionChecker( QgsInterruptionChecker* interruptionChecker );

protected:
/**
* If you write a feature iterator for your provider, this is the method you
Expand Down Expand Up @@ -168,8 +191,6 @@ class QgsAbstractFeatureIteratorFromSource : public QgsAbstractFeatureIterator
bool mOwnSource;
};



/**
* \ingroup core
* Wrapper for iterator of features from vector data provider or vector layer
Expand All @@ -195,6 +216,15 @@ class CORE_EXPORT QgsFeatureIterator
//! find out whether the iterator is still valid or closed already
bool isClosed() const;

/** Attach an object that can be queried regularly by the iterator to check
* if it must stopped. This is mostly useful for iterators where a single
* nextFeature()/fetchFeature() iteration might be very long. A typical use case is the
* WFS provider.
* @note added in QGIS 2.16
* @note not available in Python bindings
*/
void setInterruptionChecker( QgsInterruptionChecker* interruptionChecker );

friend bool operator== ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 );
friend bool operator!= ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 );

Expand Down Expand Up @@ -265,4 +295,10 @@ inline bool operator!= ( const QgsFeatureIterator &fi1, const QgsFeatureIterator
return !( fi1 == fi2 );
}

inline void QgsFeatureIterator::setInterruptionChecker( QgsInterruptionChecker* interruptionChecker )
{
if ( mIter )
mIter->setInterruptionChecker( interruptionChecker );
}

#endif // QGSFEATUREITERATOR_H
9 changes: 7 additions & 2 deletions src/core/qgsvectorlayerfeatureiterator.cpp
Expand Up @@ -94,6 +94,7 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
: QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>( source, ownSource, request )
, mFetchedFid( false )
, mEditGeometrySimplifier( nullptr )
, mInterruptionChecker( nullptr )
{
prepareExpressions();

Expand Down Expand Up @@ -236,6 +237,7 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f )
{
mChangedFeaturesIterator.close();
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
mProviderIterator.setInterruptionChecker( mInterruptionChecker );
}

while ( mProviderIterator.nextFeature( f ) )
Expand Down Expand Up @@ -310,8 +312,11 @@ bool QgsVectorLayerFeatureIterator::close()
return true;
}



void QgsVectorLayerFeatureIterator::setInterruptionChecker( QgsInterruptionChecker* interruptionChecker )
{
mProviderIterator.setInterruptionChecker( interruptionChecker );
mInterruptionChecker = interruptionChecker;
}

bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature& f )
{
Expand Down
4 changes: 4 additions & 0 deletions src/core/qgsvectorlayerfeatureiterator.h
Expand Up @@ -84,6 +84,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
//! end of iterating: free the resources / lock
virtual bool close() override;

virtual void setInterruptionChecker( QgsInterruptionChecker* interruptionChecker ) override;

protected:
//! fetch next feature, return true on success
virtual bool fetchFeature( QgsFeature& feature ) override;
Expand Down Expand Up @@ -179,6 +181,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera

QScopedPointer<QgsExpressionContext> mExpressionContext;

QgsInterruptionChecker* mInterruptionChecker;

/**
* Will always return true. We assume that ordering has been done on provider level already.
*
Expand Down
27 changes: 24 additions & 3 deletions src/core/qgsvectorlayerrenderer.cpp
Expand Up @@ -44,6 +44,7 @@
QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer* layer, QgsRenderContext& context )
: QgsMapLayerRenderer( layer->id() )
, mContext( context )
, mInterruptionChecker( context )
, mLayer( layer )
, mFields( layer->fields() )
, mRendererV2( nullptr )
Expand Down Expand Up @@ -246,6 +247,11 @@ bool QgsVectorLayerRenderer::render()
}

QgsFeatureIterator fit = mSource->getFeatures( featureRequest );
// Attach an interruption checker so that iterators that have potentially
// slow fetchFeature() implementations, such as in the WFS provider, can
// check it, instead of relying on just the mContext.renderingStopped() check
// in drawRendererV2()
fit.setInterruptionChecker( &mInterruptionChecker );

if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() )
drawRendererV2Levels( fit );
Expand Down Expand Up @@ -392,9 +398,6 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
if ( !fet.constGeometry() )
continue; // skip features without geometry

if ( mContext.renderingStopped() )
{
qDebug( "rendering stop!" );
Expand All @@ -403,6 +406,9 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
return;
}

if ( !fet.constGeometry() )
continue; // skip features without geometry

mContext.expressionContext().setFeature( fet );
QgsSymbolV2* sym = mRendererV2->symbolForFeature( fet, mContext );
if ( !sym )
Expand Down Expand Up @@ -627,3 +633,18 @@ void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QStringList
mContext.labelingEngine()->prepareDiagramLayer( layer, attributeNames, mContext ); // will make internal copy of diagSettings + initialize it

}

/* ----------------------------------------- */
/* QgsVectorLayerRendererInterruptionChecker */
/* ----------------------------------------- */

QgsVectorLayerRendererInterruptionChecker::QgsVectorLayerRendererInterruptionChecker
( const QgsRenderContext& context )
: mContext( context )
{
}

bool QgsVectorLayerRendererInterruptionChecker::mustStop() const
{
return mContext.renderingStopped();
}
15 changes: 15 additions & 0 deletions src/core/qgsvectorlayerrenderer.h
Expand Up @@ -44,6 +44,19 @@ typedef QList<int> QgsAttributeList;
class QgsVectorLayerLabelProvider;
class QgsVectorLayerDiagramProvider;

/** Interruption checker used by QgsVectorLayerRenderer::render()
* @note not available in Python bindings
*/
class QgsVectorLayerRendererInterruptionChecker: public QgsInterruptionChecker
{
public:
/** Constructor */
QgsVectorLayerRendererInterruptionChecker( const QgsRenderContext& context );
bool mustStop() const override;
private:
const QgsRenderContext& mContext;
};

/**
* Implementation of threaded rendering for vector layers.
*
Expand Down Expand Up @@ -87,6 +100,8 @@ class QgsVectorLayerRenderer : public QgsMapLayerRenderer

QgsRenderContext& mContext;

QgsVectorLayerRendererInterruptionChecker mInterruptionChecker;

/** The rendered layer */
QgsVectorLayer* mLayer;

Expand Down

0 comments on commit 114c108

Please sign in to comment.