Skip to content

Commit

Permalink
Allow model child parameters to take values from an expression
Browse files Browse the repository at this point in the history
The expression is evaluated just before the child algorithm is
executed, so can utilise results already calculated by other
children in the model through the use of expression context
functions
  • Loading branch information
nyalldawson committed Jul 7, 2017
1 parent 52f4c5e commit 3243a1a
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 6 deletions.
34 changes: 33 additions & 1 deletion python/core/processing/qgsprocessingmodelalgorithm.sip
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ class QgsProcessingModelAlgorithm : QgsProcessingAlgorithm
ModelParameter,
ChildOutput,
StaticValue,
Expression,
};

ChildParameterSource();
%Docstring
Constructor for ChildParameterSource. It is recommended that the static methods
fromStaticValue(), fromModelParameter() and fromChildOutput() are used instead.
fromStaticValue(), fromModelParameter(), fromChildOutput() and fromExpression() are used instead.
%End

bool operator==( const QgsProcessingModelAlgorithm::ChildParameterSource &other ) const;
Expand All @@ -58,6 +59,7 @@ class QgsProcessingModelAlgorithm : QgsProcessingAlgorithm
Returns a new ChildParameterSource which takes its value from a static ``value``.
.. seealso:: fromModelParameter()
.. seealso:: fromChildOutput()
.. seealso:: fromExpression()
:rtype: QgsProcessingModelAlgorithm.ChildParameterSource
%End

Expand All @@ -66,13 +68,27 @@ class QgsProcessingModelAlgorithm : QgsProcessingAlgorithm
Returns a new ChildParameterSource which takes its value from a parent model parameter.
.. seealso:: fromStaticValue()
.. seealso:: fromChildOutput()
.. seealso:: fromExpression()
:rtype: QgsProcessingModelAlgorithm.ChildParameterSource
%End

static QgsProcessingModelAlgorithm::ChildParameterSource fromChildOutput( const QString &childId, const QString &outputName );
%Docstring
Returns a new ChildParameterSource which takes its value from an output generated by a child algorithm.
.. seealso:: fromStaticValue()
.. seealso:: fromModelParameter()
.. seealso:: fromExpression()
:rtype: QgsProcessingModelAlgorithm.ChildParameterSource
%End

static QgsProcessingModelAlgorithm::ChildParameterSource fromExpression( const QString &expression );
%Docstring
Returns a new ChildParameterSource which takes its value from an expression. The expression
is evaluated just before the child algorithm executes, and can use functions available
in its expression context to include results calculated from the child algorithms already
executed by the model.
.. seealso:: fromStaticValue()
.. seealso:: fromChildOutput()
.. seealso:: fromModelParameter()
:rtype: QgsProcessingModelAlgorithm.ChildParameterSource
%End
Expand Down Expand Up @@ -137,6 +153,22 @@ class QgsProcessingModelAlgorithm : QgsProcessingAlgorithm
Sets the source's child algorithm output ``name`` from which the output value will be taken. Calling this will also change the source() to ChildOutput.
.. seealso:: outputName()
.. seealso:: setOutputChildId()
%End

QString expression() const;
%Docstring
Returns the source's expression. This is only used when the source() is Expression.
.. seealso:: setExpression()
:rtype: str
%End

void setExpression( const QString &expression );
%Docstring
Sets the source's expression. Calling this will also change the source() to Expression.
The expression is evaluated just before the child algorithm executes, and can use functions available
in its expression context to include results calculated from the child algorithms already
executed by the model.
.. seealso:: expression()
%End

QVariant toVariant() const;
Expand Down
28 changes: 28 additions & 0 deletions src/core/processing/qgsprocessingmodelalgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,13 @@ QVariantMap QgsProcessingModelAlgorithm::parametersForChildAlgorithm( const Chil
paramParts << linkedChildResults.value( source.outputName() );
break;
}

case ChildParameterSource::Expression:
{
QgsExpression exp( source.expression() );
paramParts << exp.evaluate();
break;
}
}
}
if ( paramParts.count() == 1 )
Expand Down Expand Up @@ -1125,6 +1132,8 @@ bool QgsProcessingModelAlgorithm::ChildParameterSource::operator==( const QgsPro
return mChildId == other.mChildId && mOutputName == other.mOutputName;
case ModelParameter:
return mParameterName == other.mParameterName;
case Expression:
return mExpression == other.mExpression;
}
return false;
}
Expand Down Expand Up @@ -1154,6 +1163,14 @@ QgsProcessingModelAlgorithm::ChildParameterSource QgsProcessingModelAlgorithm::C
return src;
}

QgsProcessingModelAlgorithm::ChildParameterSource QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( const QString &expression )
{
ChildParameterSource src;
src.mSource = Expression;
src.mExpression = expression;
return src;
}

QgsProcessingModelAlgorithm::ChildParameterSource::Source QgsProcessingModelAlgorithm::ChildParameterSource::source() const
{
return mSource;
Expand All @@ -1177,6 +1194,10 @@ QVariant QgsProcessingModelAlgorithm::ChildParameterSource::toVariant() const
case StaticValue:
map.insert( QStringLiteral( "static_value" ), mStaticValue );
break;

case Expression:
map.insert( QStringLiteral( "expression" ), mExpression );
break;
}
return map;
}
Expand All @@ -1198,6 +1219,10 @@ bool QgsProcessingModelAlgorithm::ChildParameterSource::loadVariant( const QVari
case StaticValue:
mStaticValue = map.value( QStringLiteral( "static_value" ) );
break;

case Expression:
mExpression = map.value( QStringLiteral( "expression" ) ).toString();
break;
}
return true;
}
Expand All @@ -1214,6 +1239,9 @@ QString QgsProcessingModelAlgorithm::ChildParameterSource::asPythonCode() const

case StaticValue:
return mStaticValue.toString();

case Expression:
return QStringLiteral( "QgsExpression('%1').evaluate()" ).arg( mExpression );
}
return QString();
}
Expand Down
33 changes: 32 additions & 1 deletion src/core/processing/qgsprocessingmodelalgorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
ModelParameter, //!< Parameter value is taken from a parent model parameter
ChildOutput, //!< Parameter value is taken from an output generated by a child algorithm
StaticValue, //!< Parameter value is a static value
Expression, //!< Parameter value is taken from an expression, evaluated just before the algorithm runs
};

/**
* Constructor for ChildParameterSource. It is recommended that the static methods
* fromStaticValue(), fromModelParameter() and fromChildOutput() are used instead.
* fromStaticValue(), fromModelParameter(), fromChildOutput() and fromExpression() are used instead.
*/
ChildParameterSource() = default;

Expand All @@ -67,23 +68,37 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
* Returns a new ChildParameterSource which takes its value from a static \a value.
* \see fromModelParameter()
* \see fromChildOutput()
* \see fromExpression()
*/
static QgsProcessingModelAlgorithm::ChildParameterSource fromStaticValue( const QVariant &value );

/**
* Returns a new ChildParameterSource which takes its value from a parent model parameter.
* \see fromStaticValue()
* \see fromChildOutput()
* \see fromExpression()
*/
static QgsProcessingModelAlgorithm::ChildParameterSource fromModelParameter( const QString &parameterName );

/**
* Returns a new ChildParameterSource which takes its value from an output generated by a child algorithm.
* \see fromStaticValue()
* \see fromModelParameter()
* \see fromExpression()
*/
static QgsProcessingModelAlgorithm::ChildParameterSource fromChildOutput( const QString &childId, const QString &outputName );

/**
* Returns a new ChildParameterSource which takes its value from an expression. The expression
* is evaluated just before the child algorithm executes, and can use functions available
* in its expression context to include results calculated from the child algorithms already
* executed by the model.
* \see fromStaticValue()
* \see fromChildOutput()
* \see fromModelParameter()
*/
static QgsProcessingModelAlgorithm::ChildParameterSource fromExpression( const QString &expression );

/**
* Returns the parameter value's source.
*/
Expand Down Expand Up @@ -141,6 +156,21 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
*/
void setOutputName( const QString &name ) { mOutputName = name; mSource = ChildOutput; }

/**
* Returns the source's expression. This is only used when the source() is Expression.
* \see setExpression()
*/
QString expression() const { return mExpression; }

/**
* Sets the source's expression. Calling this will also change the source() to Expression.
* The expression is evaluated just before the child algorithm executes, and can use functions available
* in its expression context to include results calculated from the child algorithms already
* executed by the model.
* \see expression()
*/
void setExpression( const QString &expression ) { mExpression = expression; mSource = Expression; }

/**
* Saves this source to a QVariant.
* \see loadVariant()
Expand All @@ -165,6 +195,7 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
QString mParameterName;
QString mChildId;
QString mOutputName;
QString mExpression;

};

Expand Down
34 changes: 30 additions & 4 deletions tests/src/core/testqgsprocessing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4220,6 +4220,19 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( oSource.outputName(), QStringLiteral( "d" ) );
QCOMPARE( oSource.source(), QgsProcessingModelAlgorithm::ChildParameterSource::ChildOutput );

// expression source
QgsProcessingModelAlgorithm::ChildParameterSource expSource = QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( "1+2" );
QCOMPARE( expSource.source(), QgsProcessingModelAlgorithm::ChildParameterSource::Expression );
QCOMPARE( expSource.expression(), QStringLiteral( "1+2" ) );
expSource.setExpression( "1+3" );
QCOMPARE( expSource.expression(), QStringLiteral( "1+3" ) );
expSource = QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 5 );
// check that calling setExpression flips source to Expression
QCOMPARE( expSource.source(), QgsProcessingModelAlgorithm::ChildParameterSource::StaticValue );
expSource.setExpression( "1+4" );
QCOMPARE( expSource.expression(), QStringLiteral( "1+4" ) );
QCOMPARE( expSource.source(), QgsProcessingModelAlgorithm::ChildParameterSource::Expression );

// source equality operator
QVERIFY( QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 5 ) ==
QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 5 ) );
Expand All @@ -4239,6 +4252,12 @@ void TestQgsProcessing::modelerAlgorithm()
QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( QStringLiteral( "alg2" ), QStringLiteral( "out" ) ) );
QVERIFY( QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out" ) ) !=
QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out2" ) ) );
QVERIFY( QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( QStringLiteral( "a" ) ) ==
QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( QStringLiteral( "a" ) ) );
QVERIFY( QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( QStringLiteral( "a" ) ) !=
QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( QStringLiteral( "b" ) ) );
QVERIFY( QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( QStringLiteral( "a" ) ) !=
QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( QStringLiteral( "b" ) ) );



Expand Down Expand Up @@ -4562,9 +4581,11 @@ void TestQgsProcessing::modelerAlgorithm()
alg5c1.addParameterSources( "x", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "p1" ) );
alg5c1.addParameterSources( "y", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( "cx2", "out3" ) );
alg5c1.addParameterSources( "z", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 5 ) );
alg5c1.addParameterSources( "a", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( "2*2" ) );
alg5c1.addParameterSources( "zm", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 6 )
<< QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "p2" )
<< QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( "cx2", "out4" ) );
<< QgsProcessingModelAlgorithm::ChildParameterSource::fromChildOutput( "cx2", "out4" )
<< QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( "1+2" ) );
alg5c1.setActive( true );
alg5c1.setOutputsCollapsed( true );
alg5c1.setParametersCollapsed( true );
Expand Down Expand Up @@ -4609,21 +4630,26 @@ void TestQgsProcessing::modelerAlgorithm()
QCOMPARE( alg6c1.description(), QStringLiteral( "child 1" ) );
QCOMPARE( alg6c1.position().x(), 1.0 );
QCOMPARE( alg6c1.position().y(), 2.0 );
QCOMPARE( alg6c1.parameterSources().count(), 4 );
QCOMPARE( alg6c1.parameterSources().count(), 5 );
QCOMPARE( alg6c1.parameterSources().value( "x" ).at( 0 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::ModelParameter );
QCOMPARE( alg6c1.parameterSources().value( "x" ).at( 0 ).parameterName(), QStringLiteral( "p1" ) );
QCOMPARE( alg6c1.parameterSources().value( "y" ).at( 0 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::ChildOutput );
QCOMPARE( alg6c1.parameterSources().value( "y" ).at( 0 ).outputChildId(), QStringLiteral( "cx2" ) );
QCOMPARE( alg6c1.parameterSources().value( "y" ).at( 0 ).outputName(), QStringLiteral( "out3" ) );
QCOMPARE( alg6c1.parameterSources().value( "z" ).at( 0 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::StaticValue );
QCOMPARE( alg6c1.parameterSources().value( "z" ).at( 0 ).staticValue().toInt(), 5 );
QCOMPARE( alg6c1.parameterSources().value( "a" ).at( 0 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::Expression );
QCOMPARE( alg6c1.parameterSources().value( "a" ).at( 0 ).expression(), QStringLiteral( "2*2" ) );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).count(), 4 );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 0 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::StaticValue );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 0 ).staticValue().toInt(), 6 );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 1 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::ModelParameter );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 1 ).parameterName(), QStringLiteral( "p2" ) );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 2 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::ChildOutput );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 2 ).outputChildId(), QStringLiteral( "cx2" ) );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 2 ).outputName(), QStringLiteral( "out4" ) );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 3 ).source(), QgsProcessingModelAlgorithm::ChildParameterSource::Expression );
QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 3 ).expression(), QStringLiteral( "1+2" ) );

QCOMPARE( alg6c1.modelOutputs().count(), 1 );
QCOMPARE( alg6c1.modelOutputs().value( QStringLiteral( "a" ) ).description(), QStringLiteral( "my output" ) );
Expand Down Expand Up @@ -4739,7 +4765,7 @@ void TestQgsProcessing::modelExecution()
alg2c1.setAlgorithmId( "native:buffer" );
alg2c1.addParameterSources( "INPUT", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "SOURCE_LAYER" ) );
alg2c1.addParameterSources( "DISTANCE", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromModelParameter( "DIST" ) );
alg2c1.addParameterSources( "SEGMENTS", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 16 ) );
alg2c1.addParameterSources( "SEGMENTS", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromExpression( QStringLiteral( "8*2" ) ) );
alg2c1.addParameterSources( "END_CAP_STYLE", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 1 ) );
alg2c1.addParameterSources( "JOIN_STYLE", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( 2 ) );
alg2c1.addParameterSources( "DISSOLVE", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( false ) );
Expand Down Expand Up @@ -4814,7 +4840,7 @@ void TestQgsProcessing::modelExecution()
"##model_out_layer=output outputVector\n"
"##my_out=output outputVector\n"
"results={}\n"
"outputs['cx1']=processing.run('native:buffer', {'DISSOLVE':false,'DISTANCE':parameters['DIST'],'END_CAP_STYLE':1,'INPUT':parameters['SOURCE_LAYER'],'JOIN_STYLE':2,'SEGMENTS':16}, context=context, feedback=feedback)\n"
"outputs['cx1']=processing.run('native:buffer', {'DISSOLVE':false,'DISTANCE':parameters['DIST'],'END_CAP_STYLE':1,'INPUT':parameters['SOURCE_LAYER'],'JOIN_STYLE':2,'SEGMENTS':QgsExpression('8*2').evaluate()}, context=context, feedback=feedback)\n"
"results['MODEL_OUT_LAYER']=outputs['cx1']['OUTPUT']\n"
"outputs['cx2']=processing.run('native:centroids', {'INPUT':outputs['cx1']['OUTPUT']}, context=context, feedback=feedback)\n"
"outputs['cx3']=processing.run('native:extractbyexpression', {'EXPRESSION':true,'INPUT':outputs['cx1']['OUTPUT'],'OUTPUT':parameters['MY_OUT']}, context=context, feedback=feedback)\n"
Expand Down

0 comments on commit 3243a1a

Please sign in to comment.