mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-04 00:30:59 -05:00
[WFS provider] Extract reusable logic for progress dialog
Funded by Planet
This commit is contained in:
parent
3d2f9f1aab
commit
e12c5522e2
@ -16,6 +16,7 @@
|
||||
#include "qgsbackgroundcachedfeatureiterator.h"
|
||||
#include "qgsbackgroundcachedshareddata.h"
|
||||
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsfeedback.h"
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmessagelog.h"
|
||||
@ -24,6 +25,8 @@
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMutex>
|
||||
#include <QPushButton>
|
||||
#include <QStyle>
|
||||
#include <QTimer>
|
||||
|
||||
#include <sqlite3.h>
|
||||
@ -33,15 +36,54 @@ const QString QgsBackgroundCachedFeatureIteratorConstants::FIELD_UNIQUE_ID( QStr
|
||||
const QString QgsBackgroundCachedFeatureIteratorConstants::FIELD_HEXWKB_GEOM( QStringLiteral( "__qgis_hexwkb_geom" ) );
|
||||
const QString QgsBackgroundCachedFeatureIteratorConstants::FIELD_MD5( QStringLiteral( "__qgis_md5" ) );
|
||||
|
||||
// -------------------------
|
||||
|
||||
|
||||
QgsFeatureDownloaderProgressDialog::QgsFeatureDownloaderProgressDialog( const QString &labelText,
|
||||
const QString &cancelButtonText,
|
||||
int minimum, int maximum, QWidget *parent )
|
||||
: QProgressDialog( labelText, cancelButtonText, minimum, maximum, parent )
|
||||
{
|
||||
mCancel = new QPushButton( cancelButtonText, this );
|
||||
setCancelButton( mCancel );
|
||||
mHide = new QPushButton( tr( "Hide" ), this );
|
||||
connect( mHide, &QAbstractButton::clicked, this, &QgsFeatureDownloaderProgressDialog::hideRequest );
|
||||
}
|
||||
|
||||
void QgsFeatureDownloaderProgressDialog::resizeEvent( QResizeEvent *ev )
|
||||
{
|
||||
QProgressDialog::resizeEvent( ev );
|
||||
// Note: this relies heavily on the details of the layout done in QProgressDialogPrivate::layout()
|
||||
// Might be a bit fragile depending on QT versions.
|
||||
QRect rect = geometry();
|
||||
QRect cancelRect = mCancel->geometry();
|
||||
QRect hideRect = mHide->geometry();
|
||||
int mtb = style()->pixelMetric( QStyle::PM_DefaultTopLevelMargin );
|
||||
int mlr = std::min( width() / 10, mtb );
|
||||
if ( rect.width() - cancelRect.x() - cancelRect.width() > mlr )
|
||||
{
|
||||
// Force right alighnment of cancel button
|
||||
cancelRect.setX( rect.width() - cancelRect.width() - mlr );
|
||||
mCancel->setGeometry( cancelRect );
|
||||
}
|
||||
mHide->setGeometry( rect.width() - cancelRect.x() - cancelRect.width(),
|
||||
cancelRect.y(), hideRect.width(), cancelRect.height() );
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
|
||||
QgsFeatureDownloaderImpl::QgsFeatureDownloaderImpl( QgsFeatureDownloader *downloader ): mDownloader( downloader )
|
||||
QgsFeatureDownloaderImpl::QgsFeatureDownloaderImpl( QgsBackgroundCachedSharedData *shared, QgsFeatureDownloader *downloader ): mSharedBase( shared ), mDownloader( downloader )
|
||||
{
|
||||
// Needed because used by a signal
|
||||
qRegisterMetaType< QVector<QgsFeatureUniqueIdPair> >( "QVector<QgsFeatureUniqueIdPair>" );
|
||||
}
|
||||
|
||||
QgsFeatureDownloaderImpl::~QgsFeatureDownloaderImpl()
|
||||
{
|
||||
if ( mProgressDialog )
|
||||
mProgressDialog->deleteLater();
|
||||
}
|
||||
|
||||
void QgsFeatureDownloaderImpl::emitFeatureReceived( QVector<QgsFeatureUniqueIdPair> features )
|
||||
{
|
||||
emit mDownloader->featureReceived( features );
|
||||
@ -57,6 +99,98 @@ void QgsFeatureDownloaderImpl::emitEndOfDownload( bool success )
|
||||
emit mDownloader->endOfDownload( success );
|
||||
}
|
||||
|
||||
void QgsFeatureDownloaderImpl::stop()
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "QgsFeatureDownloaderImpl::stop()" ), 4 );
|
||||
mStop = true;
|
||||
emitDoStop();
|
||||
}
|
||||
|
||||
void QgsFeatureDownloaderImpl::setStopFlag()
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "QgsFeatureDownloaderImpl::setStopFlag()" ), 4 );
|
||||
mStop = true;
|
||||
}
|
||||
|
||||
void QgsFeatureDownloaderImpl::hideProgressDialog()
|
||||
{
|
||||
mSharedBase->setHideProgressDialog( true );
|
||||
mProgressDialog->deleteLater();
|
||||
mProgressDialog = nullptr;
|
||||
}
|
||||
|
||||
// Called from GUI thread
|
||||
void QgsFeatureDownloaderImpl::createProgressDialog( int numberMatched )
|
||||
{
|
||||
Q_ASSERT( qApp->thread() == QThread::currentThread() );
|
||||
|
||||
// Make sure that the creation is done in an atomic way, so that the
|
||||
// starting thread (running QgsFeatureDownloaderImpl::run()) can be sure that
|
||||
// this function has either run completely, or not at all (mStop == true),
|
||||
// when it wants to destroy mProgressDialog
|
||||
QMutexLocker locker( &mMutexCreateProgressDialog );
|
||||
|
||||
if ( mStop )
|
||||
return;
|
||||
Q_ASSERT( !mProgressDialog );
|
||||
|
||||
if ( !mMainWindow )
|
||||
{
|
||||
const QWidgetList widgets = QgsApplication::topLevelWidgets();
|
||||
for ( QWidget *widget : widgets )
|
||||
{
|
||||
if ( widget->objectName() == QLatin1String( "QgisApp" ) )
|
||||
{
|
||||
mMainWindow = widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !mMainWindow )
|
||||
return;
|
||||
|
||||
mProgressDialog = new QgsFeatureDownloaderProgressDialog( QObject::tr( "Loading features for layer %1" ).arg( mSharedBase->layerName() ),
|
||||
QObject::tr( "Abort" ), 0, numberMatched, mMainWindow );
|
||||
mProgressDialog->setWindowTitle( QObject::tr( "QGIS" ) );
|
||||
mProgressDialog->setValue( 0 );
|
||||
if ( mProgressDialogShowImmediately )
|
||||
mProgressDialog->show();
|
||||
}
|
||||
|
||||
void QgsFeatureDownloaderImpl::endOfRun( bool serializeFeatures,
|
||||
bool success, int totalDownloadedFeatureCount,
|
||||
bool truncatedResponse, bool interrupted,
|
||||
const QString &errorMessage )
|
||||
{
|
||||
{
|
||||
QMutexLocker locker( &mMutexCreateProgressDialog );
|
||||
mStop = true;
|
||||
}
|
||||
|
||||
if ( serializeFeatures )
|
||||
mSharedBase->endOfDownload( success, totalDownloadedFeatureCount, truncatedResponse, interrupted, errorMessage );
|
||||
|
||||
// We must emit the signal *AFTER* the previous call to mShared->endOfDownload()
|
||||
// to avoid issues with iterators that would start just now, wouldn't detect
|
||||
// that the downloader has finished, would register to itself, but would never
|
||||
// receive the endOfDownload signal. This is not just a theoretical problem.
|
||||
// If you switch both calls, it happens quite easily in Release mode with the
|
||||
// test suite.
|
||||
emitEndOfDownload( success );
|
||||
|
||||
if ( mProgressDialog )
|
||||
{
|
||||
mProgressDialog->deleteLater();
|
||||
mProgressDialog = nullptr;
|
||||
}
|
||||
if ( mTimer )
|
||||
{
|
||||
mTimer->deleteLater();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
|
||||
|
||||
@ -317,7 +451,7 @@ QgsFeatureRequest QgsBackgroundCachedFeatureIterator::buildRequestCache( int gen
|
||||
|
||||
QgsBackgroundCachedFeatureIterator::~QgsBackgroundCachedFeatureIterator()
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "qgsWFSFeatureIterator::~QgsBackgroundCachedFeatureIterator()" ), 4 );
|
||||
QgsDebugMsgLevel( QStringLiteral( "QgsBackgroundCachedFeatureIterator::~QgsBackgroundCachedFeatureIterator()" ), 4 );
|
||||
|
||||
close();
|
||||
|
||||
|
@ -22,9 +22,11 @@
|
||||
|
||||
class QDataStream;
|
||||
class QFile;
|
||||
class QPushButton;
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include <QWaitCondition>
|
||||
#include <QProgressDialog>
|
||||
|
||||
struct QgsBackgroundCachedFeatureIteratorConstants
|
||||
{
|
||||
@ -40,6 +42,27 @@ typedef QPair<QgsFeature, QString> QgsFeatureUniqueIdPair;
|
||||
|
||||
class QgsFeatureDownloader;
|
||||
|
||||
|
||||
//! Utility class for QgsFeatureDownloaderImpl
|
||||
class QgsFeatureDownloaderProgressDialog: public QProgressDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
//! Constructor
|
||||
QgsFeatureDownloaderProgressDialog( const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent );
|
||||
|
||||
void resizeEvent( QResizeEvent *ev ) override;
|
||||
|
||||
signals:
|
||||
void hideRequest();
|
||||
|
||||
private:
|
||||
QPushButton *mCancel = nullptr;
|
||||
QPushButton *mHide = nullptr;
|
||||
};
|
||||
|
||||
class QgsBackgroundCachedSharedData;
|
||||
|
||||
/**
|
||||
* This class is an abstract class that must
|
||||
* be subclassed and whose main role is to download features, through one or
|
||||
@ -57,8 +80,8 @@ class QgsFeatureDownloader;
|
||||
class QgsFeatureDownloaderImpl
|
||||
{
|
||||
public:
|
||||
QgsFeatureDownloaderImpl( QgsFeatureDownloader *downloader );
|
||||
virtual ~QgsFeatureDownloaderImpl() = default;
|
||||
QgsFeatureDownloaderImpl( QgsBackgroundCachedSharedData *shared, QgsFeatureDownloader *downloader );
|
||||
virtual ~QgsFeatureDownloaderImpl();
|
||||
|
||||
/**
|
||||
* Start the download.
|
||||
@ -70,7 +93,10 @@ class QgsFeatureDownloaderImpl
|
||||
virtual void run( bool serializeFeatures, int maxFeatures ) = 0;
|
||||
|
||||
//! To interrupt the download. Must be thread-safe
|
||||
virtual void stop() = 0;
|
||||
void stop();
|
||||
|
||||
//! Emit doStop() signal
|
||||
virtual void emitDoStop() = 0;
|
||||
|
||||
// To be used when new features have been received
|
||||
void emitFeatureReceived( QVector<QgsFeatureUniqueIdPair> features );
|
||||
@ -81,10 +107,84 @@ class QgsFeatureDownloaderImpl
|
||||
// To be used when the download is finished (successful or not)
|
||||
void emitEndOfDownload( bool success );
|
||||
|
||||
#if 0
|
||||
// NOTE: implementations should copy & paste the below block
|
||||
signals:
|
||||
/* Used internally by the stop() method */
|
||||
void doStop();
|
||||
|
||||
/* Emitted with the total accumulated number of features downloaded. */
|
||||
void updateProgress( int totalFeatureCount );
|
||||
#endif
|
||||
|
||||
protected:
|
||||
//! Progress dialog
|
||||
QgsFeatureDownloaderProgressDialog *mProgressDialog = nullptr;
|
||||
|
||||
//! Whether the download should stop
|
||||
bool mStop = false;
|
||||
|
||||
/**
|
||||
* If the progress dialog should be shown immediately, or if it should be
|
||||
let to QProgressDialog logic to decide when to show it */
|
||||
bool mProgressDialogShowImmediately = false;
|
||||
|
||||
QTimer *mTimer = nullptr;
|
||||
|
||||
void createProgressDialog( int numberMatched );
|
||||
|
||||
void setStopFlag();
|
||||
void hideProgressDialog();
|
||||
|
||||
void endOfRun( bool serializeFeatures,
|
||||
bool success, int totalDownloadedFeatureCount,
|
||||
bool truncatedResponse, bool interrupted,
|
||||
const QString &errorMessage );
|
||||
|
||||
private:
|
||||
QgsBackgroundCachedSharedData *mSharedBase;
|
||||
QgsFeatureDownloader *mDownloader;
|
||||
QWidget *mMainWindow = nullptr;
|
||||
QMutex mMutexCreateProgressDialog;
|
||||
};
|
||||
|
||||
// Sorry for ugliness. Due to QgsFeatureDownloaderImpl that cannot derived from QObject
|
||||
#define CONNECT_PROGRESS_DIALOG(actual_downloader_impl_class) do { \
|
||||
connect( mProgressDialog, &QProgressDialog::canceled, this, &actual_downloader_impl_class::setStopFlag, Qt::DirectConnection ); \
|
||||
connect( mProgressDialog, &QProgressDialog::canceled, this, &actual_downloader_impl_class::stop ); \
|
||||
connect( mProgressDialog, &QgsFeatureDownloaderProgressDialog::hideRequest, this, &actual_downloader_impl_class::hideProgressDialog ); \
|
||||
\
|
||||
/* Make sure the progress dialog has not been deleted by another thread */ \
|
||||
if ( mProgressDialog ) \
|
||||
{ \
|
||||
connect( this, &actual_downloader_impl_class::updateProgress, mProgressDialog, &QProgressDialog::setValue ); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
// Sorry for ugliness. Due to QgsFeatureDownloaderImpl that cannot derived from QObject
|
||||
#define DEFINE_FEATURE_DOWLOADER_IMPL_SLOTS \
|
||||
protected: \
|
||||
void emitDoStop() override { emit doStop(); } \
|
||||
void setStopFlag() { QgsFeatureDownloaderImpl::setStopFlag(); } \
|
||||
void stop() { QgsFeatureDownloaderImpl::stop(); } \
|
||||
void hideProgressDialog() { QgsFeatureDownloaderImpl::hideProgressDialog(); }
|
||||
|
||||
#define CREATE_PROGRESS_DIALOG(actual_downloader_impl_class) \
|
||||
do { \
|
||||
/* This is a bit tricky. We want the createProgressDialog() */ \
|
||||
/* method to be run into the GUI thread */ \
|
||||
mTimer = new QTimer(); \
|
||||
mTimer->setSingleShot( true ); \
|
||||
\
|
||||
/* Direct connection, since we want createProgressDialog() */ \
|
||||
/* to be invoked from the same thread as timer, and not in the */ \
|
||||
/* thread of this */ \
|
||||
connect( mTimer, &QTimer::timeout, this, &actual_downloader_impl_class::createProgressDialog, Qt::DirectConnection ); \
|
||||
\
|
||||
mTimer->moveToThread( qApp->thread() ); \
|
||||
QMetaObject::invokeMethod( mTimer, "start", Qt::QueuedConnection ); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Interface of the downloader, typically called by QgsThreadedFeatureDownloader.
|
||||
* The real work is done by the implementation passed to setImpl().
|
||||
|
@ -157,6 +157,9 @@ class QgsBackgroundCachedSharedData
|
||||
//! To be called when a temporary file is removed from the directory
|
||||
void releaseCacheDirectory();
|
||||
|
||||
//! Set whether the progress dialog should be hidden
|
||||
void setHideProgressDialog( bool b ) { mHideProgressDialog = b; }
|
||||
|
||||
//////// Pure virtual methods
|
||||
|
||||
//! Instantiate a new feature downloader implementation.
|
||||
@ -168,6 +171,9 @@ class QgsBackgroundCachedSharedData
|
||||
//! Return whether the layer has a geometry field
|
||||
virtual bool hasGeometry() const = 0;
|
||||
|
||||
//! Return layer name
|
||||
virtual QString layerName() const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
//////////// Input members. Implementations should define them to meaningful values
|
||||
@ -196,6 +202,9 @@ class QgsBackgroundCachedSharedData
|
||||
//! Flag is a /items request returns a numberMatched property
|
||||
bool mHasNumberMatched = false;
|
||||
|
||||
//! Whether progress dialog should be hidden
|
||||
bool mHideProgressDialog = false;
|
||||
|
||||
//////////// Methods
|
||||
|
||||
//! Should be called in the destructor of the implementation of this class !
|
||||
@ -313,9 +322,6 @@ class QgsBackgroundCachedSharedData
|
||||
//! Return whether the server limit downloading a limiter number of features
|
||||
virtual bool supportsLimitedFeatureCountDownloads() const = 0;
|
||||
|
||||
//! Return layer name
|
||||
virtual QString layerName() const = 0;
|
||||
|
||||
//! Return whether a server-side (non-spatial) filter is applied
|
||||
virtual bool hasServerSideFilter() const = 0;
|
||||
|
||||
|
@ -340,8 +340,8 @@ QString QgsOapifProvider::description() const
|
||||
QgsOapifSharedData::QgsOapifSharedData( const QString &uri )
|
||||
: QgsBackgroundCachedSharedData( "oapif", tr( "OAPIF" ) )
|
||||
, mURI( uri )
|
||||
, mHideProgressDialog( mURI.hideDownloadProgressDialog() )
|
||||
{
|
||||
mHideProgressDialog = mURI.hideDownloadProgressDialog();
|
||||
}
|
||||
|
||||
QgsOapifSharedData::~QgsOapifSharedData()
|
||||
@ -576,21 +576,13 @@ void QgsOapifSharedData::pushError( const QString &errorMsg )
|
||||
// ---------------------------------
|
||||
|
||||
QgsOapifFeatureDownloaderImpl::QgsOapifFeatureDownloaderImpl( QgsOapifSharedData *shared, QgsFeatureDownloader *downloader ):
|
||||
QgsFeatureDownloaderImpl( downloader ),
|
||||
QgsFeatureDownloaderImpl( shared, downloader ),
|
||||
mShared( shared )
|
||||
{
|
||||
}
|
||||
|
||||
QgsOapifFeatureDownloaderImpl::~QgsOapifFeatureDownloaderImpl()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void QgsOapifFeatureDownloaderImpl::stop()
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "QgsOapifFeatureDownloaderImpl::stop()" ), 4 );
|
||||
mStop = true;
|
||||
emit doStop();
|
||||
}
|
||||
|
||||
void QgsOapifFeatureDownloaderImpl::run( bool serializeFeatures, int maxFeatures )
|
||||
|
@ -157,9 +157,6 @@ class QgsOapifSharedData : public QObject, public QgsBackgroundCachedSharedData
|
||||
//! Page size. 0 = disabled
|
||||
int mPageSize = 0;
|
||||
|
||||
//! Whether progress dialog should be hidden
|
||||
bool mHideProgressDialog = false;
|
||||
|
||||
//! Url to /collections/{collectionId}
|
||||
QString mCollectionUrl;
|
||||
|
||||
@ -203,29 +200,26 @@ class QgsOapifSharedData : public QObject, public QgsBackgroundCachedSharedData
|
||||
class QgsOapifFeatureDownloaderImpl: public QObject, public QgsFeatureDownloaderImpl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
DEFINE_FEATURE_DOWLOADER_IMPL_SLOTS
|
||||
|
||||
signals:
|
||||
/* Used internally by the stop() method */
|
||||
void doStop();
|
||||
|
||||
/* Emitted with the total accumulated number of features downloaded. */
|
||||
void updateProgress( int totalFeatureCount );
|
||||
|
||||
public:
|
||||
QgsOapifFeatureDownloaderImpl( QgsOapifSharedData *shared, QgsFeatureDownloader *downloader );
|
||||
~QgsOapifFeatureDownloaderImpl() override;
|
||||
|
||||
void run( bool serializeFeatures, int maxFeatures ) override;
|
||||
|
||||
void stop() override;
|
||||
|
||||
signals:
|
||||
|
||||
//! Used internally by the stop() method
|
||||
void doStop();
|
||||
|
||||
//! Emitted with the total accumulated number of features downloaded.
|
||||
void updateProgress( int totalFeatureCount );
|
||||
|
||||
private:
|
||||
|
||||
//! Mutable data shared between provider, feature sources and downloader.
|
||||
QgsOapifSharedData *mShared = nullptr;
|
||||
|
||||
//! Whether the download should stop
|
||||
bool mStop = false;
|
||||
};
|
||||
|
||||
|
||||
|
@ -34,9 +34,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <QDir>
|
||||
#include <QProgressDialog>
|
||||
#include <QTimer>
|
||||
#include <QStyle>
|
||||
|
||||
QgsWFSFeatureHitsAsyncRequest::QgsWFSFeatureHitsAsyncRequest( QgsWFSDataSourceURI &uri )
|
||||
: QgsWfsRequest( uri )
|
||||
@ -81,43 +79,9 @@ QString QgsWFSFeatureHitsAsyncRequest::errorMessageWithReason( const QString &re
|
||||
|
||||
// -------------------------
|
||||
|
||||
|
||||
QgsWFSProgressDialog::QgsWFSProgressDialog( const QString &labelText,
|
||||
const QString &cancelButtonText,
|
||||
int minimum, int maximum, QWidget *parent )
|
||||
: QProgressDialog( labelText, cancelButtonText, minimum, maximum, parent )
|
||||
{
|
||||
mCancel = new QPushButton( cancelButtonText, this );
|
||||
setCancelButton( mCancel );
|
||||
mHide = new QPushButton( tr( "Hide" ), this );
|
||||
connect( mHide, &QAbstractButton::clicked, this, &QgsWFSProgressDialog::hideRequest );
|
||||
}
|
||||
|
||||
void QgsWFSProgressDialog::resizeEvent( QResizeEvent *ev )
|
||||
{
|
||||
QProgressDialog::resizeEvent( ev );
|
||||
// Note: this relies heavily on the details of the layout done in QProgressDialogPrivate::layout()
|
||||
// Might be a bit fragile depending on QT versions.
|
||||
QRect rect = geometry();
|
||||
QRect cancelRect = mCancel->geometry();
|
||||
QRect hideRect = mHide->geometry();
|
||||
int mtb = style()->pixelMetric( QStyle::PM_DefaultTopLevelMargin );
|
||||
int mlr = std::min( width() / 10, mtb );
|
||||
if ( rect.width() - cancelRect.x() - cancelRect.width() > mlr )
|
||||
{
|
||||
// Force right alighnment of cancel button
|
||||
cancelRect.setX( rect.width() - cancelRect.width() - mlr );
|
||||
mCancel->setGeometry( cancelRect );
|
||||
}
|
||||
mHide->setGeometry( rect.width() - cancelRect.x() - cancelRect.width(),
|
||||
cancelRect.y(), hideRect.width(), cancelRect.height() );
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
|
||||
QgsWFSFeatureDownloaderImpl::QgsWFSFeatureDownloaderImpl( QgsWFSSharedData *shared, QgsFeatureDownloader *downloader ):
|
||||
QgsWfsRequest( shared->mURI ),
|
||||
QgsFeatureDownloaderImpl( downloader ),
|
||||
QgsFeatureDownloaderImpl( shared, downloader ),
|
||||
mShared( shared ),
|
||||
mPageSize( shared->mPageSize ),
|
||||
mFeatureHitsAsyncRequest( shared->mURI )
|
||||
@ -128,81 +92,10 @@ QgsWFSFeatureDownloaderImpl::~QgsWFSFeatureDownloaderImpl()
|
||||
{
|
||||
stop();
|
||||
|
||||
if ( mProgressDialog )
|
||||
mProgressDialog->deleteLater();
|
||||
if ( mTimer )
|
||||
mTimer->deleteLater();
|
||||
}
|
||||
|
||||
void QgsWFSFeatureDownloaderImpl::stop()
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "QgsWFSFeatureDownloaderImpl::stop()" ), 4 );
|
||||
mStop = true;
|
||||
emit doStop();
|
||||
}
|
||||
|
||||
void QgsWFSFeatureDownloaderImpl::setStopFlag()
|
||||
{
|
||||
QgsDebugMsgLevel( QStringLiteral( "QgsWFSFeatureDownloaderImpl::setStopFlag()" ), 4 );
|
||||
mStop = true;
|
||||
}
|
||||
|
||||
void QgsWFSFeatureDownloaderImpl::hideProgressDialog()
|
||||
{
|
||||
mShared->mHideProgressDialog = true;
|
||||
mProgressDialog->deleteLater();
|
||||
mProgressDialog = nullptr;
|
||||
}
|
||||
|
||||
// Called from GUI thread
|
||||
void QgsWFSFeatureDownloaderImpl::createProgressDialog()
|
||||
{
|
||||
Q_ASSERT( qApp->thread() == QThread::currentThread() );
|
||||
|
||||
// Make sure that the creation is done in an atomic way, so that the
|
||||
// starting thread (running QgsWFSFeatureDownloaderImpl::run()) can be sure that
|
||||
// this function has either run completely, or not at all (mStop == true),
|
||||
// when it wants to destroy mProgressDialog
|
||||
QMutexLocker locker( &mMutexCreateProgressDialog );
|
||||
|
||||
if ( mStop )
|
||||
return;
|
||||
Q_ASSERT( !mProgressDialog );
|
||||
|
||||
if ( !mMainWindow )
|
||||
{
|
||||
const QWidgetList widgets = qApp->topLevelWidgets();
|
||||
for ( QWidget *widget : widgets )
|
||||
{
|
||||
if ( widget->objectName() == QLatin1String( "QgisApp" ) )
|
||||
{
|
||||
mMainWindow = widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !mMainWindow )
|
||||
return;
|
||||
|
||||
mProgressDialog = new QgsWFSProgressDialog( tr( "Loading features for layer %1" ).arg( mShared->mURI.typeName() ),
|
||||
tr( "Abort" ), 0, mNumberMatched, mMainWindow );
|
||||
mProgressDialog->setWindowTitle( tr( "QGIS" ) );
|
||||
mProgressDialog->setValue( 0 );
|
||||
if ( mProgressDialogShowImmediately )
|
||||
mProgressDialog->show();
|
||||
|
||||
connect( mProgressDialog, &QProgressDialog::canceled, this, &QgsWFSFeatureDownloaderImpl::setStopFlag, Qt::DirectConnection );
|
||||
connect( mProgressDialog, &QProgressDialog::canceled, this, &QgsWFSFeatureDownloaderImpl::stop );
|
||||
connect( mProgressDialog, &QgsWFSProgressDialog::hideRequest, this, &QgsWFSFeatureDownloaderImpl::hideProgressDialog );
|
||||
|
||||
// Make sure the progress dialog has not been deleted by another thread
|
||||
if ( mProgressDialog )
|
||||
{
|
||||
connect( this, &QgsWFSFeatureDownloaderImpl::updateProgress, mProgressDialog, &QProgressDialog::setValue );
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsWFSFeatureDownloaderImpl::sanitizeFilter( QString filter )
|
||||
{
|
||||
filter = filter.replace( QLatin1String( "<fes:ValueReference xmlns:fes=\"http://www.opengis.net/fes/2.0\">" ), QLatin1String( "<fes:ValueReference>" ) );
|
||||
@ -442,6 +335,12 @@ void QgsWFSFeatureDownloaderImpl::startHitsRequest()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsWFSFeatureDownloaderImpl::createProgressDialog()
|
||||
{
|
||||
QgsFeatureDownloaderImpl::createProgressDialog( mNumberMatched );
|
||||
CONNECT_PROGRESS_DIALOG( QgsWFSFeatureDownloaderImpl );
|
||||
}
|
||||
|
||||
void QgsWFSFeatureDownloaderImpl::run( bool serializeFeatures, int maxFeatures )
|
||||
{
|
||||
bool success = true;
|
||||
@ -453,12 +352,8 @@ void QgsWFSFeatureDownloaderImpl::run( bool serializeFeatures, int maxFeatures )
|
||||
|
||||
QTimer timerForHits;
|
||||
|
||||
if ( !mShared->mHideProgressDialog && maxFeatures != 1 && mShared->supportsFastFeatureCount() )
|
||||
{
|
||||
mUseProgressDialog = true;
|
||||
}
|
||||
|
||||
if ( mUseProgressDialog )
|
||||
const bool useProgressDialog = ( !mShared->mHideProgressDialog && maxFeatures != 1 && mShared->supportsFastFeatureCount() );
|
||||
if ( useProgressDialog )
|
||||
{
|
||||
// In case the header of the GetFeature response doesn't contain the total
|
||||
// number of features, or we don't get it within 4 seconds, we will issue
|
||||
@ -617,7 +512,7 @@ void QgsWFSFeatureDownloaderImpl::run( bool serializeFeatures, int maxFeatures )
|
||||
|
||||
// Consider if we should display a progress dialog
|
||||
// We can only do that if we know how many features will be downloaded
|
||||
if ( !mTimer && maxFeatures != 1 && mUseProgressDialog )
|
||||
if ( !mTimer && maxFeatures != 1 && useProgressDialog )
|
||||
{
|
||||
if ( mNumberMatched < 0 )
|
||||
{
|
||||
@ -647,18 +542,7 @@ void QgsWFSFeatureDownloaderImpl::run( bool serializeFeatures, int maxFeatures )
|
||||
if ( mShared->supportsFastFeatureCount() )
|
||||
disconnect( &timerForHits, &QTimer::timeout, this, &QgsWFSFeatureDownloaderImpl::startHitsRequest );
|
||||
|
||||
// This is a bit tricky. We want the createProgressDialog()
|
||||
// method to be run into the GUI thread
|
||||
mTimer = new QTimer();
|
||||
mTimer->setSingleShot( true );
|
||||
|
||||
// Direct connection, since we want createProgressDialog()
|
||||
// to be invoked from the same thread as timer, and not in the
|
||||
// thread of this
|
||||
connect( mTimer, &QTimer::timeout, this, &QgsWFSFeatureDownloaderImpl::createProgressDialog, Qt::DirectConnection );
|
||||
|
||||
mTimer->moveToThread( qApp->thread() );
|
||||
QMetaObject::invokeMethod( mTimer, "start", Qt::QueuedConnection );
|
||||
CREATE_PROGRESS_DIALOG( QgsWFSFeatureDownloaderImpl );
|
||||
}
|
||||
}
|
||||
|
||||
@ -850,32 +734,7 @@ void QgsWFSFeatureDownloaderImpl::run( bool serializeFeatures, int maxFeatures )
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker locker( &mMutexCreateProgressDialog );
|
||||
mStop = true;
|
||||
}
|
||||
|
||||
if ( serializeFeatures )
|
||||
mShared->endOfDownload( success, mTotalDownloadedFeatureCount, truncatedResponse, interrupted, mErrorMessage );
|
||||
|
||||
// We must emit the signal *AFTER* the previous call to mShared->endOfDownload()
|
||||
// to avoid issues with iterators that would start just now, wouldn't detect
|
||||
// that the downloader has finished, would register to itself, but would never
|
||||
// receive the endOfDownload signal. This is not just a theoretical problem.
|
||||
// If you switch both calls, it happens quite easily in Release mode with the
|
||||
// test suite.
|
||||
emitEndOfDownload( success );
|
||||
|
||||
if ( mProgressDialog )
|
||||
{
|
||||
mProgressDialog->deleteLater();
|
||||
mProgressDialog = nullptr;
|
||||
}
|
||||
if ( mTimer )
|
||||
{
|
||||
mTimer->deleteLater();
|
||||
mTimer = nullptr;
|
||||
}
|
||||
endOfRun( serializeFeatures, success, mTotalDownloadedFeatureCount, truncatedResponse, interrupted, mErrorMessage );
|
||||
|
||||
// explicitly abort here so that mReply is destroyed within the right thread
|
||||
// otherwise will deadlock because deleteLayer() will not have a valid thread to post
|
||||
|
@ -24,16 +24,12 @@
|
||||
#include "qgsbackgroundcachedfeatureiterator.h"
|
||||
|
||||
#include <memory>
|
||||
#include <QProgressDialog>
|
||||
#include <QPushButton>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
class QgsWFSProvider;
|
||||
class QgsWFSSharedData;
|
||||
class QgsVectorDataProvider;
|
||||
class QProgressDialog;
|
||||
|
||||
|
||||
//! Utility class to issue a GetFeature resultType=hits request
|
||||
class QgsWFSFeatureHitsAsyncRequest: public QgsWfsRequest
|
||||
@ -60,26 +56,6 @@ class QgsWFSFeatureHitsAsyncRequest: public QgsWfsRequest
|
||||
int mNumberMatched;
|
||||
};
|
||||
|
||||
|
||||
//! Utility class for QgsWFSFeatureDownloader
|
||||
class QgsWFSProgressDialog: public QProgressDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
//! Constructor
|
||||
QgsWFSProgressDialog( const QString &labelText, const QString &cancelButtonText, int minimum, int maximum, QWidget *parent );
|
||||
|
||||
void resizeEvent( QResizeEvent *ev ) override;
|
||||
|
||||
signals:
|
||||
void hideRequest();
|
||||
|
||||
private:
|
||||
QPushButton *mCancel = nullptr;
|
||||
QPushButton *mHide = nullptr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class runs one (or several if paging is needed) GetFeature request,
|
||||
process the results as soon as they arrived and notify them to the
|
||||
@ -91,31 +67,30 @@ class QgsWFSProgressDialog: public QProgressDialog
|
||||
class QgsWFSFeatureDownloaderImpl: public QgsWfsRequest, public QgsFeatureDownloaderImpl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
DEFINE_FEATURE_DOWLOADER_IMPL_SLOTS
|
||||
|
||||
signals:
|
||||
/* Used internally by the stop() method */
|
||||
void doStop();
|
||||
|
||||
/* Emitted with the total accumulated number of features downloaded. */
|
||||
void updateProgress( int totalFeatureCount );
|
||||
|
||||
public:
|
||||
QgsWFSFeatureDownloaderImpl( QgsWFSSharedData *shared, QgsFeatureDownloader *downloader );
|
||||
~QgsWFSFeatureDownloaderImpl() override;
|
||||
|
||||
void run( bool serializeFeatures, int maxFeatures ) override;
|
||||
|
||||
void stop() override;
|
||||
|
||||
signals:
|
||||
|
||||
//! Used internally by the stop() method
|
||||
void doStop();
|
||||
|
||||
//! Emitted with the total accumulated number of features downloaded.
|
||||
void updateProgress( int totalFeatureCount );
|
||||
|
||||
protected:
|
||||
QString errorMessageWithReason( const QString &reason ) override;
|
||||
|
||||
private slots:
|
||||
void createProgressDialog();
|
||||
void startHitsRequest();
|
||||
void gotHitsResponse();
|
||||
void setStopFlag();
|
||||
void hideProgressDialog();
|
||||
|
||||
void createProgressDialog();
|
||||
|
||||
private:
|
||||
QUrl buildURL( qint64 startIndex, int maxFeatures, bool forHits );
|
||||
@ -124,24 +99,12 @@ class QgsWFSFeatureDownloaderImpl: public QgsWfsRequest, public QgsFeatureDownlo
|
||||
|
||||
//! Mutable data shared between provider, feature sources and downloader.
|
||||
QgsWFSSharedData *mShared = nullptr;
|
||||
//! Whether the download should stop
|
||||
bool mStop = false;
|
||||
//! Progress dialog
|
||||
QgsWFSProgressDialog *mProgressDialog = nullptr;
|
||||
|
||||
/**
|
||||
* If the progress dialog should be shown immediately, or if it should be
|
||||
let to QProgressDialog logic to decide when to show it */
|
||||
bool mProgressDialogShowImmediately = false;
|
||||
int mPageSize = 0;
|
||||
bool mRemoveNSPrefix = false;
|
||||
int mNumberMatched = -1;
|
||||
bool mUseProgressDialog = false;
|
||||
QWidget *mMainWindow = nullptr;
|
||||
QTimer *mTimer = nullptr;
|
||||
QgsWFSFeatureHitsAsyncRequest mFeatureHitsAsyncRequest;
|
||||
qint64 mTotalDownloadedFeatureCount = 0;
|
||||
QMutex mMutexCreateProgressDialog;
|
||||
};
|
||||
|
||||
|
||||
|
@ -24,8 +24,8 @@
|
||||
QgsWFSSharedData::QgsWFSSharedData( const QString &uri )
|
||||
: QgsBackgroundCachedSharedData( "wfs", tr( "WFS" ) )
|
||||
, mURI( uri )
|
||||
, mHideProgressDialog( mURI.hideDownloadProgressDialog() )
|
||||
{
|
||||
mHideProgressDialog = mURI.hideDownloadProgressDialog();
|
||||
}
|
||||
|
||||
QgsWFSSharedData::~QgsWFSSharedData()
|
||||
|
@ -80,9 +80,6 @@ class QgsWFSSharedData : public QObject, public QgsBackgroundCachedSharedData
|
||||
//! Server capabilities
|
||||
QgsWfsCapabilities::Capabilities mCaps;
|
||||
|
||||
//! Whether progress dialog should be hidden
|
||||
bool mHideProgressDialog = false;
|
||||
|
||||
//! If we have already issued a warning about missing feature ids
|
||||
bool mHasWarnedAboutMissingFeatureId = false;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user