Skip to content

Commit

Permalink
[FEATURE] Background saving of vector layers
Browse files Browse the repository at this point in the history
Switches the vector layer save operation to a background task
using the task manager framework. Allows layers to saved without
blocking the interface.
  • Loading branch information
nyalldawson committed Feb 12, 2017
1 parent 92091c5 commit 95d000f
Show file tree
Hide file tree
Showing 11 changed files with 220 additions and 33 deletions.
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -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
Expand Down
9 changes: 1 addition & 8 deletions python/core/qgstaskmanager.sip
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions python/core/qgsvectorfilewriter.sip
Expand Up @@ -97,6 +97,7 @@ class QgsVectorFileWriter
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
Canceled,
};

enum SymbologyExport
Expand Down Expand Up @@ -323,6 +324,8 @@ class QgsVectorFileWriter

/** Field value converter */
QgsVectorFileWriter::FieldValueConverter* fieldValueConverter;

QgsFeedback* feedback;
};

/** Writes a layer out to a vector file.
Expand Down
25 changes: 25 additions & 0 deletions python/core/qgsvectorfilewritertask.sip
@@ -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 );
};

52 changes: 28 additions & 24 deletions src/app/qgisapp.cpp
Expand Up @@ -250,6 +250,7 @@
#include "qgsvectorlayerjoininfo.h"
#include "qgsvectorlayerutils.h"
#include "qgshelp.h"
#include "qgsvectorfilewritertask.h"

#include "qgssublayersdialog.h"
#include "ogr/qgsopenvectorlayerdialog.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -237,6 +237,7 @@ SET(QGIS_CORE_SRCS
qgsunittypes.cpp
qgsvectordataprovider.cpp
qgsvectorfilewriter.cpp
qgsvectorfilewritertask.cpp
qgsvectorlayer.cpp
qgsvectorlayercache.cpp
qgsvectorlayerdiagramprovider.cpp
Expand Down Expand Up @@ -528,6 +529,7 @@ SET(QGIS_CORE_MOC_HDRS
qgstransactiongroup.h
qgsunittypes.h
qgsvectordataprovider.h
qgsvectorfilewritertask.h
qgsvectorlayercache.h
qgsvectorlayereditbuffer.h
qgsvectorlayereditpassthrough.h
Expand Down
4 changes: 3 additions & 1 deletion src/core/qgstaskmanager.h
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/core/qgsvectorfilewriter.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -3085,3 +3091,4 @@ bool QgsVectorFileWriter::areThereNewFieldsToCreate( const QString& datasetName,
OGR_DS_Destroy( hDS );
return ret;
}

8 changes: 8 additions & 0 deletions src/core/qgsvectorfilewriter.h
Expand Up @@ -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>
Expand Down Expand Up @@ -164,6 +167,7 @@ class CORE_EXPORT QgsVectorFileWriter
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
Canceled, //!< Writing was interrupted by manual cancelation
};

enum SymbologyExport
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -616,4 +623,5 @@ class CORE_EXPORT QgsVectorFileWriter

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities )


#endif
57 changes: 57 additions & 0 deletions src/core/qgsvectorfilewritertask.cpp
@@ -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 );
}
85 changes: 85 additions & 0 deletions src/core/qgsvectorfilewritertask.h
@@ -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

0 comments on commit 95d000f

Please sign in to comment.