mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
Merge pull request #7792 from m-kuhn/threadsafefeaturesource
Add threadsafe method to get featuresource from layer
This commit is contained in:
commit
f875bb77b4
@ -498,6 +498,37 @@ QgsFeature QgsVectorLayerUtils::duplicateFeature( QgsVectorLayer *layer, const Q
|
||||
return newFeature;
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsVectorLayerFeatureSource> QgsVectorLayerUtils::getFeatureSource( QPointer<QgsVectorLayer> layer )
|
||||
{
|
||||
std::unique_ptr<QgsVectorLayerFeatureSource> featureSource;
|
||||
|
||||
auto getFeatureSource = [ layer, &featureSource ]
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
|
||||
Q_ASSERT( QThread::currentThread() == qApp->thread() );
|
||||
#endif
|
||||
QgsVectorLayer *lyr = layer.data();
|
||||
|
||||
if ( lyr )
|
||||
{
|
||||
featureSource.reset( new QgsVectorLayerFeatureSource( lyr ) );
|
||||
}
|
||||
};
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK( 5, 10, 0 )
|
||||
// Make sure we only deal with the vector layer on the main thread where it lives.
|
||||
// Anything else risks a crash.
|
||||
if ( QThread::currentThread() == qApp->thread() )
|
||||
getFeatureSource();
|
||||
else
|
||||
QMetaObject::invokeMethod( qApp, getFeatureSource, Qt::BlockingQueuedConnection );
|
||||
#else
|
||||
getFeatureSource();
|
||||
#endif
|
||||
|
||||
return featureSource;
|
||||
}
|
||||
|
||||
QList<QgsVectorLayer *> QgsVectorLayerUtils::QgsDuplicateFeatureContext::layers() const
|
||||
{
|
||||
QList<QgsVectorLayer *> layers;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "qgis_core.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgsvectorlayerfeatureiterator.h"
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -155,6 +156,18 @@ class CORE_EXPORT QgsVectorLayerUtils
|
||||
*/
|
||||
static QgsFeature duplicateFeature( QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext SIP_OUT );
|
||||
|
||||
/**
|
||||
* Gets the feature source from a QgsVectorLayer pointer.
|
||||
* This method is thread-safe but will block the main thread for execution. Executing it from the main
|
||||
* thread is safe too.
|
||||
* This should be used in scenarios, where a ``QWeakPointer<QgsVectorLayer>`` is kept in a thread
|
||||
* and features should be fetched from this layer. Using the layer directly is not safe to do.
|
||||
* The result will be ``nullptr`` if the layer has been deleted.
|
||||
*
|
||||
* \note Requires Qt >= 5.10 to make use of the thread-safe implementation
|
||||
* \since QGIS 3.4
|
||||
*/
|
||||
static std::unique_ptr<QgsVectorLayerFeatureSource> getFeatureSource( QPointer<QgsVectorLayer> layer ) SIP_SKIP;
|
||||
};
|
||||
|
||||
|
||||
|
@ -192,6 +192,7 @@ SET(TESTS
|
||||
testqgsvectorlayercache.cpp
|
||||
testqgsvectorlayerjoinbuffer.cpp
|
||||
testqgsvectorlayer.cpp
|
||||
testqgsvectorlayerutils.cpp
|
||||
testziplayer.cpp
|
||||
testqgsmeshlayer.cpp
|
||||
testqgsmeshlayerrenderer.cpp
|
||||
|
129
tests/src/core/testqgsvectorlayerutils.cpp
Normal file
129
tests/src/core/testqgsvectorlayerutils.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/***************************************************************************
|
||||
test_template.cpp
|
||||
--------------------------------------
|
||||
Date : Sun Sep 16 12:22:23 AKDT 2007
|
||||
Copyright : (C) 2007 by Gary E. Sherman
|
||||
Email : sherman at mrcc dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgstest.h"
|
||||
|
||||
#include "qgsvectorlayerutils.h"
|
||||
|
||||
/**
|
||||
* \ingroup UnitTests
|
||||
* This is a unit test for the vector layer class.
|
||||
*/
|
||||
class TestQgsVectorLayerUtils : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestQgsVectorLayerUtils() = default;
|
||||
|
||||
private slots:
|
||||
|
||||
void initTestCase(); // will be called before the first testfunction is executed.
|
||||
void cleanupTestCase(); // will be called after the last testfunction was executed.
|
||||
void init() {} // will be called before each testfunction is executed.
|
||||
void cleanup() {} // will be called after every testfunction.
|
||||
|
||||
void testGetFeatureSource();
|
||||
};
|
||||
|
||||
void TestQgsVectorLayerUtils::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
QgsApplication::showSettings();
|
||||
|
||||
}
|
||||
|
||||
void TestQgsVectorLayerUtils::cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
class FeatureFetcher : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FeatureFetcher( QPointer<QgsVectorLayer> layer )
|
||||
: mLayer( layer )
|
||||
{
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
QgsFeature feat;
|
||||
auto fs = QgsVectorLayerUtils::getFeatureSource( mLayer );
|
||||
if ( fs )
|
||||
fs->getFeatures().nextFeature( feat );
|
||||
emit resultReady( feat.attribute( QStringLiteral( "col1" ) ) );
|
||||
}
|
||||
|
||||
signals:
|
||||
void resultReady( const QVariant &attribute );
|
||||
|
||||
private:
|
||||
QPointer<QgsVectorLayer> mLayer;
|
||||
};
|
||||
|
||||
|
||||
void TestQgsVectorLayerUtils::testGetFeatureSource()
|
||||
{
|
||||
std::unique_ptr<QgsVectorLayer> vl = qgis::make_unique<QgsVectorLayer>( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
|
||||
vl->startEditing();
|
||||
QgsFeature f1( vl->fields(), 1 );
|
||||
f1.setAttribute( QStringLiteral( "col1" ), 10 );
|
||||
vl->addFeature( f1 );
|
||||
|
||||
QPointer<QgsVectorLayer> vlPtr( vl.get() );
|
||||
|
||||
QgsFeature feat;
|
||||
QgsVectorLayerUtils::getFeatureSource( vlPtr )->getFeatures().nextFeature( feat );
|
||||
QCOMPARE( feat.attribute( QStringLiteral( "col1" ) ).toInt(), 10 );
|
||||
|
||||
FeatureFetcher *thread = new FeatureFetcher( vlPtr );
|
||||
|
||||
bool finished = false;
|
||||
QVariant result;
|
||||
|
||||
auto onResultReady = [&finished, &result]( const QVariant & res )
|
||||
{
|
||||
finished = true;
|
||||
result = res;
|
||||
};
|
||||
|
||||
connect( thread, &FeatureFetcher::resultReady, this, onResultReady );
|
||||
connect( thread, &QThread::finished, thread, &QThread::deleteLater );
|
||||
|
||||
thread->start();
|
||||
while ( !finished )
|
||||
QCoreApplication::processEvents();
|
||||
QCOMPARE( result.toInt(), 10 );
|
||||
thread->quit();
|
||||
|
||||
FeatureFetcher *thread2 = new FeatureFetcher( vlPtr );
|
||||
|
||||
finished = false;
|
||||
result = QVariant();
|
||||
connect( thread2, &FeatureFetcher::resultReady, this, onResultReady );
|
||||
connect( thread2, &QThread::finished, thread, &QThread::deleteLater );
|
||||
|
||||
vl.reset();
|
||||
thread2->start();
|
||||
while ( !finished )
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY( result.isNull() );
|
||||
thread2->quit();
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsVectorLayerUtils )
|
||||
#include "testqgsvectorlayerutils.moc"
|
Loading…
x
Reference in New Issue
Block a user