Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] Import layers to data providers in background
Flips the drag and drop import of layers to browser data sources
to use task manager, allowing these slow imports to occur
in the background.
  • Loading branch information
nyalldawson committed May 11, 2017
1 parent 384369c commit 64b612f
Show file tree
Hide file tree
Showing 12 changed files with 353 additions and 207 deletions.
63 changes: 63 additions & 0 deletions python/core/qgsvectorlayerexporter.sip
Expand Up @@ -130,6 +130,69 @@ class QgsVectorLayerExporter : QgsFeatureSink
QgsVectorLayerExporter( const QgsVectorLayerExporter &rh );
};


class QgsVectorLayerExporterTask : QgsTask
{
%Docstring
QgsTask task which performs a QgsVectorLayerExporter layer export operation as a background
task. This can be used to export a vector layer out to a provider without blocking the
QGIS interface.
.. versionadded:: 3.0
.. seealso:: QgsVectorFileWriterTask
.. seealso:: QgsRasterFileWriterTask
%End

%TypeHeaderCode
#include "qgsvectorlayerexporter.h"
%End
public:

QgsVectorLayerExporterTask( QgsVectorLayer *layer,
const QString &uri,
const QString &providerKey,
const QgsCoordinateReferenceSystem &destinationCrs,
QMap<QString, QVariant> *options = 0 );
%Docstring
Constructor for QgsVectorLayerExporterTask. Takes a source ``layer``, destination ``uri``
and ``providerKey``, and various export related parameters such as destination CRS
and export ``options``.
%End

static QgsVectorLayerExporterTask *withLayerOwnership( QgsVectorLayer *layer /Transfer/,
const QString &uri,
const QString &providerKey,
const QgsCoordinateReferenceSystem &destinationCrs,
QMap<QString, QVariant> *options = 0 ) /Factory/;
%Docstring
Creates a new QgsVectorLayerExporterTask which has ownership over a source ``layer``.
When the export task has completed (successfully or otherwise) ``layer`` will be
deleted. The destination ``uri`` and ``providerKey``, and various export related parameters such as destination CRS
and export ``options`` must be specified.
:rtype: QgsVectorLayerExporterTask
%End

virtual void cancel();

signals:

void exportComplete();
%Docstring
Emitted when exporting the layer is successfully completed.
%End

void errorOccurred( int error, const QString &errorMessage );
%Docstring
Emitted when an error occurs which prevented the layer being exported (or if
the task is canceled). The export ``error`` and ``errorMessage`` will be reported.
%End

protected:

virtual bool run();
virtual void finished( bool result );

};

/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgsrasterfilewritertask.sip
Expand Up @@ -16,6 +16,7 @@ class QgsRasterFileWriterTask : QgsTask
task. This can be used to save a raster layer out to a file without blocking the
QGIS interface.
.. seealso:: QgsVectorFileWriterTask
.. seealso:: QgsVectorFileExporterTask
.. versionadded:: 3.0
%End

Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -570,6 +570,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsvectorlayereditbuffer.h
qgsvectorlayereditpassthrough.h
qgsvectorlayer.h
qgsvectorlayerexporter.h
qgsvectorlayerfeaturecounter.h
qgsvectorlayerjoinbuffer.h
qgsvectorlayertools.h
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectorfilewritertask.h
Expand Up @@ -30,6 +30,7 @@
* task. This can be used to save a vector layer out to a file without blocking the
* QGIS interface.
* \since QGIS 3.0
* \see QgsVectorLayerExporterTask
* \see QgsRasterFileWriterTask
*/
class CORE_EXPORT QgsVectorFileWriterTask : public QgsTask
Expand Down
57 changes: 57 additions & 0 deletions src/core/qgsvectorlayerexporter.cpp
Expand Up @@ -463,3 +463,60 @@ QgsVectorLayerExporter::exportLayer( QgsVectorLayer *layer,

return NoError;
}


//
// QgsVectorLayerExporterTask
//

QgsVectorLayerExporterTask::QgsVectorLayerExporterTask( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, QMap<QString, QVariant> *options )
: QgsTask( tr( "Exporting %1" ).arg( layer->name() ), QgsTask::CanCancel )
, mLayer( layer )
, mDestUri( uri )
, mDestProviderKey( providerKey )
, mDestCrs( destinationCrs )
, mOptions( options ? * options : QMap<QString, QVariant>() )
, mOwnedFeedback( new QgsFeedback() )
{
if ( mLayer )
setDependentLayers( QList< QgsMapLayer * >() << mLayer );
}

QgsVectorLayerExporterTask *QgsVectorLayerExporterTask::withLayerOwnership( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, QMap<QString, QVariant> *options )
{
std::unique_ptr< QgsVectorLayerExporterTask > newTask( new QgsVectorLayerExporterTask( layer, uri, providerKey, destinationCrs, options ) );
newTask->mOwnsLayer = true;
return newTask.release();
}

void QgsVectorLayerExporterTask::cancel()
{
mOwnedFeedback->cancel();
QgsTask::cancel();
}

bool QgsVectorLayerExporterTask::run()
{
if ( !mLayer )
return false;

connect( mOwnedFeedback.get(), &QgsFeedback::progressChanged, this, &QgsVectorLayerExporterTask::setProgress );


mError = QgsVectorLayerExporter::exportLayer(
mLayer.data(), mDestUri, mDestProviderKey, mDestCrs, false, &mErrorMessage, false,
&mOptions );

if ( mOwnsLayer )
delete mLayer;

return mError == QgsVectorLayerExporter::NoError;
}

void QgsVectorLayerExporterTask::finished( bool result )
{
if ( result )
emit exportComplete();
else
emit errorOccurred( mError, mErrorMessage );
}
81 changes: 80 additions & 1 deletion src/core/qgsvectorlayerexporter.h
Expand Up @@ -22,10 +22,12 @@
#include "qgis.h"
#include "qgsfeature.h"
#include "qgsfeaturesink.h"
#include "qgstaskmanager.h"
#include "qgsfeedback.h"
#include "qgsvectorlayer.h"

class QProgressDialog;
class QgsVectorDataProvider;
class QgsVectorLayer;
class QgsFields;

/**
Expand Down Expand Up @@ -168,4 +170,81 @@ class CORE_EXPORT QgsVectorLayerExporter : public QgsFeatureSink

};


/**
* \class QgsVectorLayerExporterTask
* \ingroup core
* QgsTask task which performs a QgsVectorLayerExporter layer export operation as a background
* task. This can be used to export a vector layer out to a provider without blocking the
* QGIS interface.
* \since QGIS 3.0
* \see QgsVectorFileWriterTask
* \see QgsRasterFileWriterTask
*/
class CORE_EXPORT QgsVectorLayerExporterTask : public QgsTask
{
Q_OBJECT

public:

/**
* Constructor for QgsVectorLayerExporterTask. Takes a source \a layer, destination \a uri
* and \a providerKey, and various export related parameters such as destination CRS
* and export \a options.
*/
QgsVectorLayerExporterTask( QgsVectorLayer *layer,
const QString &uri,
const QString &providerKey,
const QgsCoordinateReferenceSystem &destinationCrs,
QMap<QString, QVariant> *options = nullptr );

/**
* Creates a new QgsVectorLayerExporterTask which has ownership over a source \a layer.
* When the export task has completed (successfully or otherwise) \a layer will be
* deleted. The destination \a uri and \a providerKey, and various export related parameters such as destination CRS
* and export \a options must be specified.
*/
static QgsVectorLayerExporterTask *withLayerOwnership( QgsVectorLayer *layer SIP_TRANSFER,
const QString &uri,
const QString &providerKey,
const QgsCoordinateReferenceSystem &destinationCrs,
QMap<QString, QVariant> *options = nullptr ) SIP_FACTORY;

virtual void cancel() override;

signals:

/**
* Emitted when exporting the layer is successfully completed.
*/
void exportComplete();

/**
* Emitted when an error occurs which prevented the layer being exported (or if
* the task is canceled). The export \a error and \a errorMessage will be reported.
*/
void errorOccurred( int error, const QString &errorMessage );

protected:

virtual bool run() override;
virtual void finished( bool result ) override;

private:

QPointer< QgsVectorLayer > mLayer = nullptr;
bool mOwnsLayer = false;

QString mDestUri;
QString mDestProviderKey;
QgsCoordinateReferenceSystem mDestCrs;
QMap<QString, QVariant> mOptions;

std::unique_ptr< QgsFeedback > mOwnedFeedback;

QgsVectorLayerExporter::ExportError mError = QgsVectorLayerExporter::NoError;
QString mErrorMessage;

};

#endif // QGSVECTORLAYEREXPORTER_H
1 change: 1 addition & 0 deletions src/core/raster/qgsrasterfilewritertask.h
Expand Up @@ -31,6 +31,7 @@
* task. This can be used to save a raster layer out to a file without blocking the
* QGIS interface.
* \see QgsVectorFileWriterTask
* \see QgsVectorFileExporterTask
* \since QGIS 3.0
*/
class CORE_EXPORT QgsRasterFileWriterTask : public QgsTask
Expand Down
80 changes: 32 additions & 48 deletions src/providers/db2/qgsdb2dataitems.cpp
Expand Up @@ -24,6 +24,7 @@
#include "qgsvectorlayerexporter.h"
#include "qgsvectorlayer.h"
#include "qgssettings.h"
#include "qgsmessageoutput.h"

#include <QMessageBox>
#include <QProgressDialog>
Expand Down Expand Up @@ -316,16 +317,8 @@ bool QgsDb2ConnectionItem::handleDrop( const QMimeData *data, const QString &toS
return false;

// TODO: probably should show a GUI with settings etc
qApp->setOverrideCursor( Qt::WaitCursor );

QProgressDialog *progress = new QProgressDialog( tr( "Copying features..." ), tr( "Abort" ), 0, 0, nullptr );
progress->setWindowTitle( tr( "Import layer" ) );
progress->setWindowModality( Qt::WindowModal );
progress->show();

QStringList importResults;
bool hasError = false;
bool canceled = false;

QgsMimeDataUtils::UriList lst = QgsMimeDataUtils::decodeUriList( data );
Q_FOREACH ( const QgsMimeDataUtils::Uri &u, lst )
Expand Down Expand Up @@ -357,60 +350,51 @@ bool QgsDb2ConnectionItem::handleDrop( const QMimeData *data, const QString &toS
if ( srcLayer->geometryType() != QgsWkbTypes::NullGeometry )
uri += QLatin1String( " (geom)" );

QgsVectorLayerExporter::ExportError err;
QString importError;
err = QgsVectorLayerExporter::exportLayer( srcLayer, uri, QStringLiteral( "DB2" ), srcLayer->crs(), false, &importError, false, nullptr, progress );
if ( err == QgsVectorLayerExporter::NoError )
std::unique_ptr< QgsVectorLayerExporterTask > exportTask( QgsVectorLayerExporterTask::withLayerOwnership( srcLayer, uri, QStringLiteral( "DB2" ), srcLayer->crs() ) );

// when export is successful:
connect( exportTask.get(), &QgsVectorLayerExporterTask::exportComplete, this, [ = ]()
{
importResults.append( tr( "%1: OK!" ).arg( u.name ) );
QgsDebugMsg( "import successful" );
}
else
// this is gross - TODO - find a way to get access to messageBar from data items
QMessageBox::information( nullptr, tr( "Import to DB2 database" ), tr( "Import was successful." ) );
if ( state() == Populated )
refresh();
else
populate();
} );

// when an error occurs:
connect( exportTask.get(), &QgsVectorLayerExporterTask::errorOccurred, this, [ = ]( int error, const QString & errorMessage )
{
if ( err == QgsVectorLayerExporter::ErrUserCanceled )
if ( error != QgsVectorLayerExporter::ErrUserCanceled )
{
canceled = true;
QgsDebugMsg( "import canceled" );
QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
output->setTitle( tr( "Import to DB2 database" ) );
output->setMessage( tr( "Failed to import some layers!\n\n" ) + errorMessage, QgsMessageOutput::MessageText );
output->showMessage();
}
if ( state() == Populated )
refresh();
else
{
QString errMsg = QStringLiteral( "%1: %2" ).arg( u.name, importError );
QgsDebugMsg( "import failed: " + errMsg );
importResults.append( errMsg );
hasError = true;
}
}
populate();
} );

QgsApplication::taskManager()->addTask( exportTask.release() );
}
else
{
importResults.append( tr( "%1: OK!" ).arg( u.name ) );
importResults.append( tr( "%1: Not a valid layer!" ).arg( u.name ) );
hasError = true;
}

delete srcLayer;
}

delete progress;
qApp->restoreOverrideCursor();

if ( canceled )
{
QMessageBox::information( nullptr, tr( "Import to DB2 database" ), tr( "Import canceled." ) );
refresh();
}
else if ( hasError )
if ( hasError )
{
QMessageBox::warning( nullptr, tr( "Import to DB2 database" ), tr( "Failed to import some layers!\n\n" ) + importResults.join( QStringLiteral( "\n" ) ) );
QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
output->setTitle( tr( "Import to DB2 database" ) );
output->setMessage( tr( "Failed to import some layers!\n\n" ) + importResults.join( QStringLiteral( "\n" ) ), QgsMessageOutput::MessageText );
output->showMessage();
}
else
{
QMessageBox::information( nullptr, tr( "Import to DB2 database" ), tr( "Import was successful." ) );
}

if ( state() == Populated )
refresh();
else
populate();

return true;
}
Expand Down

0 comments on commit 64b612f

Please sign in to comment.