[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 qgsvectordataprovider.sip
%Include qgsvectorfilewriter.sip
%Include qgsvectorfilewritertask.sip
%Include qgsvectorlayer.sip
%Include qgsvectorlayercache.sip
%Include qgsvectorlayereditbuffer.sip

View File

@ -78,14 +78,7 @@ class QgsTask : QObject
*/
double progress() const;
/**
* 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();
virtual void cancel();
/**
* Places the task on hold. If the task in not queued

View File

@ -97,6 +97,7 @@ class QgsVectorFileWriter
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
Canceled,
};
enum SymbologyExport
@ -323,6 +324,8 @@ class QgsVectorFileWriter
/** Field value converter */
QgsVectorFileWriter::FieldValueConverter* fieldValueConverter;
QgsFeedback* feedback;
};
/** 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 "qgsvectorlayerutils.h"
#include "qgshelp.h"
#include "qgsvectorfilewritertask.h"
#include "qgssublayersdialog.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();
QgisAppFieldValueConverter converter( vlayer, dialog->attributesAsDisplayedValues() );
QgisAppFieldValueConverter* converterPtr = nullptr;
@ -6510,32 +6505,41 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
options.attributes = dialog->selectedAttributes();
options.fieldValueConverter = converterPtr;
error = QgsVectorFileWriter::writeAsVectorFormat(
vlayer, vectorFilename, options, &newFilename, &errorMessage );
bool addToCanvas = dialog->addToCanvas();
QString layerName = dialog->layername();
QgsVectorFileWriterTask* writerTask = new QgsVectorFileWriterTask( vlayer, vectorFilename, options );
QApplication::restoreOverrideCursor();
if ( error == QgsVectorFileWriter::NoError )
// when writer is successful:
connect( writerTask, &QgsVectorFileWriterTask::writeComplete, this, [this, addToCanvas, layerName, encoding, vectorFilename, vlayer]( const QString& newFilename )
{
if ( dialog->addToCanvas() )
if ( addToCanvas )
{
QString uri( newFilename );
if ( !dialog->layername().isEmpty() )
uri += "|layername=" + dialog->layername();
addVectorLayers( QStringList( uri ), encoding, QStringLiteral( "file" ) );
if ( !layerName.isEmpty() )
uri += "|layername=" + layerName;
this->addVectorLayers( QStringList( uri ), encoding, QStringLiteral( "file" ) );
}
emit layerSavedAs( vlayer, vectorFilename );
messageBar()->pushMessage( tr( "Saving done" ),
tr( "Export to vector file has been completed" ),
QgsMessageBar::INFO, messageTimeout() );
this->emit layerSavedAs( vlayer, vectorFilename );
this->messageBar()->pushMessage( tr( "Saving done" ),
tr( "Export to vector file has been completed" ),
QgsMessageBar::INFO, messageTimeout() );
}
else
);
// when an error occurs:
connect( writerTask, &QgsVectorFileWriterTask::errorOccurred, this, [=]( int error, const QString & errorMessage )
{
QgsMessageViewer *m = new QgsMessageViewer( nullptr );
m->setWindowTitle( tr( "Save error" ) );
m->setMessageAsPlainText( tr( "Export to vector file failed.\nError: %1" ).arg( errorMessage ) );
m->exec();
if ( error != QgsVectorFileWriter::Canceled )
{
QgsMessageViewer *m = new QgsMessageViewer( nullptr );
m->setWindowTitle( tr( "Save error" ) );
m->setMessageAsPlainText( tr( "Export to vector file failed.\nError: %1" ).arg( errorMessage ) );
m->exec();
}
}
);
QgsApplication::taskManager()->addTask( writerTask );
}
delete dialog;

View File

@ -237,6 +237,7 @@ SET(QGIS_CORE_SRCS
qgsunittypes.cpp
qgsvectordataprovider.cpp
qgsvectorfilewriter.cpp
qgsvectorfilewritertask.cpp
qgsvectorlayer.cpp
qgsvectorlayercache.cpp
qgsvectorlayerdiagramprovider.cpp
@ -528,6 +529,7 @@ SET(QGIS_CORE_MOC_HDRS
qgstransactiongroup.h
qgsunittypes.h
qgsvectordataprovider.h
qgsvectorfilewritertask.h
qgsvectorlayercache.h
qgsvectorlayereditbuffer.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
* task subclasses can check and terminate their operations at an appropriate
* 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()
*/
void cancel();
virtual void cancel();
/**
* Places the task on hold. If the task in not queued

View File

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

View File

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