Skip to content
Permalink
Browse files

Add function to return variables available for child algorithms during

model execution

And use this function to determine in advance dependencies between
child algorithm parameters with expression based values and
which other child algorithms they depend upon.
  • Loading branch information
nyalldawson committed Jul 7, 2017
1 parent 3f9cfe0 commit 534844f99998e3ff00104a12480b855c07d73f6d
@@ -877,10 +877,57 @@ Copies are protected to avoid slicing
:rtype: list of QgsProcessingModelAlgorithm.ChildParameterSource
%End

class VariableDefinition
{
%Docstring
Definition of a expression context variable available during model execution.
.. versionadded:: 3.0
%End

%TypeHeaderCode
#include "qgsprocessingmodelalgorithm.h"
%End
public:

VariableDefinition( const QVariant &value, const QgsProcessingModelAlgorithm::ChildParameterSource &source );
%Docstring
Constructor for a new VariableDefinition with the specified ``value`` and original
parameter ``source``.
%End

QVariant value;
%Docstring
Value of variable
%End

QgsProcessingModelAlgorithm::ChildParameterSource source;
%Docstring
Original source of variable's value
%End
};

QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition > variablesForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters = QVariantMap(),
const QVariantMap &results = QVariantMap() ) const;
%Docstring
Returns a map of variable name to variable definition for expression context variables which are available
for use by child algorithm during model execution.

The child algorithm ``childId`` and processing ``context``
are manadatory. If ``modelParameters`` and ``results`` are not specified, then only the variable names and sources
will be returned, but all variable values will be null. This can be used to determine in advance which variables
will be available for a specific child algorithm, e.g. for use in expression builder widgets.

In order to calculate the actual variable value, the input model ``modelParameters`` and already executed child
algorithm ``results`` must be passed.
.. seealso:: createExpressionContextScopeForChildAlgorithm()
:rtype: QMap< str, QgsProcessingModelAlgorithm.VariableDefinition >
%End

QgsExpressionContextScope *createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters = QVariantMap(),
const QVariantMap &results = QVariantMap() ) const /Factory/;
%Docstring
Creates a new expression context scope for a child algorithm within the model.
.. seealso:: variablesForChildAlgorithm()
:rtype: QgsExpressionContextScope
%End

@@ -874,13 +874,6 @@ class QgsExpressionContextUtils
:rtype: QgsExpressionContextScope
%End

static QgsExpressionContextScope *processingModelResultsScope( const QVariantMap &results, QgsProcessingContext &context ) /Factory/;
%Docstring
Creates a new scope which contains variables and functions relating to processing model results
:rtype: QgsExpressionContextScope
%End


static void registerContextFunctions();
%Docstring
Registers all known core functions provided by QgsExpressionContextScope objects.
@@ -33,6 +33,7 @@
QgsExpression)
from processing.modeler.ModelerGraphicItem import ModelerGraphicItem
from processing.modeler.ModelerArrowItem import ModelerArrowItem
from processing.tools.dataobjects import createContext


class ModelerScene(QGraphicsScene):
@@ -63,11 +64,11 @@ def getOutputPositions(self):
pos[algName] = outputPos
return pos

def getItemsFromParamValue(self, value):
def getItemsFromParamValue(self, value, child_id, context):
items = []
if isinstance(value, list):
for v in value:
items.extend(self.getItemsFromParamValue(v))
items.extend(self.getItemsFromParamValue(v, child_id, context))
elif isinstance(value, QgsProcessingModelAlgorithm.ChildParameterSource):
if value.source() == QgsProcessingModelAlgorithm.ChildParameterSource.ModelParameter:
items.append((self.paramItems[value.parameterName()], 0))
@@ -78,10 +79,17 @@ def getItemsFromParamValue(self, value):
break
if value.outputChildId() in self.algItems:
items.append((self.algItems[value.outputChildId()], i))
elif value.source() == QgsProcessingModelAlgorithm.ChildParameterSource.Expression:
variables = self.model.variablesForChildAlgorithm(child_id, context)
exp = QgsExpression(value.expression())
for v in exp.referencedVariables():
if v in variables:
items.extend(self.getItemsFromParamValue(variables[v].source, child_id, context))
return items

def paintModel(self, model, controls=True):
self.model = model
context = createContext()
# Inputs
for inp in list(model.parameterComponents().values()):
item = ModelerGraphicItem(inp, model, controls, scene=self)
@@ -127,7 +135,7 @@ def paintModel(self, model, controls=True):
else:
sources = []
for source in sources:
sourceItems = self.getItemsFromParamValue(source)
sourceItems = self.getItemsFromParamValue(source, alg.childId(), context)
for sourceItem, sourceIdx in sourceItems:
arrow = ModelerArrowItem(sourceItem, sourceIdx, self.algItems[alg.childId()], idx)
sourceItem.addArrow(arrow)
@@ -663,42 +663,45 @@ QString QgsProcessingModelAlgorithm::asPythonCode() const
return lines.join( '\n' );
}

QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> QgsProcessingModelAlgorithm::variablesForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
{
QVariantMap variables;
std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope() );
QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables;

auto safeName = []( const QString & name )->QString
{
QString s = name;
return s.replace( QRegularExpression( "[\\s'\"\\(\\):]" ), QStringLiteral( "_" ) );
};

// numeric sources
ChildParameterSources sources = availableSourcesForChild( childId, QStringList() << QgsProcessingParameterNumber::typeName(),
QStringList() << QgsProcessingOutputNumber::typeName() );
Q_FOREACH ( const ChildParameterSource &source, sources )
{
QString name;
QVariant value;

switch ( source.source() )
{
case ChildParameterSource::ModelParameter:
{
name = source.parameterName();
value = modelParameters.value( source.parameterName() );
break;
variables.insert( safeName( source.parameterName() ), VariableDefinition( modelParameters.value( source.parameterName() ), source ) );
continue;
}
case ChildParameterSource::ChildOutput:
{
name = QStringLiteral( "%1_%2" ).arg( mChildAlgorithms.value( source.outputChildId() ).description().isEmpty() ?
source.outputChildId() : mChildAlgorithms.value( source.outputChildId() ).description(), source.outputName() );
value = results.value( source.outputChildId() ).toMap().value( source.outputName() );
break;
QString name = QStringLiteral( "%1_%2" ).arg( mChildAlgorithms.value( source.outputChildId() ).description().isEmpty() ?
source.outputChildId() : mChildAlgorithms.value( source.outputChildId() ).description(), source.outputName() );
variables.insert( safeName( name ),
VariableDefinition( results.value( source.outputChildId() ).toMap().value( source.outputName() ),
source ) );
continue;
}

case ChildParameterSource::Expression:
case ChildParameterSource::StaticValue:
continue;

};

variables.insert( name, value );
}

// layer sources
sources = availableSourcesForChild( childId, QStringList()
<< QgsProcessingParameterVectorLayer::typeName()
<< QgsProcessingParameterRasterLayer::typeName(),
@@ -736,10 +739,12 @@ QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextS
if ( !layer )
layer = QgsProcessingUtils::mapLayerFromString( value.toString(), context );

variables.insert( QStringLiteral( "%1_minx" ).arg( name ), layer ? layer->extent().xMinimum() : QVariant() );
variables.insert( QStringLiteral( "%1_miny" ).arg( name ), layer ? layer->extent().yMinimum() : QVariant() );
variables.insert( QStringLiteral( "%1_maxx" ).arg( name ), layer ? layer->extent().xMaximum() : QVariant() );
variables.insert( QStringLiteral( "%1_maxy" ).arg( name ), layer ? layer->extent().yMaximum() : QVariant() );
variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMinimum() : QVariant(), source ) );
variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMinimum() : QVariant(), source ) );
variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( layer ? layer->extent().xMaximum() : QVariant(), source ) );
variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( layer ? layer->extent().yMaximum() : QVariant(), source ) );

continue;
}

sources = availableSourcesForChild( childId, QStringList()
@@ -787,20 +792,24 @@ QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextS
featureSource = vl;
}

variables.insert( QStringLiteral( "%1_minx" ).arg( name ), featureSource ? featureSource->sourceExtent().xMinimum() : QVariant() );
variables.insert( QStringLiteral( "%1_miny" ).arg( name ), featureSource ? featureSource->sourceExtent().yMinimum() : QVariant() );
variables.insert( QStringLiteral( "%1_maxx" ).arg( name ), featureSource ? featureSource->sourceExtent().xMaximum() : QVariant() );
variables.insert( QStringLiteral( "%1_maxy" ).arg( name ), featureSource ? featureSource->sourceExtent().yMaximum() : QVariant() );
variables.insert( safeName( QStringLiteral( "%1_minx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMinimum() : QVariant(), source ) );
variables.insert( safeName( QStringLiteral( "%1_miny" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMinimum() : QVariant(), source ) );
variables.insert( safeName( QStringLiteral( "%1_maxx" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().xMaximum() : QVariant(), source ) );
variables.insert( safeName( QStringLiteral( "%1_maxy" ).arg( name ) ), VariableDefinition( featureSource ? featureSource->sourceExtent().yMaximum() : QVariant(), source ) );
}

QVariantMap::const_iterator varIt = variables.constBegin();
return variables;
}

QgsExpressionContextScope *QgsProcessingModelAlgorithm::createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters, const QVariantMap &results ) const
{
std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope() );
QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = variablesForChildAlgorithm( childId, context, modelParameters, results );
QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition>::const_iterator varIt = variables.constBegin();
for ( ; varIt != variables.constEnd(); ++varIt )
{
QString name = varIt.key();
name = name.replace( QRegularExpression( "[\\s'\"\\(\\):]" ), QStringLiteral( "_" ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( name, varIt.value(), true ) );
scope->addVariable( QgsExpressionContextScope::StaticVariable( varIt.key(), varIt->value, true ) );
}

return scope.release();
}

@@ -867,8 +867,50 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
QList< QgsProcessingModelAlgorithm::ChildParameterSource > availableSourcesForChild( const QString &childId, const QStringList &parameterTypes = QStringList(),
const QStringList &outputTypes = QStringList(), const QList< int > dataTypes = QList< int >() ) const;

/**
* Definition of a expression context variable available during model execution.
* \since QGIS 3.0
* \ingroup core
*/
class CORE_EXPORT VariableDefinition
{
public:

/**
* Constructor for a new VariableDefinition with the specified \a value and original
* parameter \a source.
*/
VariableDefinition( const QVariant &value, const QgsProcessingModelAlgorithm::ChildParameterSource &source )
: value( value )
, source( source )
{}

//! Value of variable
QVariant value;

//! Original source of variable's value
QgsProcessingModelAlgorithm::ChildParameterSource source;
};

/**
* Returns a map of variable name to variable definition for expression context variables which are available
* for use by child algorithm during model execution.
*
* The child algorithm \a childId and processing \a context
* are manadatory. If \a modelParameters and \a results are not specified, then only the variable names and sources
* will be returned, but all variable values will be null. This can be used to determine in advance which variables
* will be available for a specific child algorithm, e.g. for use in expression builder widgets.
*
* In order to calculate the actual variable value, the input model \a modelParameters and already executed child
* algorithm \a results must be passed.
* \see createExpressionContextScopeForChildAlgorithm()
*/
QMap< QString, QgsProcessingModelAlgorithm::VariableDefinition > variablesForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters = QVariantMap(),
const QVariantMap &results = QVariantMap() ) const;

/**
* Creates a new expression context scope for a child algorithm within the model.
* \see variablesForChildAlgorithm()
*/
QgsExpressionContextScope *createExpressionContextScopeForChildAlgorithm( const QString &childId, QgsProcessingContext &context, const QVariantMap &modelParameters = QVariantMap(),
const QVariantMap &results = QVariantMap() ) const SIP_FACTORY;
@@ -1120,13 +1120,6 @@ QgsExpressionContextScope *QgsExpressionContextUtils::processingAlgorithmScope(
return scope.release();
}

QgsExpressionContextScope *QgsExpressionContextUtils::processingModelResultsScope( const QVariantMap &results, QgsProcessingContext &context )
{
std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope( QObject::tr( "Model results" ) ) );

return scope.release();
}

void QgsExpressionContextUtils::registerContextFunctions()
{
QgsExpression::registerFunction( new GetNamedProjectColor( nullptr ) );
@@ -797,12 +797,6 @@ class CORE_EXPORT QgsExpressionContextUtils
*/
static QgsExpressionContextScope *processingAlgorithmScope( const QgsProcessingAlgorithm *algorithm, const QVariantMap &parameters, QgsProcessingContext &context ) SIP_FACTORY;

/**
* Creates a new scope which contains variables and functions relating to processing model results
*/
static QgsExpressionContextScope *processingModelResultsScope( const QVariantMap &results, QgsProcessingContext &context ) SIP_FACTORY;


/** Registers all known core functions provided by QgsExpressionContextScope objects.
*/
static void registerContextFunctions();

0 comments on commit 534844f

Please sign in to comment.
You can’t perform that action at this time.