Skip to content
Permalink
Browse files

Move algorithm expression context generation to QgsProcessingAlgorithm

Fix error when selecting "from expression" in algorithm parameter dialog
  • Loading branch information
nyalldawson committed Jun 6, 2017
1 parent 607fed8 commit 39d20a4cb47f2bcfda6ea61ef88eab3d0212e03f
@@ -228,6 +228,15 @@ class QgsProcessingAlgorithm
:rtype: QWidget
%End

QgsExpressionContext createExpressionContext( const QVariantMap &parameters,
QgsProcessingContext &context ) const;
%Docstring
Creates an expression context relating to the algorithm. This can be called by algorithms
to create a new expression context ready for evaluating expressions within the algorithm.
:rtype: QgsExpressionContext
%End


protected:

bool addParameter( QgsProcessingParameterDefinition *parameterDefinition /Transfer/ );
@@ -866,6 +866,14 @@ class QgsExpressionContextUtils
:rtype: QgsExpressionContext
%End

static QgsExpressionContextScope *processingAlgorithmScope( const QgsProcessingAlgorithm *algorithm, const QVariantMap &parameters, QgsProcessingContext &context ) /Factory/;
%Docstring
Creates a new scope which contains variables and functions relating to a processing ``algorithm``,
when used with the specified ``parameters`` and ``context``.
For instance, algorithm name and parameter functions.
:rtype: QgsExpressionContextScope
%End

static void registerContextFunctions();
%Docstring
Registers all known core functions provided by QgsExpressionContextScope objects.
@@ -36,12 +36,14 @@
from qgis.gui import QgsEncodingFileDialog, QgsExpressionBuilderDialog
from qgis.core import (QgsDataSourceUri,
QgsCredentials,
QgsExpression,
QgsSettings,
QgsProcessingParameterFeatureSink,
QgsProcessingFeatureSinkDefinition)
from processing.core.ProcessingConfig import ProcessingConfig
from processing.core.outputs import OutputVector
from processing.core.outputs import OutputDirectory
from processing.tools.dataobjects import createContext
from processing.gui.PostgisTableSelector import PostgisTableSelector
from processing.gui.ParameterGuiUtils import getFileFilter

@@ -122,11 +124,13 @@ def selectOutput(self):
popupMenu.exec_(QCursor.pos())

def showExpressionsBuilder(self):
context = self.alg.createExpressionContext({}, createContext())
dlg = QgsExpressionBuilderDialog(None, self.leText.text(), self, 'generic',
self.parameter.expressionContext(self.alg))
context)
dlg.setWindowTitle(self.tr('Expression based output'))
if dlg.exec_() == QDialog.Accepted:
self.leText.setText(dlg.expressionText())
expression = QgsExpression(dlg.expressionText())
self.leText.setText(expression.evaluate(context))

def saveToTemporary(self):
self.leText.setText('')
@@ -0,0 +1,11 @@
{
"name": "parameter",
"type": "function",
"description": "Returns the value of a processing algorithm input parameter.",
"arguments": [
{"arg":"name", "description":"name of the corresponding input parameter"}
],
"examples": [
{ "expression":"parameter('BUFFER_SIZE')", "returns":"5.6"}
]
}
@@ -685,6 +685,9 @@ void QgsExpression::initVariableHelp()
//cluster variables
sVariableHelpTexts.insert( QStringLiteral( "cluster_color" ), QCoreApplication::translate( "cluster_color", "Color of symbols within a cluster, or NULL if symbols have mixed colors." ) );
sVariableHelpTexts.insert( QStringLiteral( "cluster_size" ), QCoreApplication::translate( "cluster_size", "Number of symbols contained within a cluster." ) );

//processing variables
sVariableHelpTexts.insert( QStringLiteral( "algorithm_id" ), QCoreApplication::translate( "algorithm_id", "Unique ID for algorithm." ) );
}

QString QgsExpression::variableHelpText( const QString &variableName, bool showValue, const QVariant &value )
@@ -21,6 +21,7 @@
#include "qgsprocessingparameters.h"
#include "qgsprocessingoutputs.h"
#include "qgsrectangle.h"
#include "qgsprocessingcontext.h"

QgsProcessingAlgorithm::~QgsProcessingAlgorithm()
{
@@ -100,6 +101,22 @@ QWidget *QgsProcessingAlgorithm::createCustomParametersWidget( QWidget * ) const
return nullptr;
}

QgsExpressionContext QgsProcessingAlgorithm::createExpressionContext( const QVariantMap &parameters,
QgsProcessingContext &context ) const
{
// start with context's expression context
QgsExpressionContext c = context.expressionContext();
if ( c.scopeCount() == 0 )
{
//empty scope, populate with initial scopes
c << QgsExpressionContextUtils::globalScope()
<< QgsExpressionContextUtils::projectScope( context.project() );
}

c << QgsExpressionContextUtils::processingAlgorithmScope( this, parameters, context );
return c;
}

bool QgsProcessingAlgorithm::addParameter( QgsProcessingParameterDefinition *definition )
{
if ( !definition )
@@ -229,6 +229,14 @@ class CORE_EXPORT QgsProcessingAlgorithm
*/
virtual QWidget *createCustomParametersWidget( QWidget *parent = nullptr ) const SIP_FACTORY;

/**
* Creates an expression context relating to the algorithm. This can be called by algorithms
* to create a new expression context ready for evaluating expressions within the algorithm.
*/
QgsExpressionContext createExpressionContext( const QVariantMap &parameters,
QgsProcessingContext &context ) const;


protected:

/**
@@ -29,6 +29,8 @@
#include "qgsapplication.h"
#include "qgsmapsettings.h"
#include "qgsmaplayerlistutils.h"
#include "qgsprocessingcontext.h"
#include "qgsprocessingalgorithm.h"

#include <QSettings>
#include <QDir>
@@ -707,6 +709,30 @@ class GetLayerVisibility : public QgsScopedExpressionFunction

};

class GetProcessingParameterValue : public QgsScopedExpressionFunction
{
public:
GetProcessingParameterValue( const QVariantMap &params )
: QgsScopedExpressionFunction( QStringLiteral( "parameter" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ), QStringLiteral( "Processing" ) )
, mParams( params )
{}

QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) override
{
return mParams.value( values.at( 0 ).toString() );
}

QgsScopedExpressionFunction *clone() const override
{
return new GetProcessingParameterValue( mParams );
}

private:

const QVariantMap mParams;

};

///@endcond

QgsExpressionContextScope *QgsExpressionContextUtils::projectScope( const QgsProject *project )
@@ -1077,11 +1103,29 @@ QgsExpressionContext QgsExpressionContextUtils::createFeatureBasedContext( const
return QgsExpressionContext() << scope;
}

QgsExpressionContextScope *QgsExpressionContextUtils::processingAlgorithmScope( const QgsProcessingAlgorithm *algorithm, const QVariantMap &parameters, QgsProcessingContext &context )
{
// set aside for future use
Q_UNUSED( context );

std::unique_ptr< QgsExpressionContextScope > scope( new QgsExpressionContextScope( QObject::tr( "Algorithm" ) ) );
if ( !algorithm )
return scope.release();

//add standard algorithm variables
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "algorithm_id" ), algorithm->id(), true ) );

scope->addFunction( QStringLiteral( "parameter" ), new GetProcessingParameterValue( parameters ) );

return scope.release();
}

void QgsExpressionContextUtils::registerContextFunctions()
{
QgsExpression::registerFunction( new GetNamedProjectColor( nullptr ) );
QgsExpression::registerFunction( new GetComposerItemVariables( nullptr ) );
QgsExpression::registerFunction( new GetLayerVisibility( QList<QgsMapLayer *>() ) );
QgsExpression::registerFunction( new GetProcessingParameterValue( QVariantMap() ) );
}

bool QgsScopedExpressionFunction::usesGeometry( const QgsExpressionNodeFunction *node ) const
@@ -36,6 +36,8 @@ class QgsAtlasComposition;
class QgsMapSettings;
class QgsProject;
class QgsSymbol;
class QgsProcessingAlgorithm;
class QgsProcessingContext;

/** \ingroup core
* \class QgsScopedExpressionFunction
@@ -788,6 +790,13 @@ class CORE_EXPORT QgsExpressionContextUtils
*/
static QgsExpressionContext createFeatureBasedContext( const QgsFeature &feature, const QgsFields &fields );

/**
* Creates a new scope which contains variables and functions relating to a processing \a algorithm,
* when used with the specified \a parameters and \a context.
* For instance, algorithm name and parameter functions.
*/
static QgsExpressionContextScope *processingAlgorithmScope( const QgsProcessingAlgorithm *algorithm, const QVariantMap &parameters, QgsProcessingContext &context ) SIP_FACTORY;

/** Registers all known core functions provided by QgsExpressionContextScope objects.
*/
static void registerContextFunctions();
@@ -30,6 +30,7 @@
#include "qgspoint.h"
#include "qgsgeometry.h"
#include "qgsvectorfilewriter.h"
#include "qgsexpressioncontext.h"

class DummyAlgorithm : public QgsProcessingAlgorithm
{
@@ -219,6 +220,7 @@ class TestQgsProcessing: public QObject
void combineLayerExtent();
void processingFeatureSource();
void processingFeatureSink();
void algorithmScope();

private:

@@ -2425,5 +2427,29 @@ void TestQgsProcessing::processingFeatureSink()
QCOMPARE( layer2->crs().authid(), QStringLiteral( "EPSG:3113" ) );
}

void TestQgsProcessing::algorithmScope()
{
QgsProcessingContext pc;

// no alg
std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::processingAlgorithmScope( nullptr, QVariantMap(), pc ) );
QVERIFY( scope.get() );

// with alg
std::unique_ptr< QgsProcessingAlgorithm > alg( new DummyAlgorithm( "alg1" ) );
QVariantMap params;
params.insert( QStringLiteral( "a_param" ), 5 );
scope.reset( QgsExpressionContextUtils::processingAlgorithmScope( alg.get(), params, pc ) );
QVERIFY( scope.get() );
QCOMPARE( scope->variable( QStringLiteral( "algorithm_id" ) ).toString(), alg->id() );

QgsExpressionContext context;
context.appendScope( scope.release() );
QgsExpression exp( "parameter('bad')" );
QVERIFY( !exp.evaluate( &context ).isValid() );
QgsExpression exp2( "parameter('a_param')" );
QCOMPARE( exp2.evaluate( &context ).toInt(), 5 );
}

QGSTEST_MAIN( TestQgsProcessing )
#include "testqgsprocessing.moc"

0 comments on commit 39d20a4

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