Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
New framework for context based expressions
This commit adds the ability for expressions to be evaluated against
specific contexts. It replaces the previous behaviour where
expressions were evaluated against a specific feature and could
utilise fragile global "special columns".

Now, expressions are instead evaluated using a context designed for
each individual expression. This is done via QgsExpressionContext
and QgsExpressionContextScope objects.

A QgsExpressionContextScope encapsulates the variables and functions
relating to a specific context. For instance, scopes can be created
for "global" variables (such as QGIS version, platform, and user-set
variables specified within the QGIS options dialog. Think things
like user name, work department, etc), or for "project" variables
(eg project path, title, filename, and user-set variables set
through the project properties dialog. Project version, reference
number, that kind of thing). Many more scopes are planned, including
map layer scopes (variables for layer name, id, user-set variables
through the layer properties dialog), composer scopes, etc...

QgsExpressionContextScopes are 'stacked' into a QgsExpressionContext
object. Scopes added later to a QgsExpressionContext will override
any variables or functions provided by earlier scopes, so for
instance a user could override their global 'author' variable set
within QGIS options with a different 'author' set via the project
properties dialog.

The intended use is that a QgsExpressionContext is created before
a batch set of QgsExpression evaluations. Scopes are then added to
the context based on what makes sense for that particular
expression. Eg, almost all contexts will consist of the global
scope and project scope, and then additional scopes as required.
So a composer label would be evaluated against a context
consisting of the global scope, project scope, composition scope
and finally composer item scope. The batch set of expression
evaluations would then be performed using this context, after which
the context is discarded. In other words, a context is designed
for use for one specific set of expression evaluations only.
  • Loading branch information
nyalldawson committed Aug 22, 2015
1 parent f5de327 commit f6e0ce7
Show file tree
Hide file tree
Showing 15 changed files with 2,525 additions and 252 deletions.
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -37,6 +37,7 @@
%Include qgseditorwidgetconfig.sip
%Include qgserror.sip
%Include qgsexpression.sip
%Include qgsexpressioncontext.sip
%Include qgsfeature.sip
%Include qgsfeatureiterator.sip
%Include qgsfeaturerequest.sip
Expand Down
114 changes: 89 additions & 25 deletions python/core/qgsexpression.sip
Expand Up @@ -17,7 +17,13 @@ class QgsExpression
const QgsExpression::Node* rootNode() const;

//! Get the expression ready for evaluation - find out column indexes.
bool prepare( const QgsFields &fields );
bool prepare( const QgsFields &fields ) /Deprecated/;

/** Get the expression ready for evaluation - find out column indexes.
* @param context context for preparing expression
* @note added in QGIS 2.12
*/
bool prepare( const QgsExpressionContext *context );

/**
* Get list of columns referenced by the expression.
Expand All @@ -34,28 +40,41 @@ class QgsExpression

//! Evaluate the feature and return the result
//! @note prepare() should be called before calling this method
QVariant evaluate( const QgsFeature* f = NULL );
QVariant evaluate( const QgsFeature* f ) /Deprecated/;

//! Evaluate the feature and return the result
//! @note prepare() should be called before calling this method
//! @note available in python bindings as evaluatePrepared
QVariant evaluate( const QgsFeature& f ) /PyName=evaluatePrepared/;
QVariant evaluate( const QgsFeature& f ) /PyName=evaluatePrepared,Deprecated/;

//! Evaluate the feature and return the result
//! @note this method does not expect that prepare() has been called on this instance
QVariant evaluate( const QgsFeature* f, const QgsFields& fields );
QVariant evaluate( const QgsFeature* f, const QgsFields& fields ) /Deprecated/;

//! Evaluate the feature and return the result
//! @note this method does not expect that prepare() has been called on this instance
//! @note not available in python bindings
// inline QVariant evaluate( const QgsFeature& f, const QgsFields& fields ) { return evaluate( &f, fields ); }

/** Evaluate the feature and return the result.
* @note this method does not expect that prepare() has been called on this instance
* @note added in QGIS 2.12
*/
QVariant evaluate();

/** Evaluate the expression against the specified context and return the result.
* @param context context for evaluating expression
* @note prepare() should be called before calling this method.
* @note added in QGIS 2.12
*/
QVariant evaluate( const QgsExpressionContext* context );

//! Returns true if an error occurred when evaluating last input
bool hasEvalError() const;
//! Returns evaluation error
QString evalErrorString() const;
//! Set evaluation error (used internally by evaluation functions)
void setEvalErrorString( QString str );
void setEvalErrorString( const QString& str );

//! Set the number for $rownum special column
void setCurrentRowNumber( int rowNumber );
Expand All @@ -77,7 +96,16 @@ class QgsExpression
*/
bool isField() const;

static bool isValid( const QString& text, const QgsFields& fields, QString &errorMessage );
static bool isValid( const QString& text, const QgsFields& fields, QString &errorMessage ) /Deprecated/;

/** Tests whether a string is a valid expression.
* @param text string to test
* @param context optional expression context
* @param errorMessage will be filled with any error message from the validation
* @returns true if string is a valid expression
* @note added in QGIS 2.12
*/
static bool isValid( const QString& text, const QgsExpressionContext* context, QString &errorMessage );

void setScale( double scale );

Expand Down Expand Up @@ -114,14 +142,32 @@ class QgsExpression
QgsVectorLayer *layer,
const QMap<QString, QVariant> *substitutionMap = 0,
const QgsDistanceArea* distanceArea = 0
);
) /Deprecated/;

/** This function replaces each expression between [% and %]
in the string with the result of its evaluation with the specified context

Additional substitutions can be passed through the substitutionMap parameter
@param action
@param context expression context
@param substitutionMap
@param distanceArea optional QgsDistanceArea. If specified, the QgsDistanceArea is used for distance
and area conversion
@note added in QGIS 2.12
*/
static QString replaceExpressionText( const QString &action, const QgsExpressionContext* context,
const QMap<QString, QVariant> *substitutionMap = 0,
const QgsDistanceArea* distanceArea = 0
);

/** Attempts to evaluate a text string as an expression to a resultant double
* value.
* @param text text to evaluate as expression
* @param fallbackValue value to return if text can not be evaluated as a double
* @returns evaluated double value, or fallback value
* @note added in QGIS 2.7
* @note this method is inefficient for bulk evaluation of expressions, it is intended
* for one-off evaluations only.
*/
static double evaluateToDouble( const QString& text, const double fallbackValue );

Expand Down Expand Up @@ -231,7 +277,15 @@ class QgsExpression
/** The help text for the function. */
const QString helptext();

virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) = 0;
virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) /Deprecated/;

/** Returns result of evaluating the function.
* @param values list of values passed to the function
* @param context context expression is being evaluated against
* @param parent parent expression
* @returns result of function
*/
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent );

virtual bool handlesNull() const;
};
Expand All @@ -243,7 +297,7 @@ class QgsExpression
static bool unregisterFunction( QString name );

// tells whether the identifier is a name of existing function
static bool isFunctionName( QString name );
static bool isFunctionName( const QString& name );

// return index of the function in Functions array
static int functionIndex( const QString& name );
Expand Down Expand Up @@ -297,11 +351,21 @@ class QgsExpression
virtual QgsExpression::NodeType nodeType() const = 0;
// abstract virtual eval function
// errors are reported to the parent
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) = 0;
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) /Deprecated/;

/** Evaluation function for nodes. Errors are reported to the parent.
* @note added in QGIS 2.12
*/
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );

// abstract virtual preparation function
// errors are reported to the parent
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) = 0;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) /Deprecated/;

/** Preparation function for nodes. Errors are reported to the parent.
* @note added in QGIS 2.12
*/
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );

virtual QString dump() const = 0;

Expand Down Expand Up @@ -357,8 +421,8 @@ class QgsExpression
QgsExpression::Node* operand() const;

virtual QgsExpression::NodeType nodeType() const;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand All @@ -377,8 +441,8 @@ class QgsExpression
QgsExpression::Node* opRight() const;

virtual QgsExpression::NodeType nodeType() const;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand All @@ -400,8 +464,8 @@ class QgsExpression
QgsExpression::NodeList* list() const;

virtual QgsExpression::NodeType nodeType() const;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand All @@ -420,8 +484,8 @@ class QgsExpression
QgsExpression::NodeList* args() const;

virtual QgsExpression::NodeType nodeType() const;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand All @@ -437,8 +501,8 @@ class QgsExpression
const QVariant& value() const;

virtual QgsExpression::NodeType nodeType() const;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand All @@ -454,8 +518,8 @@ class QgsExpression
QString name() const;

virtual QgsExpression::NodeType nodeType() const;
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand All @@ -482,8 +546,8 @@ class QgsExpression
~NodeCondition();

virtual QgsExpression::NodeType nodeType() const;
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
virtual QString dump() const;

virtual QStringList referencedColumns() const;
Expand Down

0 comments on commit f6e0ce7

Please sign in to comment.