Skip to content

Commit

Permalink
Use a QgsProject pointer instead of bool loadIntoProject
Browse files Browse the repository at this point in the history
Allows potential future use case of loading results into
a different open project
  • Loading branch information
nyalldawson committed Jun 5, 2017
1 parent 72be86d commit 81da209
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 52 deletions.
34 changes: 27 additions & 7 deletions python/core/processing/qgsprocessingcontext.sip
Expand Up @@ -83,25 +83,45 @@ class QgsProcessingContext
:rtype: QgsMapLayerStore
%End

QgsStringMap layersToLoadOnCompletion() const;
struct LayerDetails
{

LayerDetails( const QString &name, QgsProject *project );
%Docstring
Constructor for LayerDetails.
%End

QString name;
%Docstring
Friendly name for layer, to use when loading layer into project.
%End

QgsProject *project;
%Docstring
Destination project
%End

};

QMap< QString, QgsProcessingContext::LayerDetails > layersToLoadOnCompletion() const;
%Docstring
Returns a map of layers (by ID or datasource) to friendly layer name, to load into the canvas upon completion of the algorithm or model.
Returns a map of layers (by ID or datasource) to LayerDetails, to load into the canvas upon completion of the algorithm or model.
.. seealso:: setLayersToLoadOnCompletion()
.. seealso:: addLayerToLoadOnCompletion()
:rtype: QgsStringMap
:rtype: QMap< str, QgsProcessingContext.LayerDetails >
%End

void setLayersToLoadOnCompletion( const QgsStringMap &layers );
void setLayersToLoadOnCompletion( const QMap< QString, QgsProcessingContext::LayerDetails > &layers );
%Docstring
Sets the map of ``layers`` (by ID or datasource) to friendly layer name, to load into the canvas upon completion of the algorithm or model.
Sets the map of ``layers`` (by ID or datasource) to LayerDetails, to load into the canvas upon completion of the algorithm or model.
.. seealso:: addLayerToLoadOnCompletion()
.. seealso:: layersToLoadOnCompletion()
%End

void addLayerToLoadOnCompletion( const QString &layer, const QString &name );
void addLayerToLoadOnCompletion( const QString &layer, const QgsProcessingContext::LayerDetails &details );
%Docstring
Adds a ``layer`` to load (by ID or datasource) into the canvas upon completion of the algorithm or model.
The ``name`` parameter dictates a friendly display name for the layer.
The ``details`` parameter dictates the LayerDetails.
.. seealso:: setLayersToLoadOnCompletion()
.. seealso:: layersToLoadOnCompletion()
%End
Expand Down
14 changes: 10 additions & 4 deletions python/core/processing/qgsprocessingparameters.sip
Expand Up @@ -70,24 +70,30 @@ class QgsProcessingFeatureSink
%End
public:

QgsProcessingFeatureSink( const QString &sink = QString(), bool loadIntoProject = false );
QgsProcessingFeatureSink( const QString &sink = QString(), QgsProject *destinationProject = 0 );
%Docstring
Constructor for QgsProcessingFeatureSink, accepting a static string sink.
The ``destinationProject`` parameter can be set to a QgsProject instance in which
to automatically load the resulting sink after completing processing.
%End

QgsProcessingFeatureSink( const QgsProperty &sink, bool loadIntoProject = false );
QgsProcessingFeatureSink( const QgsProperty &sink, QgsProject *destinationProject = 0 );
%Docstring
Constructor for QgsProcessingFeatureSink, accepting a QgsProperty sink.
The ``destinationProject`` parameter can be set to a QgsProject instance in which
to automatically load the resulting sink after completing processing.
%End

QgsProperty sink;
%Docstring
Sink definition. Usually a static property set to the destination file name for the sink's layer.
%End

bool loadIntoProject;
QgsProject *destinationProject;
%Docstring
True if sink should be loaded into the current project when the algorithm completes.
Destination project. Can be set to a QgsProject instance in which
to automatically load the resulting sink after completing processing.
The default behavior is not to load the result into any project (None).
%End

QVariantMap createOptions;
Expand Down
6 changes: 3 additions & 3 deletions python/plugins/processing/gui/AlgorithmDialog.py
Expand Up @@ -110,13 +110,13 @@ def getParamValues(self):
if not param.checkValueIsAcceptable(value):
raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget)
else:
open_after_run = False
dest_project = None
if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
isinstance(param, (OutputRaster, QgsProcessingParameterFeatureSink, OutputTable)):
if self.mainWidget.checkBoxes[param.name()].isChecked():
open_after_run = True
dest_project = QgsProject.instance()

parameters[param.name()] = QgsProcessingFeatureSink(self.mainWidget.outputWidgets[param.name()].getValue(), open_after_run)
parameters[param.name()] = QgsProcessingFeatureSink(self.mainWidget.outputWidgets[param.name()].getValue(), dest_project)

return parameters

Expand Down
7 changes: 4 additions & 3 deletions python/plugins/processing/gui/Postprocessing.py
Expand Up @@ -55,14 +55,15 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
feedback = QgsProcessingFeedback()
feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
i = 0
for l, name in context.layersToLoadOnCompletion().items():
for l, details in context.layersToLoadOnCompletion().items():
feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
try:
layer = QgsProcessingUtils.mapLayerFromString(l, context)
if layer:
layer.setName(name)
QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
layer.setName(details.name)
details.project.addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
else:
name = details.name
if ProcessingConfig.getSetting(
ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
name = os.path.basename(l)
Expand Down
36 changes: 28 additions & 8 deletions src/core/processing/qgsprocessingcontext.h
Expand Up @@ -101,35 +101,55 @@ class CORE_EXPORT QgsProcessingContext
*/
QgsMapLayerStore *temporaryLayerStore() { return &tempLayerStore; }

//! Details for layers to load into projects.
struct LayerDetails
{

/**
* Constructor for LayerDetails.
*/
LayerDetails( const QString &name, QgsProject *project )
: name( name )
, project( project )
{}

//! Friendly name for layer, to use when loading layer into project.
QString name;

//! Destination project
QgsProject *project;

};

/**
* Returns a map of layers (by ID or datasource) to friendly layer name, to load into the canvas upon completion of the algorithm or model.
* Returns a map of layers (by ID or datasource) to LayerDetails, to load into the canvas upon completion of the algorithm or model.
* \see setLayersToLoadOnCompletion()
* \see addLayerToLoadOnCompletion()
*/
QgsStringMap layersToLoadOnCompletion() const
QMap< QString, QgsProcessingContext::LayerDetails > layersToLoadOnCompletion() const
{
return mLayersToLoadOnCompletion;
}

/**
* Sets the map of \a layers (by ID or datasource) to friendly layer name, to load into the canvas upon completion of the algorithm or model.
* Sets the map of \a layers (by ID or datasource) to LayerDetails, to load into the canvas upon completion of the algorithm or model.
* \see addLayerToLoadOnCompletion()
* \see layersToLoadOnCompletion()
*/
void setLayersToLoadOnCompletion( const QgsStringMap &layers )
void setLayersToLoadOnCompletion( const QMap< QString, QgsProcessingContext::LayerDetails > &layers )
{
mLayersToLoadOnCompletion = layers;
}

/**
* Adds a \a layer to load (by ID or datasource) into the canvas upon completion of the algorithm or model.
* The \a name parameter dictates a friendly display name for the layer.
* The \a details parameter dictates the LayerDetails.
* \see setLayersToLoadOnCompletion()
* \see layersToLoadOnCompletion()
*/
void addLayerToLoadOnCompletion( const QString &layer, const QString &name )
void addLayerToLoadOnCompletion( const QString &layer, const QgsProcessingContext::LayerDetails &details )
{
mLayersToLoadOnCompletion.insert( layer, name );
mLayersToLoadOnCompletion.insert( layer, details );
}

/**
Expand Down Expand Up @@ -209,7 +229,7 @@ class CORE_EXPORT QgsProcessingContext
QgsFeatureRequest::InvalidGeometryCheck mInvalidGeometryCheck = QgsFeatureRequest::GeometryNoCheck;
std::function< void( const QgsFeature & ) > mInvalidGeometryCallback;
QString mDefaultEncoding;
QgsStringMap mLayersToLoadOnCompletion;
QMap< QString, LayerDetails > mLayersToLoadOnCompletion;

#ifdef SIP_RUN
QgsProcessingContext( const QgsProcessingContext &other );
Expand Down
8 changes: 4 additions & 4 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -213,13 +213,13 @@ QgsFeatureSink *QgsProcessingParameters::parameterAsSink( const QgsProcessingPar
val = parameters.value( definition->name() );
}

bool loadIntoProject = false;
QgsProject *destinationProject = nullptr;
QVariantMap createOptions;
if ( val.canConvert<QgsProcessingFeatureSink>() )
{
// input is a QgsProcessingFeatureSink - get extra properties from it
QgsProcessingFeatureSink fromVar = qvariant_cast<QgsProcessingFeatureSink>( val );
loadIntoProject = fromVar.loadIntoProject;
destinationProject = fromVar.destinationProject;
createOptions = fromVar.createOptions;
val = fromVar.sink;
}
Expand All @@ -242,8 +242,8 @@ QgsFeatureSink *QgsProcessingParameters::parameterAsSink( const QgsProcessingPar
std::unique_ptr< QgsFeatureSink > sink( QgsProcessingUtils::createFeatureSink( dest, context, fields, geometryType, crs, createOptions ) );
destinationIdentifier = dest;

if ( loadIntoProject )
context.addLayerToLoadOnCompletion( destinationIdentifier, definition ? definition->description() : QString() );
if ( destinationProject )
context.addLayerToLoadOnCompletion( destinationIdentifier, QgsProcessingContext::LayerDetails( definition ? definition->description() : QString(), destinationProject ) );

return sink.release();
}
Expand Down
18 changes: 12 additions & 6 deletions src/core/processing/qgsprocessingparameters.h
Expand Up @@ -97,18 +97,22 @@ class CORE_EXPORT QgsProcessingFeatureSink

/**
* Constructor for QgsProcessingFeatureSink, accepting a static string sink.
* The \a destinationProject parameter can be set to a QgsProject instance in which
* to automatically load the resulting sink after completing processing.
*/
QgsProcessingFeatureSink( const QString &sink = QString(), bool loadIntoProject = false )
QgsProcessingFeatureSink( const QString &sink = QString(), QgsProject *destinationProject = nullptr )
: sink( QgsProperty::fromValue( sink ) )
, loadIntoProject( loadIntoProject )
, destinationProject( destinationProject )
{}

/**
* Constructor for QgsProcessingFeatureSink, accepting a QgsProperty sink.
* The \a destinationProject parameter can be set to a QgsProject instance in which
* to automatically load the resulting sink after completing processing.
*/
QgsProcessingFeatureSink( const QgsProperty &sink, bool loadIntoProject = false )
QgsProcessingFeatureSink( const QgsProperty &sink, QgsProject *destinationProject = nullptr )
: sink( sink )
, loadIntoProject( loadIntoProject )
, destinationProject( destinationProject )
{}

/**
Expand All @@ -117,9 +121,11 @@ class CORE_EXPORT QgsProcessingFeatureSink
QgsProperty sink;

/**
* True if sink should be loaded into the current project when the algorithm completes.
* Destination project. Can be set to a QgsProject instance in which
* to automatically load the resulting sink after completing processing.
* The default behavior is not to load the result into any project (nullptr).
*/
bool loadIntoProject;
QgsProject *destinationProject;

/**
* Map of optional sink creation options, which
Expand Down
35 changes: 18 additions & 17 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -467,25 +467,25 @@ void TestQgsProcessing::context()
QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V1", "memory" );
QgsVectorLayer *v2 = new QgsVectorLayer( "Polygon", "V2", "memory" );
QVERIFY( context.layersToLoadOnCompletion().isEmpty() );
QgsStringMap layers;
layers.insert( v1->id(), QStringLiteral( "v1" ) );
QMap< QString, QgsProcessingContext::LayerDetails > layers;
layers.insert( v1->id(), QgsProcessingContext::LayerDetails( QStringLiteral( "v1" ), &p ) );
context.setLayersToLoadOnCompletion( layers );
QCOMPARE( context.layersToLoadOnCompletion().count(), 1 );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ), QStringLiteral( "v1" ) );
context.addLayerToLoadOnCompletion( v2->id(), QStringLiteral( "v2" ) );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "v1" ) );
context.addLayerToLoadOnCompletion( v2->id(), QgsProcessingContext::LayerDetails( QStringLiteral( "v2" ), &p ) );
QCOMPARE( context.layersToLoadOnCompletion().count(), 2 );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ), QStringLiteral( "v1" ) );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "v1" ) );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 1 ), v2->id() );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 1 ), QStringLiteral( "v2" ) );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 1 ).name, QStringLiteral( "v2" ) );
layers.clear();
layers.insert( v2->id(), QStringLiteral( "v2" ) );
layers.insert( v2->id(), QgsProcessingContext::LayerDetails( QStringLiteral( "v2" ), &p ) );
context.setLayersToLoadOnCompletion( layers );
QCOMPARE( context.layersToLoadOnCompletion().count(), 1 );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v2->id() );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ), QStringLiteral( "v2" ) );
context.addLayerToLoadOnCompletion( v1->id(), QString() );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "v2" ) );
context.addLayerToLoadOnCompletion( v1->id(), QgsProcessingContext::LayerDetails( QString(), &p ) );
QCOMPARE( context.layersToLoadOnCompletion().count(), 2 );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 1 ), v2->id() );
Expand Down Expand Up @@ -1148,8 +1148,9 @@ void TestQgsProcessing::parameters()
QCOMPARE( layer->crs(), crs );

// QgsProcessingFeatureSink as parameter
QgsProject p;
QgsProcessingFeatureSink fs( QStringLiteral( "test.shp" ) );
fs.loadIntoProject = true;
fs.destinationProject = &p;
QVERIFY( context.layersToLoadOnCompletion().isEmpty() );
params.insert( QStringLiteral( "fs" ), QVariant::fromValue( fs ) );
def->setName( QStringLiteral( "fs" ) );
Expand All @@ -1167,7 +1168,7 @@ void TestQgsProcessing::parameters()
// make sure layer was automatically added to list to load on completion
QCOMPARE( context.layersToLoadOnCompletion().size(), 1 );
QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), destId );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ), QStringLiteral( "desc" ) );
QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "desc" ) );

delete def;
}
Expand Down Expand Up @@ -2376,27 +2377,27 @@ void TestQgsProcessing::processingFeatureSource()
void TestQgsProcessing::processingFeatureSink()
{
QString sinkString( QStringLiteral( "test.shp" ) );
QgsProcessingFeatureSink fs( sinkString, true );
QgsProject p;
QgsProcessingFeatureSink fs( sinkString, &p );
QCOMPARE( fs.sink.staticValue().toString(), sinkString );
QVERIFY( fs.loadIntoProject );
QCOMPARE( fs.destinationProject, &p );

// test storing QgsProcessingFeatureSink in variant and retrieving
QVariant fsInVariant = QVariant::fromValue( fs );
QVERIFY( fsInVariant.isValid() );

QgsProcessingFeatureSink fromVar = qvariant_cast<QgsProcessingFeatureSink>( fsInVariant );
QCOMPARE( fromVar.sink.staticValue().toString(), sinkString );
QVERIFY( fromVar.loadIntoProject );
QCOMPARE( fromVar.destinationProject, &p );

// test evaluating parameter as sink
QgsProject p;
QgsProcessingContext context;
context.setProject( &p );

// first using static string definition
QgsProcessingParameterDefinition *def = new QgsProcessingParameterString( QStringLiteral( "layer" ) );
QVariantMap params;
params.insert( QStringLiteral( "layer" ), QgsProcessingFeatureSink( "memory:test", false ) );
params.insert( QStringLiteral( "layer" ), QgsProcessingFeatureSink( "memory:test", nullptr ) );
QString dest;
std::unique_ptr< QgsFeatureSink > sink( QgsProcessingParameters::parameterAsSink( def, params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3111" ), context, dest ) );
QVERIFY( sink.get() );
Expand All @@ -2405,7 +2406,7 @@ void TestQgsProcessing::processingFeatureSink()
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );

// next using property based definition
params.insert( QStringLiteral( "layer" ), QgsProcessingFeatureSink( QgsProperty::fromExpression( QStringLiteral( "trim('memory' + ':test2')" ) ), false ) );
params.insert( QStringLiteral( "layer" ), QgsProcessingFeatureSink( QgsProperty::fromExpression( QStringLiteral( "trim('memory' + ':test2')" ) ), nullptr ) );
sink.reset( QgsProcessingParameters::parameterAsSink( def, params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) );
QVERIFY( sink.get() );
QgsVectorLayer *layer2 = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context, false ) );
Expand Down

0 comments on commit 81da209

Please sign in to comment.