[WFS provider] Extract reusable logic for progress dialog

Funded by Planet
This commit is contained in:
Even Rouault 2019-10-15 12:32:25 +02:00 committed by Nyall Dawson
parent 3d2f9f1aab
commit e12c5522e2
9 changed files with 285 additions and 240 deletions

View File

@ -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();

View File

@ -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().

View File

@ -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;

View File

@ -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 )

View File

@ -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;
};

View File

@ -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

View File

@ -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;
};

View File

@ -24,8 +24,8 @@
QgsWFSSharedData::QgsWFSSharedData( const QString &uri )
: QgsBackgroundCachedSharedData( "wfs", tr( "WFS" ) )
, mURI( uri )
, mHideProgressDialog( mURI.hideDownloadProgressDialog() )
{
mHideProgressDialog = mURI.hideDownloadProgressDialog();
}
QgsWFSSharedData::~QgsWFSSharedData()

View File

@ -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;