Skip to content

Commit 114c108

Browse files
committed
Feature iterators: add a mechanism to check if it must be interrupted.
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.
1 parent 9fe6a9f commit 114c108

6 files changed

+92
-7
lines changed

src/core/qgsfeatureiterator.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ bool QgsAbstractFeatureIterator::prepareOrderBy( const QList<QgsFeatureRequest::
224224
return false;
225225
}
226226

227+
void QgsAbstractFeatureIterator::setInterruptionChecker( QgsInterruptionChecker* )
228+
{
229+
}
230+
227231
///////
228232

229233
QgsFeatureIterator& QgsFeatureIterator::operator=( const QgsFeatureIterator & other )

src/core/qgsfeatureiterator.h

+38-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@
2121

2222
class QgsAbstractGeometrySimplifier;
2323

24+
/** \ingroup core
25+
* Interface that can be optionaly attached to an iterator so its
26+
* nextFeature() implementaton can check if it must stop as soon as possible.
27+
* @note Added in QGIS 2.16
28+
* @note not available in Python bindings
29+
*/
30+
class CORE_EXPORT QgsInterruptionChecker
31+
{
32+
public:
33+
//! return true if the iterator must stop as soon as possible
34+
virtual bool mustStop() const = 0;
35+
};
36+
2437
/** \ingroup core
2538
* Internal feature iterator to be implemented within data providers
2639
*/
@@ -41,6 +54,16 @@ class CORE_EXPORT QgsAbstractFeatureIterator
4154
//! end of iterating: free the resources / lock
4255
virtual bool close() = 0;
4356

57+
/** Attach an object that can be queried regularly by the iterator to check
58+
* if it must stopped. This is mostly useful for iterators where a single
59+
* nextFeature()/fetchFeature() iteration might be very long. A typical use case is the
60+
* WFS provider. When nextFeature()/fetchFeature() is reasonably fast, it is not necessary
61+
* to implement this method. The default implementation does nothing.
62+
* @note added in QGIS 2.16
63+
* @note not available in Python bindings
64+
*/
65+
virtual void setInterruptionChecker( QgsInterruptionChecker* interruptionChecker );
66+
4467
protected:
4568
/**
4669
* If you write a feature iterator for your provider, this is the method you
@@ -168,8 +191,6 @@ class QgsAbstractFeatureIteratorFromSource : public QgsAbstractFeatureIterator
168191
bool mOwnSource;
169192
};
170193

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

219+
/** Attach an object that can be queried regularly by the iterator to check
220+
* if it must stopped. This is mostly useful for iterators where a single
221+
* nextFeature()/fetchFeature() iteration might be very long. A typical use case is the
222+
* WFS provider.
223+
* @note added in QGIS 2.16
224+
* @note not available in Python bindings
225+
*/
226+
void setInterruptionChecker( QgsInterruptionChecker* interruptionChecker );
227+
198228
friend bool operator== ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 );
199229
friend bool operator!= ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 );
200230

@@ -265,4 +295,10 @@ inline bool operator!= ( const QgsFeatureIterator &fi1, const QgsFeatureIterator
265295
return !( fi1 == fi2 );
266296
}
267297

298+
inline void QgsFeatureIterator::setInterruptionChecker( QgsInterruptionChecker* interruptionChecker )
299+
{
300+
if ( mIter )
301+
mIter->setInterruptionChecker( interruptionChecker );
302+
}
303+
268304
#endif // QGSFEATUREITERATOR_H

src/core/qgsvectorlayerfeatureiterator.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ QgsVectorLayerFeatureIterator::QgsVectorLayerFeatureIterator( QgsVectorLayerFeat
9494
: QgsAbstractFeatureIteratorFromSource<QgsVectorLayerFeatureSource>( source, ownSource, request )
9595
, mFetchedFid( false )
9696
, mEditGeometrySimplifier( nullptr )
97+
, mInterruptionChecker( nullptr )
9798
{
9899
prepareExpressions();
99100

@@ -236,6 +237,7 @@ bool QgsVectorLayerFeatureIterator::fetchFeature( QgsFeature& f )
236237
{
237238
mChangedFeaturesIterator.close();
238239
mProviderIterator = mSource->mProviderFeatureSource->getFeatures( mProviderRequest );
240+
mProviderIterator.setInterruptionChecker( mInterruptionChecker );
239241
}
240242

241243
while ( mProviderIterator.nextFeature( f ) )
@@ -310,8 +312,11 @@ bool QgsVectorLayerFeatureIterator::close()
310312
return true;
311313
}
312314

313-
314-
315+
void QgsVectorLayerFeatureIterator::setInterruptionChecker( QgsInterruptionChecker* interruptionChecker )
316+
{
317+
mProviderIterator.setInterruptionChecker( interruptionChecker );
318+
mInterruptionChecker = interruptionChecker;
319+
}
315320

316321
bool QgsVectorLayerFeatureIterator::fetchNextAddedFeature( QgsFeature& f )
317322
{

src/core/qgsvectorlayerfeatureiterator.h

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
8484
//! end of iterating: free the resources / lock
8585
virtual bool close() override;
8686

87+
virtual void setInterruptionChecker( QgsInterruptionChecker* interruptionChecker ) override;
88+
8789
protected:
8890
//! fetch next feature, return true on success
8991
virtual bool fetchFeature( QgsFeature& feature ) override;
@@ -179,6 +181,8 @@ class CORE_EXPORT QgsVectorLayerFeatureIterator : public QgsAbstractFeatureItera
179181

180182
QScopedPointer<QgsExpressionContext> mExpressionContext;
181183

184+
QgsInterruptionChecker* mInterruptionChecker;
185+
182186
/**
183187
* Will always return true. We assume that ordering has been done on provider level already.
184188
*

src/core/qgsvectorlayerrenderer.cpp

+24-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
QgsVectorLayerRenderer::QgsVectorLayerRenderer( QgsVectorLayer* layer, QgsRenderContext& context )
4545
: QgsMapLayerRenderer( layer->id() )
4646
, mContext( context )
47+
, mInterruptionChecker( context )
4748
, mLayer( layer )
4849
, mFields( layer->fields() )
4950
, mRendererV2( nullptr )
@@ -246,6 +247,11 @@ bool QgsVectorLayerRenderer::render()
246247
}
247248

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

250256
if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() )
251257
drawRendererV2Levels( fit );
@@ -392,9 +398,6 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
392398
QgsFeature fet;
393399
while ( fit.nextFeature( fet ) )
394400
{
395-
if ( !fet.constGeometry() )
396-
continue; // skip features without geometry
397-
398401
if ( mContext.renderingStopped() )
399402
{
400403
qDebug( "rendering stop!" );
@@ -403,6 +406,9 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
403406
return;
404407
}
405408

409+
if ( !fet.constGeometry() )
410+
continue; // skip features without geometry
411+
406412
mContext.expressionContext().setFeature( fet );
407413
QgsSymbolV2* sym = mRendererV2->symbolForFeature( fet, mContext );
408414
if ( !sym )
@@ -627,3 +633,18 @@ void QgsVectorLayerRenderer::prepareDiagrams( QgsVectorLayer* layer, QStringList
627633
mContext.labelingEngine()->prepareDiagramLayer( layer, attributeNames, mContext ); // will make internal copy of diagSettings + initialize it
628634

629635
}
636+
637+
/* ----------------------------------------- */
638+
/* QgsVectorLayerRendererInterruptionChecker */
639+
/* ----------------------------------------- */
640+
641+
QgsVectorLayerRendererInterruptionChecker::QgsVectorLayerRendererInterruptionChecker
642+
( const QgsRenderContext& context )
643+
: mContext( context )
644+
{
645+
}
646+
647+
bool QgsVectorLayerRendererInterruptionChecker::mustStop() const
648+
{
649+
return mContext.renderingStopped();
650+
}

src/core/qgsvectorlayerrenderer.h

+15
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ typedef QList<int> QgsAttributeList;
4444
class QgsVectorLayerLabelProvider;
4545
class QgsVectorLayerDiagramProvider;
4646

47+
/** Interruption checker used by QgsVectorLayerRenderer::render()
48+
* @note not available in Python bindings
49+
*/
50+
class QgsVectorLayerRendererInterruptionChecker: public QgsInterruptionChecker
51+
{
52+
public:
53+
/** Constructor */
54+
QgsVectorLayerRendererInterruptionChecker( const QgsRenderContext& context );
55+
bool mustStop() const override;
56+
private:
57+
const QgsRenderContext& mContext;
58+
};
59+
4760
/**
4861
* Implementation of threaded rendering for vector layers.
4962
*
@@ -87,6 +100,8 @@ class QgsVectorLayerRenderer : public QgsMapLayerRenderer
87100

88101
QgsRenderContext& mContext;
89102

103+
QgsVectorLayerRendererInterruptionChecker mInterruptionChecker;
104+
90105
/** The rendered layer */
91106
QgsVectorLayer* mLayer;
92107

0 commit comments

Comments
 (0)