Skip to content

Commit f875bb7

Browse files
authoredSep 5, 2018
Merge pull request #7792 from m-kuhn/threadsafefeaturesource
Add threadsafe method to get featuresource from layer
2 parents 969c7c5 + 86f4293 commit f875bb7

File tree

4 files changed

+174
-0
lines changed

4 files changed

+174
-0
lines changed
 

‎src/core/qgsvectorlayerutils.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,37 @@ QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const Q
498498
return newFeature;
499499
}
500500

501+
std::unique_ptr<QgsVectorLayerFeatureSource> QgsVectorLayerUtils::getFeatureSource( QPointer<QgsVectorLayer> layer )
502+
{
503+
std::unique_ptr<QgsVectorLayerFeatureSource> featureSource;
504+
505+
auto getFeatureSource = [ layer, &featureSource ]
506+
{
507+
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
508+
Q_ASSERT( QThread::currentThread() == qApp->thread() );
509+
#endif
510+
QgsVectorLayer *lyr = layer.data();
511+
512+
if ( lyr )
513+
{
514+
featureSource.reset( new QgsVectorLayerFeatureSource( lyr ) );
515+
}
516+
};
517+
518+
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
519+
// Make sure we only deal with the vector layer on the main thread where it lives.
520+
// Anything else risks a crash.
521+
if ( QThread::currentThread() == qApp->thread() )
522+
getFeatureSource();
523+
else
524+
QMetaObject::invokeMethod( qApp, getFeatureSource, Qt::BlockingQueuedConnection );
525+
#else
526+
getFeatureSource();
527+
#endif
528+
529+
return featureSource;
530+
}
531+
501532
QList<QgsVectorLayer *> QgsVectorLayerUtils::QgsDuplicateFeatureContext::layers() const
502533
{
503534
QList<QgsVectorLayer *> layers;

‎src/core/qgsvectorlayerutils.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "qgis_core.h"
2020
#include "qgsvectorlayer.h"
2121
#include "qgsgeometry.h"
22+
#include "qgsvectorlayerfeatureiterator.h"
2223

2324
/**
2425
* \ingroup core
@@ -155,6 +156,18 @@ class CORE_EXPORT QgsVectorLayerUtils
155156
*/
156157
static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext SIP_OUT );
157158

159+
/**
160+
* Gets the feature source from a QgsVectorLayer pointer.
161+
* This method is thread-safe but will block the main thread for execution. Executing it from the main
162+
* thread is safe too.
163+
* This should be used in scenarios, where a ``QWeakPointer<QgsVectorLayer>`` is kept in a thread
164+
* and features should be fetched from this layer. Using the layer directly is not safe to do.
165+
* The result will be ``nullptr`` if the layer has been deleted.
166+
*
167+
* \note Requires Qt >= 5.10 to make use of the thread-safe implementation
168+
* \since QGIS 3.4
169+
*/
170+
static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( QPointer<QgsVectorLayer> layer ) SIP_SKIP;
158171
};
159172

160173

‎tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ SET(TESTS
192192
testqgsvectorlayercache.cpp
193193
testqgsvectorlayerjoinbuffer.cpp
194194
testqgsvectorlayer.cpp
195+
testqgsvectorlayerutils.cpp
195196
testziplayer.cpp
196197
testqgsmeshlayer.cpp
197198
testqgsmeshlayerrenderer.cpp
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/***************************************************************************
2+
test_template.cpp
3+
--------------------------------------
4+
Date : Sun Sep 16 12:22:23 AKDT 2007
5+
Copyright : (C) 2007 by Gary E. Sherman
6+
Email : sherman at mrcc dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
#include "qgstest.h"
16+
17+
#include "qgsvectorlayerutils.h"
18+
19+
/**
20+
* \ingroup UnitTests
21+
* This is a unit test for the vector layer class.
22+
*/
23+
class TestQgsVectorLayerUtils : public QObject
24+
{
25+
Q_OBJECT
26+
public:
27+
TestQgsVectorLayerUtils() = default;
28+
29+
private slots:
30+
31+
void initTestCase(); // will be called before the first testfunction is executed.
32+
void cleanupTestCase(); // will be called after the last testfunction was executed.
33+
void init() {} // will be called before each testfunction is executed.
34+
void cleanup() {} // will be called after every testfunction.
35+
36+
void testGetFeatureSource();
37+
};
38+
39+
void TestQgsVectorLayerUtils::initTestCase()
40+
{
41+
QgsApplication::init();
42+
QgsApplication::initQgis();
43+
QgsApplication::showSettings();
44+
45+
}
46+
47+
void TestQgsVectorLayerUtils::cleanupTestCase()
48+
{
49+
QgsApplication::exitQgis();
50+
}
51+
52+
class FeatureFetcher : public QThread
53+
{
54+
Q_OBJECT
55+
56+
public:
57+
FeatureFetcher( QPointer<QgsVectorLayer> layer )
58+
: mLayer( layer )
59+
{
60+
}
61+
62+
void run() override
63+
{
64+
QgsFeature feat;
65+
auto fs = QgsVectorLayerUtils::getFeatureSource( mLayer );
66+
if ( fs )
67+
fs->getFeatures().nextFeature( feat );
68+
emit resultReady( feat.attribute( QStringLiteral( "col1" ) ) );
69+
}
70+
71+
signals:
72+
void resultReady( const QVariant &attribute );
73+
74+
private:
75+
QPointer<QgsVectorLayer> mLayer;
76+
};
77+
78+
79+
void TestQgsVectorLayerUtils::testGetFeatureSource()
80+
{
81+
std::unique_ptr<QgsVectorLayer> vl = qgis::make_unique<QgsVectorLayer>( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
82+
vl->startEditing();
83+
QgsFeature f1( vl->fields(), 1 );
84+
f1.setAttribute( QStringLiteral( "col1" ), 10 );
85+
vl->addFeature( f1 );
86+
87+
QPointer<QgsVectorLayer> vlPtr( vl.get() );
88+
89+
QgsFeature feat;
90+
QgsVectorLayerUtils::getFeatureSource( vlPtr )->getFeatures().nextFeature( feat );
91+
QCOMPARE( feat.attribute( QStringLiteral( "col1" ) ).toInt(), 10 );
92+
93+
FeatureFetcher *thread = new FeatureFetcher( vlPtr );
94+
95+
bool finished = false;
96+
QVariant result;
97+
98+
auto onResultReady = [&finished, &result]( const QVariant & res )
99+
{
100+
finished = true;
101+
result = res;
102+
};
103+
104+
connect( thread, &FeatureFetcher::resultReady, this, onResultReady );
105+
connect( thread, &QThread::finished, thread, &QThread::deleteLater );
106+
107+
thread->start();
108+
while ( !finished )
109+
QCoreApplication::processEvents();
110+
QCOMPARE( result.toInt(), 10 );
111+
thread->quit();
112+
113+
FeatureFetcher *thread2 = new FeatureFetcher( vlPtr );
114+
115+
finished = false;
116+
result = QVariant();
117+
connect( thread2, &FeatureFetcher::resultReady, this, onResultReady );
118+
connect( thread2, &QThread::finished, thread, &QThread::deleteLater );
119+
120+
vl.reset();
121+
thread2->start();
122+
while ( !finished )
123+
QCoreApplication::processEvents();
124+
QVERIFY( result.isNull() );
125+
thread2->quit();
126+
}
127+
128+
QGSTEST_MAIN( TestQgsVectorLayerUtils )
129+
#include "testqgsvectorlayerutils.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.