Move algorithm expression context generation to QgsProcessingAlgorithm

Fix error when selecting "from expression" in algorithm parameter dialog
This commit is contained in:
Nyall Dawson 2017-06-06 11:25:09 +10:00
parent 607fed8c48
commit 39d20a4cb4
10 changed files with 141 additions and 2 deletions

View File

@ -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/ );

View File

@ -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.

View File

@ -36,12 +36,14 @@ from qgis.PyQt.QtGui import QCursor
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 @@ class DestinationSelectionPanel(BASE, WIDGET):
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('')

View File

@ -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"}
]
}

View File

@ -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 )

View File

@ -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 )

View File

@ -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:
/**

View File

@ -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

View File

@ -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();

View File

@ -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"