[FEATURE] Background saving of vector layers

Switches the vector layer save operation to a background task
using the task manager framework. Allows layers to saved without
blocking the interface.
This commit is contained in:
Nyall Dawson 2017-02-11 14:41:21 +10:00
parent 92091c536b
commit 95d000f662
11 changed files with 220 additions and 33 deletions

View File

@ -149,6 +149,7 @@
%Include qgsunittypes.sip %Include qgsunittypes.sip
%Include qgsvectordataprovider.sip %Include qgsvectordataprovider.sip
%Include qgsvectorfilewriter.sip %Include qgsvectorfilewriter.sip
%Include qgsvectorfilewritertask.sip
%Include qgsvectorlayer.sip %Include qgsvectorlayer.sip
%Include qgsvectorlayercache.sip %Include qgsvectorlayercache.sip
%Include qgsvectorlayereditbuffer.sip %Include qgsvectorlayereditbuffer.sip

View File

@ -78,14 +78,7 @@ class QgsTask : QObject
*/ */
double progress() const; double progress() const;
/** virtual void cancel();
* Notifies the task that it should terminate. Calling this is not guaranteed
* to immediately end the task, rather it sets the isCanceled() flag which
* task subclasses can check and terminate their operations at an appropriate
* time. Any subtasks owned by this task will also be canceled.
* @see isCanceled()
*/
void cancel();
/** /**
* Places the task on hold. If the task in not queued * Places the task on hold. If the task in not queued

View File

@ -97,6 +97,7 @@ class QgsVectorFileWriter
ErrProjection, ErrProjection,
ErrFeatureWriteFailed, ErrFeatureWriteFailed,
ErrInvalidLayer, ErrInvalidLayer,
Canceled,
}; };
enum SymbologyExport enum SymbologyExport
@ -323,6 +324,8 @@ class QgsVectorFileWriter
/** Field value converter */ /** Field value converter */
QgsVectorFileWriter::FieldValueConverter* fieldValueConverter; QgsVectorFileWriter::FieldValueConverter* fieldValueConverter;
QgsFeedback* feedback;
}; };
/** Writes a layer out to a vector file. /** Writes a layer out to a vector file.

View File

@ -0,0 +1,25 @@
class QgsVectorFileWriterTask : QgsTask
{
%TypeHeaderCode
#include <qgsvectorfilewritertask.h>
%End
public:
QgsVectorFileWriterTask( QgsVectorLayer* layer,
const QString& fileName,
const QgsVectorFileWriter::SaveVectorOptions& options );
virtual void cancel();
signals:
void writeComplete( const QString& newFilename );
void errorOccurred( int error, const QString& errorMessage );
protected:
virtual bool run();
virtual void finished( bool result );
};

View File

@ -250,6 +250,7 @@
#include "qgsvectorlayerjoininfo.h" #include "qgsvectorlayerjoininfo.h"
#include "qgsvectorlayerutils.h" #include "qgsvectorlayerutils.h"
#include "qgshelp.h" #include "qgshelp.h"
#include "qgsvectorfilewritertask.h"
#include "qgssublayersdialog.h" #include "qgssublayersdialog.h"
#include "ogr/qgsopenvectorlayerdialog.h" #include "ogr/qgsopenvectorlayerdialog.h"
@ -6477,12 +6478,6 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
} }
} }
// ok if the file existed it should be deleted now so we can continue...
QApplication::setOverrideCursor( Qt::WaitCursor );
QgsVectorFileWriter::WriterError error;
QString errorMessage;
QString newFilename;
QgsRectangle filterExtent = dialog->filterExtent(); QgsRectangle filterExtent = dialog->filterExtent();
QgisAppFieldValueConverter converter( vlayer, dialog->attributesAsDisplayedValues() ); QgisAppFieldValueConverter converter( vlayer, dialog->attributesAsDisplayedValues() );
QgisAppFieldValueConverter* converterPtr = nullptr; QgisAppFieldValueConverter* converterPtr = nullptr;
@ -6510,26 +6505,31 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
options.attributes = dialog->selectedAttributes(); options.attributes = dialog->selectedAttributes();
options.fieldValueConverter = converterPtr; options.fieldValueConverter = converterPtr;
error = QgsVectorFileWriter::writeAsVectorFormat( bool addToCanvas = dialog->addToCanvas();
vlayer, vectorFilename, options, &newFilename, &errorMessage ); QString layerName = dialog->layername();
QgsVectorFileWriterTask* writerTask = new QgsVectorFileWriterTask( vlayer, vectorFilename, options );
QApplication::restoreOverrideCursor(); // when writer is successful:
connect( writerTask, &QgsVectorFileWriterTask::writeComplete, this, [this, addToCanvas, layerName, encoding, vectorFilename, vlayer]( const QString& newFilename )
if ( error == QgsVectorFileWriter::NoError )
{ {
if ( dialog->addToCanvas() ) if ( addToCanvas )
{ {
QString uri( newFilename ); QString uri( newFilename );
if ( !dialog->layername().isEmpty() ) if ( !layerName.isEmpty() )
uri += "|layername=" + dialog->layername(); uri += "|layername=" + layerName;
addVectorLayers( QStringList( uri ), encoding, QStringLiteral( "file" ) ); this->addVectorLayers( QStringList( uri ), encoding, QStringLiteral( "file" ) );
} }
emit layerSavedAs( vlayer, vectorFilename ); this->emit layerSavedAs( vlayer, vectorFilename );
messageBar()->pushMessage( tr( "Saving done" ), this->messageBar()->pushMessage( tr( "Saving done" ),
tr( "Export to vector file has been completed" ), tr( "Export to vector file has been completed" ),
QgsMessageBar::INFO, messageTimeout() ); QgsMessageBar::INFO, messageTimeout() );
} }
else );
// when an error occurs:
connect( writerTask, &QgsVectorFileWriterTask::errorOccurred, this, [=]( int error, const QString & errorMessage )
{
if ( error != QgsVectorFileWriter::Canceled )
{ {
QgsMessageViewer *m = new QgsMessageViewer( nullptr ); QgsMessageViewer *m = new QgsMessageViewer( nullptr );
m->setWindowTitle( tr( "Save error" ) ); m->setWindowTitle( tr( "Save error" ) );
@ -6537,6 +6537,10 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
m->exec(); m->exec();
} }
} }
);
QgsApplication::taskManager()->addTask( writerTask );
}
delete dialog; delete dialog;
} }

View File

@ -237,6 +237,7 @@ SET(QGIS_CORE_SRCS
qgsunittypes.cpp qgsunittypes.cpp
qgsvectordataprovider.cpp qgsvectordataprovider.cpp
qgsvectorfilewriter.cpp qgsvectorfilewriter.cpp
qgsvectorfilewritertask.cpp
qgsvectorlayer.cpp qgsvectorlayer.cpp
qgsvectorlayercache.cpp qgsvectorlayercache.cpp
qgsvectorlayerdiagramprovider.cpp qgsvectorlayerdiagramprovider.cpp
@ -528,6 +529,7 @@ SET(QGIS_CORE_MOC_HDRS
qgstransactiongroup.h qgstransactiongroup.h
qgsunittypes.h qgsunittypes.h
qgsvectordataprovider.h qgsvectordataprovider.h
qgsvectorfilewritertask.h
qgsvectorlayercache.h qgsvectorlayercache.h
qgsvectorlayereditbuffer.h qgsvectorlayereditbuffer.h
qgsvectorlayereditpassthrough.h qgsvectorlayereditpassthrough.h

View File

@ -116,9 +116,11 @@ class CORE_EXPORT QgsTask : public QObject
* to immediately end the task, rather it sets the isCanceled() flag which * to immediately end the task, rather it sets the isCanceled() flag which
* task subclasses can check and terminate their operations at an appropriate * task subclasses can check and terminate their operations at an appropriate
* time. Any subtasks owned by this task will also be canceled. * time. Any subtasks owned by this task will also be canceled.
* Derived classes must ensure that the base class implementation is called
* from any overridden version.
* @see isCanceled() * @see isCanceled()
*/ */
void cancel(); virtual void cancel();
/** /**
* Places the task on hold. If the task in not queued * Places the task on hold. If the task in not queued

View File

@ -2426,6 +2426,12 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
// write all features // write all features
while ( fit.nextFeature( fet ) ) while ( fit.nextFeature( fet ) )
{ {
if ( options.feedback && options.feedback->isCanceled() )
{
delete writer;
return Canceled;
}
if ( shallTransform ) if ( shallTransform )
{ {
try try
@ -3085,3 +3091,4 @@ bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString& datasetName,
OGR_DS_Destroy( hDS ); OGR_DS_Destroy( hDS );
return ret; return ret;
} }

View File

@ -21,7 +21,10 @@
#include "qgis_core.h" #include "qgis_core.h"
#include "qgsfields.h" #include "qgsfields.h"
#include "qgsfeedback.h"
#include "qgssymbol.h" #include "qgssymbol.h"
#include "qgstaskmanager.h"
#include "qgsvectorlayer.h"
#include <ogr_api.h> #include <ogr_api.h>
#include <QPair> #include <QPair>
@ -164,6 +167,7 @@ class CORE_EXPORT QgsVectorFileWriter
ErrProjection, ErrProjection,
ErrFeatureWriteFailed, ErrFeatureWriteFailed,
ErrInvalidLayer, ErrInvalidLayer,
Canceled, //!< Writing was interrupted by manual cancelation
}; };
enum SymbologyExport enum SymbologyExport
@ -391,6 +395,9 @@ class CORE_EXPORT QgsVectorFileWriter
//! Field value converter //! Field value converter
FieldValueConverter* fieldValueConverter; FieldValueConverter* fieldValueConverter;
//! Optional feedback object allowing cancelation of layer save
QgsFeedback* feedback = nullptr;
}; };
/** Writes a layer out to a vector file. /** Writes a layer out to a vector file.
@ -616,4 +623,5 @@ class CORE_EXPORT QgsVectorFileWriter
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities ) Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities )
#endif #endif

View File

@ -0,0 +1,57 @@
/***************************************************************************
qgsvectorfilewritertask.cpp
---------------------------
begin : Feb 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail 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 "qgsvectorfilewritertask.h"
QgsVectorFileWriterTask::QgsVectorFileWriterTask( QgsVectorLayer* layer, const QString& fileName, const QgsVectorFileWriter::SaveVectorOptions& options )
: QgsTask( tr( "Saving %1 " ).arg( fileName ), QgsTask::CanCancel )
, mLayer( layer )
, mDestFileName( fileName )
, mOptions( options )
{
if ( !mOptions.feedback )
{
mOwnedFeedback.reset( new QgsFeedback() );
mOptions.feedback = mOwnedFeedback.get();
}
}
void QgsVectorFileWriterTask::cancel()
{
mOptions.feedback->cancel();
QgsTask::cancel();
}
bool QgsVectorFileWriterTask::run()
{
if ( !mLayer )
return false;
mError = QgsVectorFileWriter::writeAsVectorFormat(
mLayer, mDestFileName, mOptions, &mNewFilename, &mErrorMessage );
return mError == QgsVectorFileWriter::NoError;
}
void QgsVectorFileWriterTask::finished( bool result )
{
if ( result )
emit writeComplete( mNewFilename );
else
emit errorOccurred( mError, mErrorMessage );
}

View File

@ -0,0 +1,85 @@
/***************************************************************************
qgsvectorfilewritertask.h
-------------------------
begin : Feb 2017
copyright : (C) 2017 by Nyall Dawson
email : nyall dot dawson at gmail 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. *
* *
***************************************************************************/
#ifndef QGSVECTORFILEWRITERTASK_H
#define QGSVECTORFILEWRITERTASK_H
#include "qgis_core.h"
#include "qgsvectorfilewriter.h"
#include "qgstaskmanager.h"
#include "qgsvectorlayer.h"
/**
* \class QgsVectorFileWriterTask
* \ingroup core
* QgsTask task which performs a QgsVectorFileWriter layer saving operation as a background
* task. This can be used to save a vector layer out to a file without blocking the
* QGIS interface.
* \note Added in QGIS 3.0
*/
class CORE_EXPORT QgsVectorFileWriterTask : public QgsTask
{
Q_OBJECT
public:
/**
* Constructor for QgsVectorFileWriterTask. Takes a source \a layer, destination \a fileName
* and save \a options.
*/
QgsVectorFileWriterTask( QgsVectorLayer* layer,
const QString& fileName,
const QgsVectorFileWriter::SaveVectorOptions& options );
virtual void cancel() override;
signals:
/**
* Emitted when writing the layer is successfully completed. The \a newFilename
* parameter indicates the file path for the written file.
*/
void writeComplete( const QString& newFilename );
/**
* Emitted when an error occurs which prevented the file being written (or if
* the task is canceled). The writing \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;
QString mDestFileName;
std::unique_ptr< QgsFeedback > mOwnedFeedback;
QgsVectorFileWriter::WriterError mError = QgsVectorFileWriter::NoError;
QString mNewFilename;
QString mErrorMessage;
QgsVectorFileWriter::SaveVectorOptions mOptions;
};
#endif