mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
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.
This commit is contained in:
parent
f5de32779f
commit
f6e0ce788e
@ -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
|
||||
|
@ -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.
|
||||
@ -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 );
|
||||
@ -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 );
|
||||
|
||||
@ -114,7 +142,23 @@ 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.
|
||||
@ -122,6 +166,8 @@ class QgsExpression
|
||||
* @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 );
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -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 );
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
392
python/core/qgsexpressioncontext.sip
Normal file
392
python/core/qgsexpressioncontext.sip
Normal file
@ -0,0 +1,392 @@
|
||||
/** \ingroup core
|
||||
* \class QgsScopedExpressionFunction
|
||||
* \brief Expression function for use within a QgsExpressionContextScope. This differs from a
|
||||
* standard QgsExpression::Function in that it requires an implemented
|
||||
* clone() method.
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class QgsScopedExpressionFunction : QgsExpression::Function
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsexpressioncontext.h>
|
||||
%End
|
||||
public:
|
||||
QgsScopedExpressionFunction( QString fnname,
|
||||
int params,
|
||||
QString group,
|
||||
QString helpText = QString(),
|
||||
bool usesGeometry = false,
|
||||
QStringList referencedColumns = QStringList(),
|
||||
bool lazyEval = false,
|
||||
bool handlesNull = false );
|
||||
|
||||
virtual ~QgsScopedExpressionFunction();
|
||||
|
||||
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) = 0;
|
||||
|
||||
/** Returns a clone of the function.
|
||||
*/
|
||||
virtual QgsScopedExpressionFunction* clone() const = 0 /Factory/;
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsExpressionContextScope
|
||||
* \brief Single scope for storing variables and functions for use within a QgsExpressionContext.
|
||||
* Examples include a project's scope, which could contain information about the current project such as
|
||||
* the project file's location. QgsExpressionContextScope can encapsulate both variables (static values)
|
||||
* and functions(which are calculated only when an expression is evaluated).
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class QgsExpressionContextScope
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsexpressioncontext.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Single variable definition for use within a QgsExpressionContextScope.
|
||||
*/
|
||||
struct StaticVariable
|
||||
{
|
||||
/** Constructor for StaticVariable.
|
||||
* @param name variable name (should be unique within the QgsExpressionContextScope)
|
||||
* @param value intial variable value
|
||||
* @param readOnly true if variable should not be editable by users
|
||||
*/
|
||||
StaticVariable( const QString& name = QString(), const QVariant& value = QVariant(), bool readOnly = false );
|
||||
|
||||
/** Variable name */
|
||||
QString name;
|
||||
|
||||
/** Variable value */
|
||||
QVariant value;
|
||||
|
||||
/** True if variable should not be editable by users */
|
||||
bool readOnly;
|
||||
};
|
||||
|
||||
/** Constructor for QgsExpressionContextScope
|
||||
* @param name friendly display name for the context scope
|
||||
*/
|
||||
QgsExpressionContextScope( const QString& name = QString() );
|
||||
|
||||
QgsExpressionContextScope( const QgsExpressionContextScope& other );
|
||||
|
||||
~QgsExpressionContextScope();
|
||||
|
||||
/** Returns the friendly display name of the context scope.
|
||||
*/
|
||||
QString name() const;
|
||||
|
||||
/** Convenience method for setting a variable in the context scope by name and value. If a variable
|
||||
* with the same name is already set then its value is overwritten, otherwise a new variable is added to the scope.
|
||||
* @param name variable name
|
||||
* @param value variable value
|
||||
* @see addVariable()
|
||||
*/
|
||||
void setVariable( const QString& name, const QVariant& value );
|
||||
|
||||
/** Adds a variable into the context scope. If a variable with the same name is already set then its
|
||||
* value is overwritten, otherwise a new variable is added to the scope.
|
||||
* @param variable definition of variable to insert
|
||||
* @see setVariable()
|
||||
* @see addFunction()
|
||||
*/
|
||||
void addVariable( const QgsExpressionContextScope::StaticVariable& variable );
|
||||
|
||||
/** Removes a variable from the context scope, if found.
|
||||
* @param name name of variable to remove
|
||||
* @returns true if variable was removed from the scope, false if matching variable was not
|
||||
* found within the scope
|
||||
*/
|
||||
bool removeVariable( const QString& name );
|
||||
|
||||
/** Tests whether a variable with the specified name exists in the scope.
|
||||
* @param name variable name
|
||||
* @returns true if matching variable was found in the scope
|
||||
* @see variable()
|
||||
* @see hasFunction()
|
||||
*/
|
||||
bool hasVariable( const QString& name ) const;
|
||||
|
||||
/** Retrieves a variable's value from the scope.
|
||||
* @param name variable name
|
||||
* @returns variable value, or invalid QVariant if matching variable could not be found
|
||||
* @see hasVariable()
|
||||
* @see function()
|
||||
*/
|
||||
QVariant variable( const QString& name ) const;
|
||||
|
||||
/** Returns a list of variable names contained within the scope.
|
||||
*/
|
||||
QStringList variableNames() const;
|
||||
|
||||
/** Tests whether the specified variable is read only and should not be editable
|
||||
* by users.
|
||||
* @param name variable name
|
||||
* @returns true if variable is read only
|
||||
*/
|
||||
bool isReadOnly( const QString& name ) const;
|
||||
|
||||
/** Returns the count of variables contained within the scope.
|
||||
*/
|
||||
int variableCount() const;
|
||||
|
||||
/** Tests whether a function with the specified name exists in the scope.
|
||||
* @param name function name
|
||||
* @returns true if matching function was found in the scope
|
||||
* @see function()
|
||||
* @see hasFunction()
|
||||
*/
|
||||
bool hasFunction( const QString &name ) const;
|
||||
|
||||
/** Retrieves a function from the scope.
|
||||
* @param name function name
|
||||
* @returns function, or null if matching function could not be found
|
||||
* @see hasFunction()
|
||||
* @see variable()
|
||||
*/
|
||||
QgsExpression::Function* function( const QString &name ) const;
|
||||
|
||||
/** Adds a function to the scope.
|
||||
* @param name function name
|
||||
* @param function function to insert. Ownership is transferred to the scope.
|
||||
* @see addVariable()
|
||||
*/
|
||||
void addFunction( const QString& name, QgsScopedExpressionFunction* function /Transfer/ );
|
||||
|
||||
/** Convenience function for setting a feature for the scope. Any existing
|
||||
* feature set by the scope will be overwritten.
|
||||
* @param feature feature for scope
|
||||
*/
|
||||
void setFeature( const QgsFeature& feature );
|
||||
|
||||
/** Convenience function for setting a fields for the scope. Any existing
|
||||
* fields set by the scope will be overwritten.
|
||||
* @param fields fields for scope
|
||||
*/
|
||||
void setFields( const QgsFields& fields );
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsExpressionContext
|
||||
* \brief Expression contexts are used to encapsulate the parameters around which a QgsExpression should
|
||||
* be evaluated. QgsExpressions can then utilise the information stored within a context to contextualise
|
||||
* their evaluated result. A QgsExpressionContext consists of a stack of QgsExpressionContextScope objects,
|
||||
* where scopes added later to the stack will override conflicting variables and functions from scopes
|
||||
* lower in the stack.
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class QgsExpressionContext
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsexpressioncontext.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsExpressionContext( );
|
||||
QgsExpressionContext( const QgsExpressionContext& other );
|
||||
|
||||
~QgsExpressionContext();
|
||||
|
||||
/** Check whether a variable is specified by any scope within the context.
|
||||
* @param name variable name
|
||||
* @returns true if variable is set
|
||||
* @see variable()
|
||||
* @see variableNames()
|
||||
*/
|
||||
bool hasVariable( const QString& name ) const;
|
||||
|
||||
/** Fetches a matching variable from the context. The variable will be fetched
|
||||
* from the last scope contained within the context which has a matching
|
||||
* variable set.
|
||||
* @param name variable name
|
||||
* @returns variable value if matching variable exists in the context, otherwise an invalid QVariant
|
||||
* @see hasVariable()
|
||||
* @see variableNames()
|
||||
*/
|
||||
QVariant variable( const QString& name ) const;
|
||||
|
||||
/** Returns the currently active scope from the context for a specified variable name.
|
||||
* As scopes later in the stack override earlier contexts, this will be the last matching
|
||||
* scope which contains a matching variable.
|
||||
* @param name variable name
|
||||
* @returns matching scope containing variable, or null if none found
|
||||
*/
|
||||
QgsExpressionContextScope* activeScopeForVariable( const QString& name );
|
||||
|
||||
/** Returns the currently active scope from the context for a specified variable name.
|
||||
* As scopes later in the stack override earlier contexts, this will be the last matching
|
||||
* scope which contains a matching variable.
|
||||
* @param name variable name
|
||||
* @returns matching scope containing variable, or null if none found
|
||||
*/
|
||||
//const QgsExpressionContextScope* activeScopeForVariable( const QString& name ) const;
|
||||
|
||||
/** Returns the scope at the specified index within the context.
|
||||
* @param index index of scope
|
||||
* @returns matching scope, or null if none found
|
||||
* @see lastScope()
|
||||
*/
|
||||
QgsExpressionContextScope* scope( int index );
|
||||
|
||||
/** Returns the last scope added to the context.
|
||||
* @see scope()
|
||||
*/
|
||||
QgsExpressionContextScope* lastScope();
|
||||
|
||||
/** Returns a list of scopes contained within the stack.
|
||||
* @returns list of pointers to scopes
|
||||
*/
|
||||
QList< QgsExpressionContextScope* > scopes();
|
||||
|
||||
/** Returns the index of the specified scope if it exists within the context.
|
||||
* @param scope scope to find
|
||||
* @returns index of scope, or -1 if scope was not found within the context.
|
||||
*/
|
||||
int indexOfScope( QgsExpressionContextScope* scope ) const;
|
||||
|
||||
/** Returns a list of variables names set by all scopes in the context.
|
||||
* @returns list of unique variable names
|
||||
* @see hasVariable
|
||||
* @see variable
|
||||
*/
|
||||
QStringList variableNames() const;
|
||||
|
||||
/** Returns whether a variable is read only, and should not be modifiable by users.
|
||||
* @param name variable name
|
||||
* @returns true if variable is read only. Read only status will be taken from last
|
||||
* matching scope which contains a matching variable.
|
||||
*/
|
||||
bool isReadOnly( const QString& name ) const;
|
||||
|
||||
/** Checks whether a specified function is contained in the context.
|
||||
* @param name function name
|
||||
* @returns true if context provides a matching function
|
||||
* @see function
|
||||
*/
|
||||
bool hasFunction( const QString& name ) const;
|
||||
|
||||
/** Fetches a matching function from the context. The function will be fetched
|
||||
* from the last scope contained within the context which has a matching
|
||||
* function set.
|
||||
* @param name function name
|
||||
* @returns function if contained by the context, otherwise null.
|
||||
* @see hasFunction
|
||||
*/
|
||||
QgsExpression::Function* function( const QString& name ) const;
|
||||
|
||||
/** Returns the number of scopes contained in the context.
|
||||
*/
|
||||
int scopeCount() const;
|
||||
|
||||
/** Appends a scope to the end of the context. This scope will override
|
||||
* any matching variables or functions provided by existing scopes within the
|
||||
* context. Ownership of the scope is transferred to the stack.
|
||||
* @param scope expression context to append to context
|
||||
*/
|
||||
void appendScope( QgsExpressionContextScope* scope /Transfer/ );
|
||||
|
||||
/** Appends a scope to the end of the context. This scope will override
|
||||
* any matching variables or functions provided by existing scopes within the
|
||||
* context. Ownership of the scope is transferred to the stack.
|
||||
*/
|
||||
QgsExpressionContext& operator<< ( QgsExpressionContextScope* scope /Transfer/ );
|
||||
|
||||
/** Convenience function for setting a feature for the context. The feature
|
||||
* will be set within the last scope of the context, so will override any
|
||||
* existing features within the context.
|
||||
* @param feature feature for context
|
||||
*/
|
||||
void setFeature( const QgsFeature& feature );
|
||||
|
||||
/** Convenience function for setting a fields for the context. The fields
|
||||
* will be set within the last scope of the context, so will override any
|
||||
* existing fields within the context.
|
||||
* @param fields fields for context
|
||||
*/
|
||||
void setFields( const QgsFields& fields );
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsExpressionContextUtils
|
||||
* \brief Contains utilities for working with QgsExpressionContext objects, including methods
|
||||
* for creating scopes for specific uses (eg project scopes, layer scopes).
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class QgsExpressionContextUtils
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgsexpressioncontext.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Creates a new scope which contains variables and functions relating to the global QGIS context.
|
||||
* For instance, QGIS version numbers and variables specified through QGIS options.
|
||||
* @see setGlobalVariable()
|
||||
*/
|
||||
static QgsExpressionContextScope* globalScope() /Factory/;
|
||||
|
||||
/** Sets a global context variable. This variable will be contained within scopes retrieved via
|
||||
* globalScope().
|
||||
* @param name variable name
|
||||
* @param value variable value
|
||||
* @see setGlobalVariable()
|
||||
* @see globalScope()
|
||||
*/
|
||||
static void setGlobalVariable( const QString& name, const QVariant& value );
|
||||
|
||||
/** Sets all global context variables. Existing global variables will be removed and replaced
|
||||
* with the variables specified.
|
||||
* @param variables new set of global variables
|
||||
* @see setGlobalVariable()
|
||||
* @see globalScope()
|
||||
*/
|
||||
static void setGlobalVariables( const QgsStringMap& variables );
|
||||
|
||||
/** Creates a new scope which contains variables and functions relating to the current QGIS project.
|
||||
* For instance, project path and title, and variables specified through the project properties.
|
||||
* @see setProjectVariable()
|
||||
*/
|
||||
static QgsExpressionContextScope* projectScope() /Factory/;
|
||||
|
||||
/** Sets a project context variable. This variable will be contained within scopes retrieved via
|
||||
* projectScope().
|
||||
* @param name variable name
|
||||
* @param value variable value
|
||||
* @see setProjectVariables()
|
||||
* @see projectScope()
|
||||
*/
|
||||
static void setProjectVariable( const QString& name, const QVariant& value );
|
||||
|
||||
/** Sets all project context variables. Existing project variables will be removed and replaced
|
||||
* with the variables specified.
|
||||
* @param variables new set of project variables
|
||||
* @see setProjectVariable()
|
||||
* @see projectScope()
|
||||
*/
|
||||
static void setProjectVariables( const QgsStringMap& variables );
|
||||
|
||||
/** Creates a new scope which contains variables and functions relating to a QgsMapLayer.
|
||||
* For instance, layer name, id and fields.
|
||||
*/
|
||||
static QgsExpressionContextScope* layerScope( QgsMapLayer* layer ) /Factory/;
|
||||
|
||||
/** Helper function for creating an expression context which contains just a feature and fields
|
||||
* collection. Generally this method should not be used as the created context does not include
|
||||
* standard scopes such as the global and project scopes.
|
||||
*/
|
||||
static QgsExpressionContext createFeatureBasedContext( const QgsFeature& feature, const QgsFields& fields );
|
||||
|
||||
/** Registers all known core functions provided by QgsExpressionContextScope objects.
|
||||
*/
|
||||
static void registerContextFunctions();
|
||||
};
|
||||
|
@ -75,6 +75,11 @@ class QgsProject : QObject
|
||||
QString fileName() const;
|
||||
//@}
|
||||
|
||||
/** Returns QFileInfo object for the project's associated file.
|
||||
* @note added in QGIS 2.9
|
||||
*/
|
||||
QFileInfo fileInfo() const;
|
||||
|
||||
/** Clear the project
|
||||
* @note added in 2.4
|
||||
*/
|
||||
|
@ -94,6 +94,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgsdistancearea.cpp
|
||||
qgserror.cpp
|
||||
qgsexpression.cpp
|
||||
qgsexpressioncontext.cpp
|
||||
qgsexpression_texts.cpp
|
||||
qgsexpressionfieldbuffer.cpp
|
||||
qgsfeature.cpp
|
||||
@ -537,6 +538,7 @@ SET(QGIS_CORE_HDRS
|
||||
qgserror.h
|
||||
qgsexception.h
|
||||
qgsexpression.h
|
||||
qgsexpressioncontext.h
|
||||
qgsexpressionfieldbuffer.h
|
||||
qgsfeature.h
|
||||
qgsfeature_p.h
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,7 @@
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QDomDocument>
|
||||
#include "qgis.h"
|
||||
|
||||
class QgsFeature;
|
||||
class QgsGeometry;
|
||||
@ -31,6 +32,7 @@ class QgsField;
|
||||
class QgsFields;
|
||||
class QgsDistanceArea;
|
||||
class QDomElement;
|
||||
class QgsExpressionContext;
|
||||
|
||||
/**
|
||||
Class for parsing and evaluation of expressions (formerly called "search strings").
|
||||
@ -100,7 +102,13 @@ class CORE_EXPORT QgsExpression
|
||||
const Node* rootNode() const { return mRootNode; }
|
||||
|
||||
//! Get the expression ready for evaluation - find out column indexes.
|
||||
bool prepare( const QgsFields &fields );
|
||||
Q_DECL_DEPRECATED bool prepare( const QgsFields &fields );
|
||||
|
||||
/** 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.
|
||||
@ -117,28 +125,41 @@ class CORE_EXPORT QgsExpression
|
||||
|
||||
//! Evaluate the feature and return the result
|
||||
//! @note prepare() should be called before calling this method
|
||||
QVariant evaluate( const QgsFeature* f = NULL );
|
||||
Q_DECL_DEPRECATED QVariant evaluate( const QgsFeature* f );
|
||||
|
||||
//! Evaluate the feature and return the result
|
||||
//! @note prepare() should be called before calling this method
|
||||
//! @note available in python bindings as evaluatePrepared
|
||||
inline QVariant evaluate( const QgsFeature& f ) { return evaluate( &f ); }
|
||||
Q_DECL_DEPRECATED inline QVariant evaluate( const QgsFeature& f ) { Q_NOWARN_DEPRECATED_PUSH return evaluate( &f ); Q_NOWARN_DEPRECATED_POP }
|
||||
|
||||
//! 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 );
|
||||
Q_DECL_DEPRECATED QVariant evaluate( const QgsFeature* f, const QgsFields& fields );
|
||||
|
||||
//! 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 ); }
|
||||
Q_DECL_DEPRECATED inline QVariant evaluate( const QgsFeature& f, const QgsFields& 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 { return !mEvalErrorString.isNull(); }
|
||||
//! Returns evaluation error
|
||||
QString evalErrorString() const { return mEvalErrorString; }
|
||||
//! Set evaluation error (used internally by evaluation functions)
|
||||
void setEvalErrorString( QString str ) { mEvalErrorString = str; }
|
||||
void setEvalErrorString( const QString& str ) { mEvalErrorString = str; }
|
||||
|
||||
//! Set the number for $rownum special column
|
||||
void setCurrentRowNumber( int rowNumber ) { mRowNumber = rowNumber; }
|
||||
@ -160,7 +181,16 @@ class CORE_EXPORT QgsExpression
|
||||
*/
|
||||
bool isField() const { return rootNode() && dynamic_cast<const NodeColumnRef*>( rootNode() ) ;}
|
||||
|
||||
static bool isValid( const QString& text, const QgsFields& fields, QString &errorMessage );
|
||||
Q_DECL_DEPRECATED static bool isValid( const QString& text, const QgsFields& fields, QString &errorMessage );
|
||||
|
||||
/** 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 ) { mScale = scale; }
|
||||
|
||||
@ -199,8 +229,24 @@ class CORE_EXPORT QgsExpression
|
||||
@param distanceArea optional QgsDistanceArea. If specified, the QgsDistanceArea is used for distance
|
||||
and area conversion
|
||||
*/
|
||||
static QString replaceExpressionText( const QString &action, const QgsFeature *feat,
|
||||
QgsVectorLayer *layer,
|
||||
Q_DECL_DEPRECATED static QString replaceExpressionText( const QString &action, const QgsFeature *feat,
|
||||
QgsVectorLayer *layer,
|
||||
const QMap<QString, QVariant> *substitutionMap = 0,
|
||||
const QgsDistanceArea* distanceArea = 0
|
||||
);
|
||||
|
||||
/** 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
|
||||
);
|
||||
@ -211,6 +257,8 @@ class CORE_EXPORT QgsExpression
|
||||
* @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 );
|
||||
|
||||
@ -278,7 +326,7 @@ class CORE_EXPORT QgsExpression
|
||||
static const char* UnaryOperatorText[];
|
||||
|
||||
typedef QVariant( *FcnEval )( const QVariantList& values, const QgsFeature* f, QgsExpression* parent );
|
||||
|
||||
typedef QVariant( *FcnEvalContext )( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent );
|
||||
|
||||
/**
|
||||
* A abstract base class for defining QgsExpression functions.
|
||||
@ -332,7 +380,15 @@ class CORE_EXPORT QgsExpression
|
||||
/** The help text for the function. */
|
||||
const QString helptext() { return mHelpText.isEmpty() ? QgsExpression::helptext( mName ) : mHelpText; }
|
||||
|
||||
virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) = 0;
|
||||
Q_DECL_DEPRECATED virtual QVariant func( const QVariantList&, const QgsFeature*, QgsExpression* );
|
||||
|
||||
/** 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 );
|
||||
|
||||
bool operator==( const Function& other ) const
|
||||
{
|
||||
@ -358,9 +414,28 @@ class CORE_EXPORT QgsExpression
|
||||
class StaticFunction : public Function
|
||||
{
|
||||
public:
|
||||
Q_DECL_DEPRECATED StaticFunction( QString fnname,
|
||||
int params,
|
||||
FcnEval fcn,
|
||||
QString group,
|
||||
QString helpText = QString(),
|
||||
bool usesGeometry = false,
|
||||
QStringList referencedColumns = QStringList(),
|
||||
bool lazyEval = false,
|
||||
const QStringList& aliases = QStringList(),
|
||||
bool handlesNull = false )
|
||||
: Function( fnname, params, group, helpText, usesGeometry, referencedColumns, lazyEval, handlesNull )
|
||||
, mFnc( fcn )
|
||||
, mAliases( aliases )
|
||||
{}
|
||||
|
||||
virtual ~StaticFunction() {}
|
||||
|
||||
/** Static function for evaluation against a QgsExpressionContext
|
||||
*/
|
||||
StaticFunction( QString fnname,
|
||||
int params,
|
||||
FcnEval fcn,
|
||||
FcnEvalContext fcn,
|
||||
QString group,
|
||||
QString helpText = QString(),
|
||||
bool usesGeometry = false,
|
||||
@ -369,21 +444,32 @@ class CORE_EXPORT QgsExpression
|
||||
const QStringList& aliases = QStringList(),
|
||||
bool handlesNull = false )
|
||||
: Function( fnname, params, group, helpText, usesGeometry, referencedColumns, lazyEval, handlesNull )
|
||||
, mFnc( fcn )
|
||||
, mContextFnc( fcn )
|
||||
, mAliases( aliases )
|
||||
{}
|
||||
|
||||
virtual ~StaticFunction() {}
|
||||
|
||||
virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) override
|
||||
Q_DECL_DEPRECATED virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) override
|
||||
{
|
||||
Q_NOWARN_DEPRECATED_PUSH
|
||||
return mFnc( values, f, parent );
|
||||
Q_NOWARN_DEPRECATED_POP
|
||||
}
|
||||
|
||||
/** 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 ) override
|
||||
{
|
||||
return mContextFnc( values, context, parent );
|
||||
}
|
||||
|
||||
virtual QStringList aliases() const override { return mAliases; }
|
||||
|
||||
private:
|
||||
FcnEval mFnc;
|
||||
FcnEvalContext mContextFnc;
|
||||
QStringList mAliases;
|
||||
};
|
||||
|
||||
@ -397,7 +483,7 @@ class CORE_EXPORT 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 );
|
||||
@ -443,17 +529,32 @@ class CORE_EXPORT QgsExpression
|
||||
* @return The type of this node
|
||||
*/
|
||||
virtual NodeType nodeType() const = 0;
|
||||
/**
|
||||
|
||||
/**
|
||||
* Abstract virtual eval method
|
||||
* Errors are reported to the parent
|
||||
*/
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) = 0;
|
||||
Q_DECL_DEPRECATED virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
|
||||
|
||||
/**
|
||||
* Abstract virtual eval method
|
||||
* Errors are reported to the parent
|
||||
* @note added in QGIS 2.12
|
||||
*/
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
|
||||
|
||||
/**
|
||||
* Abstract virtual preparation method
|
||||
* Errors are reported to the parent
|
||||
*/
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) = 0;
|
||||
Q_DECL_DEPRECATED virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
|
||||
|
||||
/**
|
||||
* Abstract virtual preparation method
|
||||
* Errors are reported to the parent
|
||||
* @note added in QGIS 2.12
|
||||
*/
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
|
||||
|
||||
/**
|
||||
* Abstract virtual dump method
|
||||
@ -558,8 +659,8 @@ class CORE_EXPORT QgsExpression
|
||||
Node* operand() const { return mOperand; }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntUnaryOperator; }
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override { return mOperand->referencedColumns(); }
|
||||
@ -582,8 +683,8 @@ class CORE_EXPORT QgsExpression
|
||||
Node* opRight() const { return mOpRight; }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntBinaryOperator; }
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override { return mOpLeft->referencedColumns() + mOpRight->referencedColumns(); }
|
||||
@ -615,8 +716,8 @@ class CORE_EXPORT QgsExpression
|
||||
NodeList* list() const { return mList; }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntInOperator; }
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override { QStringList lst( mNode->referencedColumns() ); foreach ( Node* n, mList->list() ) lst.append( n->referencedColumns() ); return lst; }
|
||||
@ -640,8 +741,8 @@ class CORE_EXPORT QgsExpression
|
||||
NodeList* args() const { return mArgs; }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntFunction; }
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override;
|
||||
@ -652,6 +753,10 @@ class CORE_EXPORT QgsExpression
|
||||
//QString mName;
|
||||
int mFnIndex;
|
||||
NodeList* mArgs;
|
||||
|
||||
private:
|
||||
|
||||
QgsExpression::Function* getFunc() const;
|
||||
};
|
||||
|
||||
class CORE_EXPORT NodeLiteral : public Node
|
||||
@ -662,8 +767,8 @@ class CORE_EXPORT QgsExpression
|
||||
inline QVariant value() const { return mValue; }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntLiteral; }
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override { return QStringList(); }
|
||||
@ -682,8 +787,8 @@ class CORE_EXPORT QgsExpression
|
||||
QString name() const { return mName; }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntColumnRef; }
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override { return QStringList( mName ); }
|
||||
@ -715,8 +820,8 @@ class CORE_EXPORT QgsExpression
|
||||
~NodeCondition() { delete mElseExp; qDeleteAll( mConditions ); }
|
||||
|
||||
virtual NodeType nodeType() const override { return ntCondition; }
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) override;
|
||||
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context ) override;
|
||||
virtual QString dump() const override;
|
||||
|
||||
virtual QStringList referencedColumns() const override;
|
||||
|
523
src/core/qgsexpressioncontext.cpp
Normal file
523
src/core/qgsexpressioncontext.cpp
Normal file
@ -0,0 +1,523 @@
|
||||
/***************************************************************************
|
||||
qgsexpressioncontext.cpp
|
||||
------------------------
|
||||
Date : April 2015
|
||||
Copyright : (C) 2015 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsexpressioncontext.h"
|
||||
|
||||
#include "qgslogger.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsfield.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgssymbollayerv2utils.h"
|
||||
#include <QSettings>
|
||||
#include <QDir>
|
||||
|
||||
|
||||
const QString QgsExpressionContext::EXPR_FIELDS( "_fields_" );
|
||||
const QString QgsExpressionContext::EXPR_FEATURE( "_feature_" );
|
||||
|
||||
//
|
||||
// QgsExpressionContextScope
|
||||
//
|
||||
|
||||
QgsExpressionContextScope::QgsExpressionContextScope( const QString& name )
|
||||
: mName( name )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QgsExpressionContextScope::QgsExpressionContextScope( const QgsExpressionContextScope& other )
|
||||
: mName( other.mName )
|
||||
, mVariables( other.mVariables )
|
||||
{
|
||||
Q_FOREACH ( QString key, other.mFunctions.keys() )
|
||||
{
|
||||
mFunctions.insert( key, other.mFunctions.value( key )->clone() );
|
||||
}
|
||||
}
|
||||
|
||||
QgsExpressionContextScope& QgsExpressionContextScope::operator=( const QgsExpressionContextScope & other )
|
||||
{
|
||||
mName = other.mName;
|
||||
mVariables = other.mVariables;
|
||||
|
||||
qDeleteAll( mFunctions );
|
||||
mFunctions.clear();
|
||||
Q_FOREACH ( QString key, other.mFunctions.keys() )
|
||||
{
|
||||
mFunctions.insert( key, other.mFunctions.value( key )->clone() );
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QgsExpressionContextScope::~QgsExpressionContextScope()
|
||||
{
|
||||
qDeleteAll( mFunctions );
|
||||
}
|
||||
|
||||
void QgsExpressionContextScope::setVariable( const QString &name, const QVariant &value )
|
||||
{
|
||||
if ( mVariables.contains( name ) )
|
||||
{
|
||||
StaticVariable existing = mVariables.value( name );
|
||||
existing.value = value;
|
||||
addVariable( existing );
|
||||
}
|
||||
else
|
||||
{
|
||||
addVariable( QgsExpressionContextScope::StaticVariable( name, value ) );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsExpressionContextScope::addVariable( const QgsExpressionContextScope::StaticVariable &variable )
|
||||
{
|
||||
mVariables.insert( variable.name, variable );
|
||||
}
|
||||
|
||||
bool QgsExpressionContextScope::removeVariable( const QString &name )
|
||||
{
|
||||
return mVariables.remove( name ) > 0;
|
||||
}
|
||||
|
||||
bool QgsExpressionContextScope::hasVariable( const QString &name ) const
|
||||
{
|
||||
return mVariables.contains( name );
|
||||
}
|
||||
|
||||
QVariant QgsExpressionContextScope::variable( const QString &name ) const
|
||||
{
|
||||
return hasVariable( name ) ? mVariables.value( name ).value : QVariant();
|
||||
}
|
||||
|
||||
QStringList QgsExpressionContextScope::variableNames() const
|
||||
{
|
||||
QStringList names = mVariables.keys();
|
||||
return names;
|
||||
}
|
||||
|
||||
bool QgsExpressionContextScope::isReadOnly( const QString &name ) const
|
||||
{
|
||||
return hasVariable( name ) ? mVariables.value( name ).readOnly : false;
|
||||
}
|
||||
|
||||
bool QgsExpressionContextScope::hasFunction( const QString& name ) const
|
||||
{
|
||||
return mFunctions.contains( name );
|
||||
}
|
||||
|
||||
QgsExpression::Function* QgsExpressionContextScope::function( const QString& name ) const
|
||||
{
|
||||
return mFunctions.contains( name ) ? mFunctions.value( name ) : 0;
|
||||
}
|
||||
|
||||
void QgsExpressionContextScope::addFunction( const QString& name, QgsScopedExpressionFunction* function )
|
||||
{
|
||||
mFunctions.insert( name, function );
|
||||
}
|
||||
|
||||
void QgsExpressionContextScope::setFeature( const QgsFeature &feature )
|
||||
{
|
||||
setVariable( QgsExpressionContext::EXPR_FEATURE, QVariant::fromValue( feature ) );
|
||||
}
|
||||
|
||||
void QgsExpressionContextScope::setFields( const QgsFields &fields )
|
||||
{
|
||||
setVariable( QgsExpressionContext::EXPR_FIELDS, QVariant::fromValue( fields ) );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsExpressionContext
|
||||
//
|
||||
|
||||
QgsExpressionContext::QgsExpressionContext( const QgsExpressionContext& other )
|
||||
{
|
||||
Q_FOREACH ( const QgsExpressionContextScope* scope, other.mStack )
|
||||
{
|
||||
mStack << new QgsExpressionContextScope( *scope );
|
||||
}
|
||||
}
|
||||
|
||||
QgsExpressionContext& QgsExpressionContext::operator=( const QgsExpressionContext & other )
|
||||
{
|
||||
qDeleteAll( mStack );
|
||||
mStack.clear();
|
||||
Q_FOREACH ( const QgsExpressionContextScope* scope, other.mStack )
|
||||
{
|
||||
mStack << new QgsExpressionContextScope( *scope );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
QgsExpressionContext::~QgsExpressionContext()
|
||||
{
|
||||
qDeleteAll( mStack );
|
||||
mStack.clear();
|
||||
}
|
||||
|
||||
bool QgsExpressionContext::hasVariable( const QString& name ) const
|
||||
{
|
||||
Q_FOREACH ( const QgsExpressionContextScope* scope, mStack )
|
||||
{
|
||||
if ( scope->hasVariable( name ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant QgsExpressionContext::variable( const QString& name ) const
|
||||
{
|
||||
const QgsExpressionContextScope* scope = activeScopeForVariable( name );
|
||||
return scope ? scope->variable( name ) : QVariant();
|
||||
}
|
||||
|
||||
const QgsExpressionContextScope* QgsExpressionContext::activeScopeForVariable( const QString& name ) const
|
||||
{
|
||||
//iterate through stack backwards, so that higher priority variables take precedence
|
||||
QList< QgsExpressionContextScope* >::const_iterator it = mStack.constEnd();
|
||||
while ( it != mStack.constBegin() )
|
||||
{
|
||||
--it;
|
||||
if (( *it )->hasVariable( name ) )
|
||||
return ( *it );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QgsExpressionContextScope* QgsExpressionContext::activeScopeForVariable( const QString& name )
|
||||
{
|
||||
//iterate through stack backwards, so that higher priority variables take precedence
|
||||
QList< QgsExpressionContextScope* >::iterator it = mStack.end();
|
||||
while ( it != mStack.begin() )
|
||||
{
|
||||
--it;
|
||||
if (( *it )->hasVariable( name ) )
|
||||
return ( *it );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QgsExpressionContextScope* QgsExpressionContext::scope( int index )
|
||||
{
|
||||
if ( index < 0 || index >= mStack.count() )
|
||||
return 0;
|
||||
|
||||
return mStack[index];
|
||||
}
|
||||
|
||||
QgsExpressionContextScope *QgsExpressionContext::lastScope()
|
||||
{
|
||||
if ( mStack.count() < 1 )
|
||||
return 0;
|
||||
|
||||
return mStack.last();
|
||||
}
|
||||
|
||||
int QgsExpressionContext::indexOfScope( QgsExpressionContextScope* scope ) const
|
||||
{
|
||||
if ( !scope )
|
||||
return -1;
|
||||
|
||||
return mStack.indexOf( scope );
|
||||
}
|
||||
|
||||
QStringList QgsExpressionContext::variableNames() const
|
||||
{
|
||||
QStringList names;
|
||||
Q_FOREACH ( const QgsExpressionContextScope* scope, mStack )
|
||||
{
|
||||
names << scope->variableNames();
|
||||
}
|
||||
return names.toSet().toList();
|
||||
}
|
||||
|
||||
bool QgsExpressionContext::isReadOnly( const QString& name ) const { Q_UNUSED( name ); return true; }
|
||||
|
||||
bool QgsExpressionContext::hasFunction( const QString &name ) const
|
||||
{
|
||||
Q_FOREACH ( const QgsExpressionContextScope* scope, mStack )
|
||||
{
|
||||
if ( scope->hasFunction( name ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsExpression::Function *QgsExpressionContext::function( const QString &name ) const
|
||||
{
|
||||
//iterate through stack backwards, so that higher priority variables take precedence
|
||||
QList< QgsExpressionContextScope* >::const_iterator it = mStack.constEnd();
|
||||
while ( it != mStack.constBegin() )
|
||||
{
|
||||
--it;
|
||||
if (( *it )->hasFunction( name ) )
|
||||
return ( *it )->function( name );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QgsExpressionContext::scopeCount() const
|
||||
{
|
||||
return mStack.count();
|
||||
}
|
||||
|
||||
void QgsExpressionContext::appendScope( QgsExpressionContextScope* scope )
|
||||
{
|
||||
mStack.append( scope );
|
||||
}
|
||||
|
||||
QgsExpressionContext& QgsExpressionContext::operator<<( QgsExpressionContextScope* scope )
|
||||
{
|
||||
mStack.append( scope );
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QgsExpressionContext::setFeature( const QgsFeature &feature )
|
||||
{
|
||||
if ( mStack.isEmpty() )
|
||||
mStack.append( new QgsExpressionContextScope() );
|
||||
|
||||
mStack.last()->setFeature( feature );
|
||||
}
|
||||
|
||||
void QgsExpressionContext::setFields( const QgsFields &fields )
|
||||
{
|
||||
if ( mStack.isEmpty() )
|
||||
mStack.append( new QgsExpressionContextScope() );
|
||||
|
||||
mStack.last()->setFields( fields );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsExpressionContextUtils
|
||||
//
|
||||
|
||||
QgsExpressionContextScope* QgsExpressionContextUtils::globalScope()
|
||||
{
|
||||
QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Global" ) );
|
||||
|
||||
//read values from QSettings
|
||||
QSettings settings;
|
||||
|
||||
//check if settings contains any variables
|
||||
if ( settings.contains( QString( "/variables/values" ) ) )
|
||||
{
|
||||
QList< QVariant > customVariableVariants = settings.value( QString( "/variables/values" ) ).toList();
|
||||
QList< QVariant > customVariableNames = settings.value( QString( "/variables/names" ) ).toList();
|
||||
int variableIndex = 0;
|
||||
for ( QList< QVariant >::const_iterator it = customVariableVariants.constBegin();
|
||||
it != customVariableVariants.constEnd(); ++it )
|
||||
{
|
||||
if ( variableIndex >= customVariableNames.length() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
QVariant value = ( *it );
|
||||
QString name = customVariableNames.at( variableIndex ).toString();
|
||||
|
||||
scope->setVariable( name, value );
|
||||
variableIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
//add some extra global variables
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_version", QGis::QGIS_VERSION, true ) );
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_version_no", QGis::QGIS_VERSION_INT, true ) );
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "qgis_release_name", QGis::QGIS_RELEASE_NAME, true ) );
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
void QgsExpressionContextUtils::setGlobalVariable( const QString& name, const QVariant& value )
|
||||
{
|
||||
// save variable to settings
|
||||
QSettings settings;
|
||||
|
||||
QList< QVariant > customVariableVariants = settings.value( QString( "/variables/values" ) ).toList();
|
||||
QList< QVariant > customVariableNames = settings.value( QString( "/variables/names" ) ).toList();
|
||||
|
||||
customVariableVariants << value;
|
||||
customVariableNames << name;
|
||||
|
||||
settings.setValue( QString( "/variables/names" ), customVariableNames );
|
||||
settings.setValue( QString( "/variables/values" ), customVariableVariants );
|
||||
}
|
||||
|
||||
void QgsExpressionContextUtils::setGlobalVariables( const QgsStringMap &variables )
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
QList< QVariant > customVariableVariants;
|
||||
QList< QVariant > customVariableNames;
|
||||
|
||||
Q_FOREACH ( QString variable, variables.keys() )
|
||||
{
|
||||
customVariableNames << variable;
|
||||
customVariableVariants << variables.value( variable );
|
||||
}
|
||||
|
||||
settings.setValue( QString( "/variables/names" ), customVariableNames );
|
||||
settings.setValue( QString( "/variables/values" ), customVariableVariants );
|
||||
}
|
||||
|
||||
class GetNamedProjectColor : public QgsScopedExpressionFunction
|
||||
{
|
||||
public:
|
||||
GetNamedProjectColor()
|
||||
: QgsScopedExpressionFunction( "project_color", 1, "Colors" )
|
||||
{
|
||||
//build up color list from project. Do this in advance for speed
|
||||
QStringList colorStrings = QgsProject::instance()->readListEntry( "Palette", "/Colors" );
|
||||
QStringList colorLabels = QgsProject::instance()->readListEntry( "Palette", "/Labels" );
|
||||
|
||||
//generate list from custom colors
|
||||
int colorIndex = 0;
|
||||
for ( QStringList::iterator it = colorStrings.begin();
|
||||
it != colorStrings.end(); ++it )
|
||||
{
|
||||
QColor color = QgsSymbolLayerV2Utils::decodeColor( *it );
|
||||
QString label;
|
||||
if ( colorLabels.length() > colorIndex )
|
||||
{
|
||||
label = colorLabels.at( colorIndex );
|
||||
}
|
||||
|
||||
mColors.insert( label.toLower(), color );
|
||||
colorIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
virtual QVariant func( const QVariantList& values, const QgsExpressionContext*, QgsExpression* ) override
|
||||
{
|
||||
QString colorName = values.at( 0 ).toString().toLower();
|
||||
if ( mColors.contains( colorName ) )
|
||||
{
|
||||
return QString( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
|
||||
}
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QgsScopedExpressionFunction* clone() const override
|
||||
{
|
||||
return new GetNamedProjectColor();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
QHash< QString, QColor > mColors;
|
||||
|
||||
};
|
||||
|
||||
QgsExpressionContextScope* QgsExpressionContextUtils::projectScope()
|
||||
{
|
||||
QgsProject* project = QgsProject::instance();
|
||||
|
||||
QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Project" ) );
|
||||
|
||||
//add variables defined in project file
|
||||
QStringList variableNames = project->readListEntry( "Variables", "/variableNames" );
|
||||
QStringList variableValues = project->readListEntry( "Variables", "/variableValues" );
|
||||
|
||||
int varIndex = 0;
|
||||
foreach ( QString variableName, variableNames )
|
||||
{
|
||||
if ( varIndex >= variableValues.length() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
QString varValueString = variableValues.at( varIndex );
|
||||
varIndex++;
|
||||
scope->setVariable( variableName, varValueString );
|
||||
}
|
||||
|
||||
//add other known project variables
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "project_title", project->title(), true ) );
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "project_path", project->fileInfo().filePath(), true ) );
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "project_folder", project->fileInfo().dir().path(), true ) );
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "project_filename", project->fileInfo().fileName(), true ) );
|
||||
|
||||
scope->addFunction( "project_color", new GetNamedProjectColor() );
|
||||
return scope;
|
||||
}
|
||||
|
||||
void QgsExpressionContextUtils::setProjectVariable( const QString& name, const QVariant& value )
|
||||
{
|
||||
QgsProject* project = QgsProject::instance();
|
||||
|
||||
//write variable to project
|
||||
QStringList variableNames = project->readListEntry( "Variables", "/variableNames" );
|
||||
QStringList variableValues = project->readListEntry( "Variables", "/variableValues" );
|
||||
|
||||
variableNames << name;
|
||||
variableValues << value.toString();
|
||||
|
||||
project->writeEntry( "Variables", "/variableNames", variableNames );
|
||||
project->writeEntry( "Variables", "/variableValues", variableValues );
|
||||
}
|
||||
|
||||
void QgsExpressionContextUtils::setProjectVariables( const QgsStringMap &variables )
|
||||
{
|
||||
QgsProject* project = QgsProject::instance();
|
||||
|
||||
//write variable to project
|
||||
QStringList variableNames;
|
||||
QStringList variableValues;
|
||||
|
||||
Q_FOREACH ( QString variable, variables.keys() )
|
||||
{
|
||||
variableNames << variable;
|
||||
variableValues << variables.value( variable );
|
||||
}
|
||||
|
||||
project->writeEntry( "Variables", "/variableNames", variableNames );
|
||||
project->writeEntry( "Variables", "/variableValues", variableValues );
|
||||
}
|
||||
|
||||
QgsExpressionContextScope* QgsExpressionContextUtils::layerScope( QgsMapLayer* layer )
|
||||
{
|
||||
QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) );
|
||||
|
||||
if ( !layer )
|
||||
return scope;
|
||||
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "layer_name", layer->name(), true ) );
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "layer_id", layer->id(), true ) );
|
||||
|
||||
QgsVectorLayer* vLayer = dynamic_cast< QgsVectorLayer* >( layer );
|
||||
if ( vLayer )
|
||||
{
|
||||
scope->addVariable( QgsExpressionContextScope::StaticVariable( "_fields_", QVariant::fromValue( vLayer->pendingFields() ), true ) );
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
QgsExpressionContext QgsExpressionContextUtils::createFeatureBasedContext( const QgsFeature &feature, const QgsFields &fields )
|
||||
{
|
||||
QgsExpressionContextScope* scope = new QgsExpressionContextScope();
|
||||
scope->setVariable( QString( "_feature_" ), QVariant::fromValue( feature ) );
|
||||
scope->setVariable( QString( "_fields_" ), QVariant::fromValue( fields ) );
|
||||
return QgsExpressionContext() << scope;
|
||||
}
|
||||
|
||||
void QgsExpressionContextUtils::registerContextFunctions()
|
||||
{
|
||||
QgsExpression::registerFunction( new GetNamedProjectColor() );
|
||||
}
|
426
src/core/qgsexpressioncontext.h
Normal file
426
src/core/qgsexpressioncontext.h
Normal file
@ -0,0 +1,426 @@
|
||||
/***************************************************************************
|
||||
qgsexpressioncontext.h
|
||||
----------------------
|
||||
Date : April 2015
|
||||
Copyright : (C) 2015 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSEXPRESSIONCONTEXT_H
|
||||
#define QGSEXPRESSIONCONTEXT_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QSet>
|
||||
#include "qgsexpression.h"
|
||||
|
||||
class QgsExpression;
|
||||
class QgsMapLayer;
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsScopedExpressionFunction
|
||||
* \brief Expression function for use within a QgsExpressionContextScope. This differs from a
|
||||
* standard QgsExpression::Function in that it requires an implemented
|
||||
* clone() method.
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class CORE_EXPORT QgsScopedExpressionFunction : public QgsExpression::Function
|
||||
{
|
||||
public:
|
||||
QgsScopedExpressionFunction( QString fnname,
|
||||
int params,
|
||||
QString group,
|
||||
QString helpText = QString(),
|
||||
bool usesGeometry = false,
|
||||
QStringList referencedColumns = QStringList(),
|
||||
bool lazyEval = false,
|
||||
bool handlesNull = false )
|
||||
: QgsExpression::Function( fnname, params, group, helpText, usesGeometry, referencedColumns, lazyEval, handlesNull )
|
||||
{}
|
||||
|
||||
virtual ~QgsScopedExpressionFunction() {}
|
||||
|
||||
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) override = 0;
|
||||
|
||||
/** Returns a clone of the function.
|
||||
*/
|
||||
virtual QgsScopedExpressionFunction* clone() const = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsExpressionContextScope
|
||||
* \brief Single scope for storing variables and functions for use within a QgsExpressionContext.
|
||||
* Examples include a project's scope, which could contain information about the current project such as
|
||||
* the project file's location. QgsExpressionContextScope can encapsulate both variables (static values)
|
||||
* and functions(which are calculated only when an expression is evaluated).
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class CORE_EXPORT QgsExpressionContextScope
|
||||
{
|
||||
public:
|
||||
|
||||
/** Single variable definition for use within a QgsExpressionContextScope.
|
||||
*/
|
||||
struct StaticVariable
|
||||
{
|
||||
/** Constructor for StaticVariable.
|
||||
* @param name variable name (should be unique within the QgsExpressionContextScope)
|
||||
* @param value intial variable value
|
||||
* @param readOnly true if variable should not be editable by users
|
||||
*/
|
||||
StaticVariable( const QString& name = QString(), const QVariant& value = QVariant(), bool readOnly = false ) : name( name ), value( value ), readOnly( readOnly ) {}
|
||||
|
||||
/** Variable name */
|
||||
QString name;
|
||||
|
||||
/** Variable value */
|
||||
QVariant value;
|
||||
|
||||
/** True if variable should not be editable by users */
|
||||
bool readOnly;
|
||||
};
|
||||
|
||||
/** Constructor for QgsExpressionContextScope
|
||||
* @param name friendly display name for the context scope
|
||||
*/
|
||||
QgsExpressionContextScope( const QString& name = QString() );
|
||||
|
||||
QgsExpressionContextScope( const QgsExpressionContextScope& other );
|
||||
|
||||
QgsExpressionContextScope& operator=( const QgsExpressionContextScope& other );
|
||||
|
||||
~QgsExpressionContextScope();
|
||||
|
||||
/** Returns the friendly display name of the context scope.
|
||||
*/
|
||||
QString name() const { return mName; }
|
||||
|
||||
/** Convenience method for setting a variable in the context scope by name and value. If a variable
|
||||
* with the same name is already set then its value is overwritten, otherwise a new variable is added to the scope.
|
||||
* @param name variable name
|
||||
* @param value variable value
|
||||
* @see addVariable()
|
||||
*/
|
||||
void setVariable( const QString& name, const QVariant& value );
|
||||
|
||||
/** Adds a variable into the context scope. If a variable with the same name is already set then its
|
||||
* value is overwritten, otherwise a new variable is added to the scope.
|
||||
* @param variable definition of variable to insert
|
||||
* @see setVariable()
|
||||
* @see addFunction()
|
||||
*/
|
||||
void addVariable( const QgsExpressionContextScope::StaticVariable& variable );
|
||||
|
||||
/** Removes a variable from the context scope, if found.
|
||||
* @param name name of variable to remove
|
||||
* @returns true if variable was removed from the scope, false if matching variable was not
|
||||
* found within the scope
|
||||
*/
|
||||
bool removeVariable( const QString& name );
|
||||
|
||||
/** Tests whether a variable with the specified name exists in the scope.
|
||||
* @param name variable name
|
||||
* @returns true if matching variable was found in the scope
|
||||
* @see variable()
|
||||
* @see hasFunction()
|
||||
*/
|
||||
bool hasVariable( const QString& name ) const;
|
||||
|
||||
/** Retrieves a variable's value from the scope.
|
||||
* @param name variable name
|
||||
* @returns variable value, or invalid QVariant if matching variable could not be found
|
||||
* @see hasVariable()
|
||||
* @see function()
|
||||
*/
|
||||
QVariant variable( const QString& name ) const;
|
||||
|
||||
/** Returns a list of variable names contained within the scope.
|
||||
*/
|
||||
QStringList variableNames() const;
|
||||
|
||||
/** Tests whether the specified variable is read only and should not be editable
|
||||
* by users.
|
||||
* @param name variable name
|
||||
* @returns true if variable is read only
|
||||
*/
|
||||
bool isReadOnly( const QString& name ) const;
|
||||
|
||||
/** Returns the count of variables contained within the scope.
|
||||
*/
|
||||
int variableCount() const { return mVariables.count(); }
|
||||
|
||||
/** Tests whether a function with the specified name exists in the scope.
|
||||
* @param name function name
|
||||
* @returns true if matching function was found in the scope
|
||||
* @see function()
|
||||
* @see hasFunction()
|
||||
*/
|
||||
bool hasFunction( const QString &name ) const;
|
||||
|
||||
/** Retrieves a function from the scope.
|
||||
* @param name function name
|
||||
* @returns function, or null if matching function could not be found
|
||||
* @see hasFunction()
|
||||
* @see variable()
|
||||
*/
|
||||
QgsExpression::Function* function( const QString &name ) const;
|
||||
|
||||
/** Adds a function to the scope.
|
||||
* @param name function name
|
||||
* @param function function to insert. Ownership is transferred to the scope.
|
||||
* @see addVariable()
|
||||
*/
|
||||
void addFunction( const QString& name, QgsScopedExpressionFunction* function );
|
||||
|
||||
/** Convenience function for setting a feature for the scope. Any existing
|
||||
* feature set by the scope will be overwritten.
|
||||
* @param feature feature for scope
|
||||
*/
|
||||
void setFeature( const QgsFeature& feature );
|
||||
|
||||
/** Convenience function for setting a fields for the scope. Any existing
|
||||
* fields set by the scope will be overwritten.
|
||||
* @param fields fields for scope
|
||||
*/
|
||||
void setFields( const QgsFields& fields );
|
||||
|
||||
private:
|
||||
QString mName;
|
||||
QHash<QString, StaticVariable> mVariables;
|
||||
QHash<QString, QgsScopedExpressionFunction* > mFunctions;
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsExpressionContext
|
||||
* \brief Expression contexts are used to encapsulate the parameters around which a QgsExpression should
|
||||
* be evaluated. QgsExpressions can then utilise the information stored within a context to contextualise
|
||||
* their evaluated result. A QgsExpressionContext consists of a stack of QgsExpressionContextScope objects,
|
||||
* where scopes added later to the stack will override conflicting variables and functions from scopes
|
||||
* lower in the stack.
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
class CORE_EXPORT QgsExpressionContext
|
||||
{
|
||||
public:
|
||||
|
||||
QgsExpressionContext( ) {}
|
||||
QgsExpressionContext( const QgsExpressionContext& other );
|
||||
QgsExpressionContext& operator=( const QgsExpressionContext& other );
|
||||
|
||||
~QgsExpressionContext();
|
||||
|
||||
/** Check whether a variable is specified by any scope within the context.
|
||||
* @param name variable name
|
||||
* @returns true if variable is set
|
||||
* @see variable()
|
||||
* @see variableNames()
|
||||
*/
|
||||
bool hasVariable( const QString& name ) const;
|
||||
|
||||
/** Fetches a matching variable from the context. The variable will be fetched
|
||||
* from the last scope contained within the context which has a matching
|
||||
* variable set.
|
||||
* @param name variable name
|
||||
* @returns variable value if matching variable exists in the context, otherwise an invalid QVariant
|
||||
* @see hasVariable()
|
||||
* @see variableNames()
|
||||
*/
|
||||
QVariant variable( const QString& name ) const;
|
||||
|
||||
/** Returns the currently active scope from the context for a specified variable name.
|
||||
* As scopes later in the stack override earlier contexts, this will be the last matching
|
||||
* scope which contains a matching variable.
|
||||
* @param name variable name
|
||||
* @returns matching scope containing variable, or null if none found
|
||||
*/
|
||||
QgsExpressionContextScope* activeScopeForVariable( const QString& name );
|
||||
|
||||
/** Returns the currently active scope from the context for a specified variable name.
|
||||
* As scopes later in the stack override earlier contexts, this will be the last matching
|
||||
* scope which contains a matching variable.
|
||||
* @param name variable name
|
||||
* @returns matching scope containing variable, or null if none found
|
||||
*/
|
||||
const QgsExpressionContextScope* activeScopeForVariable( const QString& name ) const;
|
||||
|
||||
/** Returns the scope at the specified index within the context.
|
||||
* @param index index of scope
|
||||
* @returns matching scope, or null if none found
|
||||
* @see lastScope()
|
||||
*/
|
||||
QgsExpressionContextScope* scope( int index );
|
||||
|
||||
/** Returns the last scope added to the context.
|
||||
* @see scope()
|
||||
*/
|
||||
QgsExpressionContextScope* lastScope();
|
||||
|
||||
/** Returns a list of scopes contained within the stack.
|
||||
* @returns list of pointers to scopes
|
||||
*/
|
||||
QList< QgsExpressionContextScope* > scopes() { return mStack; }
|
||||
|
||||
/** Returns the index of the specified scope if it exists within the context.
|
||||
* @param scope scope to find
|
||||
* @returns index of scope, or -1 if scope was not found within the context.
|
||||
*/
|
||||
int indexOfScope( QgsExpressionContextScope* scope ) const;
|
||||
|
||||
/** Returns a list of variables names set by all scopes in the context.
|
||||
* @returns list of unique variable names
|
||||
* @see hasVariable
|
||||
* @see variable
|
||||
*/
|
||||
QStringList variableNames() const;
|
||||
|
||||
/** Returns whether a variable is read only, and should not be modifiable by users.
|
||||
* @param name variable name
|
||||
* @returns true if variable is read only. Read only status will be taken from last
|
||||
* matching scope which contains a matching variable.
|
||||
*/
|
||||
bool isReadOnly( const QString& name ) const;
|
||||
|
||||
/** Checks whether a specified function is contained in the context.
|
||||
* @param name function name
|
||||
* @returns true if context provides a matching function
|
||||
* @see function
|
||||
*/
|
||||
bool hasFunction( const QString& name ) const;
|
||||
|
||||
/** Fetches a matching function from the context. The function will be fetched
|
||||
* from the last scope contained within the context which has a matching
|
||||
* function set.
|
||||
* @param name function name
|
||||
* @returns function if contained by the context, otherwise null.
|
||||
* @see hasFunction
|
||||
*/
|
||||
QgsExpression::Function* function( const QString& name ) const;
|
||||
|
||||
/** Returns the number of scopes contained in the context.
|
||||
*/
|
||||
int scopeCount() const;
|
||||
|
||||
/** Appends a scope to the end of the context. This scope will override
|
||||
* any matching variables or functions provided by existing scopes within the
|
||||
* context. Ownership of the scope is transferred to the stack.
|
||||
* @param scope expression context to append to context
|
||||
*/
|
||||
void appendScope( QgsExpressionContextScope* scope );
|
||||
|
||||
/** Appends a scope to the end of the context. This scope will override
|
||||
* any matching variables or functions provided by existing scopes within the
|
||||
* context. Ownership of the scope is transferred to the stack.
|
||||
*/
|
||||
QgsExpressionContext& operator<< ( QgsExpressionContextScope* scope );
|
||||
|
||||
/** Convenience function for setting a feature for the context. The feature
|
||||
* will be set within the last scope of the context, so will override any
|
||||
* existing features within the context.
|
||||
* @param feature feature for context
|
||||
*/
|
||||
void setFeature( const QgsFeature& feature );
|
||||
|
||||
/** Convenience function for setting a fields for the context. The fields
|
||||
* will be set within the last scope of the context, so will override any
|
||||
* existing fields within the context.
|
||||
* @param fields fields for context
|
||||
*/
|
||||
void setFields( const QgsFields& fields );
|
||||
|
||||
static const QString EXPR_FIELDS;
|
||||
static const QString EXPR_FEATURE;
|
||||
|
||||
private:
|
||||
|
||||
QList< QgsExpressionContextScope* > mStack;
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsExpressionContextUtils
|
||||
* \brief Contains utilities for working with QgsExpressionContext objects, including methods
|
||||
* for creating scopes for specific uses (eg project scopes, layer scopes).
|
||||
* \note added in QGIS 2.12
|
||||
*/
|
||||
|
||||
class CORE_EXPORT QgsExpressionContextUtils
|
||||
{
|
||||
public:
|
||||
|
||||
/** Creates a new scope which contains variables and functions relating to the global QGIS context.
|
||||
* For instance, QGIS version numbers and variables specified through QGIS options.
|
||||
* @see setGlobalVariable()
|
||||
*/
|
||||
static QgsExpressionContextScope* globalScope();
|
||||
|
||||
/** Sets a global context variable. This variable will be contained within scopes retrieved via
|
||||
* globalScope().
|
||||
* @param name variable name
|
||||
* @param value variable value
|
||||
* @see setGlobalVariable()
|
||||
* @see globalScope()
|
||||
*/
|
||||
static void setGlobalVariable( const QString& name, const QVariant& value );
|
||||
|
||||
/** Sets all global context variables. Existing global variables will be removed and replaced
|
||||
* with the variables specified.
|
||||
* @param variables new set of global variables
|
||||
* @see setGlobalVariable()
|
||||
* @see globalScope()
|
||||
*/
|
||||
static void setGlobalVariables( const QgsStringMap& variables );
|
||||
|
||||
/** Creates a new scope which contains variables and functions relating to the current QGIS project.
|
||||
* For instance, project path and title, and variables specified through the project properties.
|
||||
* @see setProjectVariable()
|
||||
*/
|
||||
static QgsExpressionContextScope* projectScope();
|
||||
|
||||
/** Sets a project context variable. This variable will be contained within scopes retrieved via
|
||||
* projectScope().
|
||||
* @param name variable name
|
||||
* @param value variable value
|
||||
* @see setProjectVariables()
|
||||
* @see projectScope()
|
||||
*/
|
||||
static void setProjectVariable( const QString& name, const QVariant& value );
|
||||
|
||||
/** Sets all project context variables. Existing project variables will be removed and replaced
|
||||
* with the variables specified.
|
||||
* @param variables new set of project variables
|
||||
* @see setProjectVariable()
|
||||
* @see projectScope()
|
||||
*/
|
||||
static void setProjectVariables( const QgsStringMap& variables );
|
||||
|
||||
/** Creates a new scope which contains variables and functions relating to a QgsMapLayer.
|
||||
* For instance, layer name, id and fields.
|
||||
*/
|
||||
static QgsExpressionContextScope* layerScope( QgsMapLayer* layer );
|
||||
|
||||
/** Helper function for creating an expression context which contains just a feature and fields
|
||||
* collection. Generally this method should not be used as the created context does not include
|
||||
* standard scopes such as the global and project scopes.
|
||||
*/
|
||||
static QgsExpressionContext createFeatureBasedContext( const QgsFeature& feature, const QgsFields& fields );
|
||||
|
||||
/** Registers all known core functions provided by QgsExpressionContextScope objects.
|
||||
*/
|
||||
static void registerContextFunctions();
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSEXPRESSIONCONTEXT_H
|
@ -108,6 +108,7 @@ col_next [A-Za-z0-9_]|{non_ascii}
|
||||
column_ref {col_first}{col_next}*
|
||||
|
||||
special_col "$"{column_ref}
|
||||
variable "@"{column_ref}
|
||||
|
||||
col_str_char "\"\""|[^\"]
|
||||
column_ref_quoted "\""{col_str_char}*"\""
|
||||
@ -195,6 +196,8 @@ string "'"{str_char}*"'"
|
||||
|
||||
{special_col} { TEXT; return SPECIAL_COL; }
|
||||
|
||||
{variable} { TEXT; return VARIABLE; }
|
||||
|
||||
{column_ref} { TEXT; return QgsExpression::isFunctionName(*yylval->text) ? FUNCTION : COLUMN_REF; }
|
||||
|
||||
{column_ref_quoted} { TEXT_FILTER(stripColumnRef); return COLUMN_REF; }
|
||||
|
@ -108,7 +108,7 @@ struct expression_parser_context
|
||||
// tokens for conditional expressions
|
||||
%token CASE WHEN THEN ELSE END
|
||||
|
||||
%token <text> STRING COLUMN_REF FUNCTION SPECIAL_COL
|
||||
%token <text> STRING COLUMN_REF FUNCTION SPECIAL_COL VARIABLE
|
||||
|
||||
%token COMMA
|
||||
|
||||
@ -253,6 +253,17 @@ expression:
|
||||
}
|
||||
}
|
||||
|
||||
// variables
|
||||
| VARIABLE
|
||||
{
|
||||
// @var is equivalent to var( "var" )
|
||||
QgsExpression::NodeList* args = new QgsExpression::NodeList();
|
||||
QgsExpression::NodeLiteral* literal = new QgsExpression::NodeLiteral( QString(*$1).mid(1) );
|
||||
args->append( literal );
|
||||
$$ = new QgsExpression::NodeFunction( QgsExpression::functionIndex( "var" ), args );
|
||||
delete $1;
|
||||
}
|
||||
|
||||
// literals
|
||||
| NUMBER_FLOAT { $$ = new QgsExpression::NodeLiteral( QVariant($1) ); }
|
||||
| NUMBER_INT { $$ = new QgsExpression::NodeLiteral( QVariant($1) ); }
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <QDomNode>
|
||||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
|
||||
// canonical project instance
|
||||
QgsProject *QgsProject::theProject_ = 0;
|
||||
@ -333,7 +334,6 @@ QgsProject::QgsProject()
|
||||
// whenever layers are added to or removed from the registry,
|
||||
// layer tree will be updated
|
||||
mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this );
|
||||
|
||||
} // QgsProject ctor
|
||||
|
||||
|
||||
@ -402,7 +402,12 @@ void QgsProject::setFileName( QString const &name )
|
||||
QString QgsProject::fileName() const
|
||||
{
|
||||
return imp_->file.fileName();
|
||||
} // QString QgsProject::fileName() const
|
||||
}
|
||||
|
||||
QFileInfo QgsProject::fileInfo() const
|
||||
{
|
||||
return QFileInfo( imp_->file );
|
||||
}
|
||||
|
||||
void QgsProject::clear()
|
||||
{
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QFileInfo>
|
||||
|
||||
//for the snap settings
|
||||
#include "qgssnapper.h"
|
||||
@ -121,6 +122,11 @@ class CORE_EXPORT QgsProject : public QObject
|
||||
QString fileName() const;
|
||||
//@}
|
||||
|
||||
/** Returns QFileInfo object for the project's associated file.
|
||||
* @note added in QGIS 2.9
|
||||
*/
|
||||
QFileInfo fileInfo() const;
|
||||
|
||||
/** Clear the project
|
||||
* @note added in 2.4
|
||||
*/
|
||||
|
@ -78,6 +78,7 @@ ENDMACRO (ADD_QGIS_TEST)
|
||||
# Tests:
|
||||
|
||||
ADD_QGIS_TEST(qgistest testqgis.cpp)
|
||||
ADD_QGIS_TEST(expressioncontext testqgsexpressioncontext.cpp)
|
||||
ADD_QGIS_TEST(clippertest testqgsclipper.cpp)
|
||||
ADD_QGIS_TEST(distanceareatest testqgsdistancearea.cpp)
|
||||
ADD_QGIS_TEST(applicationtest testqgsapplication.cpp)
|
||||
|
579
tests/src/core/testqgsexpressioncontext.cpp
Normal file
579
tests/src/core/testqgsexpressioncontext.cpp
Normal file
@ -0,0 +1,579 @@
|
||||
/***************************************************************************
|
||||
testqgsexpressioncontext.cpp
|
||||
----------------------------
|
||||
begin : April 2015
|
||||
copyright : (C) 2015 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsexpressioncontext.h"
|
||||
#include "qgsexpression.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgscolorscheme.h"
|
||||
#include <QObject>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class TestQgsExpressionContext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init();// will be called before each testfunction is executed.
|
||||
void cleanup();// will be called after every testfunction.
|
||||
void contextScope();
|
||||
void contextScopeCopy();
|
||||
void contextScopeFunctions();
|
||||
void contextStack();
|
||||
void contextCopy();
|
||||
void contextStackFunctions();
|
||||
void evaluate();
|
||||
void setFeature();
|
||||
void setFields();
|
||||
|
||||
void globalScope();
|
||||
void projectScope();
|
||||
void layerScope();
|
||||
void featureBasedContext();
|
||||
|
||||
private:
|
||||
|
||||
class GetTestValueFunction : public QgsScopedExpressionFunction
|
||||
{
|
||||
public:
|
||||
GetTestValueFunction()
|
||||
: QgsScopedExpressionFunction( "get_test_value", 1, "test" ) {}
|
||||
|
||||
virtual QVariant func( const QVariantList&, const QgsExpressionContext*, QgsExpression* ) override
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
QgsScopedExpressionFunction* clone() const override
|
||||
{
|
||||
return new GetTestValueFunction();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class GetTestValueFunction2 : public QgsScopedExpressionFunction
|
||||
{
|
||||
public:
|
||||
GetTestValueFunction2()
|
||||
: QgsScopedExpressionFunction( "get_test_value", 1, "test" ) {}
|
||||
|
||||
virtual QVariant func( const QVariantList&, const QgsExpressionContext*, QgsExpression* ) override
|
||||
{
|
||||
return 43;
|
||||
}
|
||||
|
||||
QgsScopedExpressionFunction* clone() const override
|
||||
{
|
||||
return new GetTestValueFunction2();
|
||||
}
|
||||
};
|
||||
|
||||
class ModifiableFunction : public QgsScopedExpressionFunction
|
||||
{
|
||||
public:
|
||||
ModifiableFunction( int* v )
|
||||
: QgsScopedExpressionFunction( "test_function", 1, "test" )
|
||||
, mVal( v )
|
||||
{}
|
||||
|
||||
virtual QVariant func( const QVariantList&, const QgsExpressionContext*, QgsExpression* ) override
|
||||
{
|
||||
if ( !mVal )
|
||||
return QVariant();
|
||||
|
||||
return ++( *mVal );
|
||||
}
|
||||
|
||||
QgsScopedExpressionFunction* clone() const override
|
||||
{
|
||||
return new ModifiableFunction( mVal );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
int* mVal;
|
||||
};
|
||||
};
|
||||
|
||||
void TestQgsExpressionContext::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
|
||||
// Set up the QSettings environment
|
||||
QCoreApplication::setOrganizationName( "QGIS" );
|
||||
QCoreApplication::setOrganizationDomain( "qgis.org" );
|
||||
QCoreApplication::setApplicationName( "QGIS-TEST" );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::contextScope()
|
||||
{
|
||||
QgsExpressionContextScope scope( "scope name" );
|
||||
QCOMPARE( scope.name(), QString( "scope name" ) );
|
||||
|
||||
QVERIFY( !scope.hasVariable( "test" ) );
|
||||
QVERIFY( !scope.variable( "test" ).isValid() );
|
||||
QCOMPARE( scope.variableNames().length(), 0 );
|
||||
QCOMPARE( scope.variableCount(), 0 );
|
||||
|
||||
scope.setVariable( "test", 5 );
|
||||
QVERIFY( scope.hasVariable( "test" ) );
|
||||
QVERIFY( scope.variable( "test" ).isValid() );
|
||||
QCOMPARE( scope.variable( "test" ).toInt(), 5 );
|
||||
QCOMPARE( scope.variableNames().length(), 1 );
|
||||
QCOMPARE( scope.variableCount(), 1 );
|
||||
QCOMPARE( scope.variableNames().at( 0 ), QString( "test" ) );
|
||||
|
||||
scope.addVariable( QgsExpressionContextScope::StaticVariable( "readonly", QString( "readonly_test" ), true ) );
|
||||
QVERIFY( scope.isReadOnly( "readonly" ) );
|
||||
scope.addVariable( QgsExpressionContextScope::StaticVariable( "notreadonly", QString( "not_readonly_test" ), false ) );
|
||||
QVERIFY( !scope.isReadOnly( "notreadonly" ) );
|
||||
|
||||
//updating a read only variable should remain read only
|
||||
scope.setVariable( "readonly", "newvalue" );
|
||||
QVERIFY( scope.isReadOnly( "readonly" ) );
|
||||
|
||||
//removal
|
||||
scope.setVariable( "toremove", 5 );
|
||||
QVERIFY( scope.hasVariable( "toremove" ) );
|
||||
QVERIFY( !scope.removeVariable( "missing" ) );
|
||||
QVERIFY( scope.removeVariable( "toremove" ) );
|
||||
QVERIFY( !scope.hasVariable( "toremove" ) );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::contextScopeCopy()
|
||||
{
|
||||
QgsExpressionContextScope scope( "scope name" );
|
||||
scope.setVariable( "test", 5 );
|
||||
scope.addFunction( "get_test_value", new GetTestValueFunction() );
|
||||
|
||||
//copy constructor
|
||||
QgsExpressionContextScope copy( scope );
|
||||
QCOMPARE( copy.name(), QString( "scope name" ) );
|
||||
QVERIFY( copy.hasVariable( "test" ) );
|
||||
QCOMPARE( copy.variable( "test" ).toInt(), 5 );
|
||||
QCOMPARE( copy.variableNames().length(), 1 );
|
||||
QCOMPARE( copy.variableCount(), 1 );
|
||||
QCOMPARE( copy.variableNames().at( 0 ), QString( "test" ) );
|
||||
QVERIFY( copy.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( copy.function( "get_test_value" ) );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::contextScopeFunctions()
|
||||
{
|
||||
QgsExpressionContextScope scope;
|
||||
|
||||
QVERIFY( !scope.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( !scope.function( "get_test_value" ) );
|
||||
|
||||
scope.addFunction( "get_test_value", new GetTestValueFunction() );
|
||||
QVERIFY( scope.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( scope.function( "get_test_value" ) );
|
||||
QgsExpressionContext temp;
|
||||
QCOMPARE( scope.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::contextStack()
|
||||
{
|
||||
QgsExpressionContext context;
|
||||
//test retrieving from empty context
|
||||
QVERIFY( !context.hasVariable( "test" ) );
|
||||
QVERIFY( !context.variable( "test" ).isValid() );
|
||||
QCOMPARE( context.variableNames().length(), 0 );
|
||||
QCOMPARE( context.scopeCount(), 0 );
|
||||
QVERIFY( !context.scope( 0 ) );
|
||||
QVERIFY( !context.lastScope() );
|
||||
|
||||
//add a scope to the context
|
||||
QgsExpressionContextScope* testScope = new QgsExpressionContextScope();
|
||||
context << testScope;
|
||||
QVERIFY( !context.hasVariable( "test" ) );
|
||||
QVERIFY( !context.variable( "test" ).isValid() );
|
||||
QCOMPARE( context.variableNames().length(), 0 );
|
||||
QCOMPARE( context.scopeCount(), 1 );
|
||||
QCOMPARE( context.scope( 0 ), testScope );
|
||||
QCOMPARE( context.lastScope(), testScope );
|
||||
|
||||
//some general context scope tests
|
||||
QVERIFY( !context.scope( -1 ) );
|
||||
QVERIFY( !context.scope( 5 ) );
|
||||
QgsExpressionContextScope* scope1 = context.scope( 0 );
|
||||
QCOMPARE( context.indexOfScope( scope1 ), 0 );
|
||||
QgsExpressionContextScope scopeNotInContext;
|
||||
QCOMPARE( context.indexOfScope( &scopeNotInContext ), -1 );
|
||||
|
||||
//now add a variable to the first scope
|
||||
scope1->setVariable( "test", 1 );
|
||||
QVERIFY( context.hasVariable( "test" ) );
|
||||
QCOMPARE( context.variable( "test" ).toInt(), 1 );
|
||||
QCOMPARE( context.variableNames().length(), 1 );
|
||||
|
||||
//add a second scope, should override the first
|
||||
QgsExpressionContextScope* scope2 = new QgsExpressionContextScope();
|
||||
context << scope2;
|
||||
QCOMPARE( context.scopeCount(), 2 );
|
||||
QCOMPARE( context.scope( 1 ), scope2 );
|
||||
QCOMPARE( context.lastScope(), scope2 );
|
||||
QCOMPARE( context.indexOfScope( scope2 ), 1 );
|
||||
//test without setting variable first...
|
||||
QVERIFY( context.hasVariable( "test" ) );
|
||||
QCOMPARE( context.variable( "test" ).toInt(), 1 );
|
||||
QCOMPARE( context.variableNames().length(), 1 );
|
||||
scope2->setVariable( "test", 2 );
|
||||
QVERIFY( context.hasVariable( "test" ) );
|
||||
QCOMPARE( context.variable( "test" ).toInt(), 2 );
|
||||
QCOMPARE( context.variableNames().length(), 1 );
|
||||
|
||||
//make sure context falls back to earlier scopes
|
||||
scope1->setVariable( "test2", 11 );
|
||||
QVERIFY( context.hasVariable( "test2" ) );
|
||||
QCOMPARE( context.variable( "test2" ).toInt(), 11 );
|
||||
QCOMPARE( context.variableNames().length(), 2 );
|
||||
|
||||
//test scopes method
|
||||
QList< QgsExpressionContextScope*> scopes = context.scopes();
|
||||
QCOMPARE( scopes.length(), 2 );
|
||||
QCOMPARE( scopes.at( 0 ), scope1 );
|
||||
QCOMPARE( scopes.at( 1 ), scope2 );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::contextCopy()
|
||||
{
|
||||
QgsExpressionContext context;
|
||||
context << new QgsExpressionContextScope();
|
||||
context.scope( 0 )->setVariable( "test", 1 );
|
||||
|
||||
//copy constructor
|
||||
QgsExpressionContext copy( context );
|
||||
QCOMPARE( copy.scopeCount(), 1 );
|
||||
QVERIFY( copy.hasVariable( "test" ) );
|
||||
QCOMPARE( copy.variable( "test" ).toInt(), 1 );
|
||||
QCOMPARE( copy.variableNames().length(), 1 );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::contextStackFunctions()
|
||||
{
|
||||
QgsExpression::registerFunction( new GetTestValueFunction() );
|
||||
QgsExpression::registerFunction( new GetTestValueFunction2() );
|
||||
|
||||
QgsExpressionContext context;
|
||||
//test retrieving from empty stack
|
||||
QVERIFY( !context.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( !context.function( "get_test_value" ) );
|
||||
|
||||
//add a scope to the context
|
||||
context << new QgsExpressionContextScope();
|
||||
QVERIFY( !context.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( !context.function( "get_test_value" ) );
|
||||
|
||||
//now add a function to the first scope
|
||||
QgsExpressionContextScope* scope1 = context.scope( 0 );
|
||||
scope1->addFunction( "get_test_value", new GetTestValueFunction() );
|
||||
QVERIFY( context.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( context.function( "get_test_value" ) );
|
||||
QgsExpressionContext temp;
|
||||
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
|
||||
|
||||
//add a second scope, should override the first
|
||||
context << new QgsExpressionContextScope();
|
||||
//test without setting function first...
|
||||
QVERIFY( context.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( context.function( "get_test_value" ) );
|
||||
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
|
||||
|
||||
//then set the variable so it overrides
|
||||
QgsExpressionContextScope* scope2 = context.scope( 1 );
|
||||
scope2->addFunction( "get_test_value", new GetTestValueFunction2() );
|
||||
QVERIFY( context.hasFunction( "get_test_value" ) );
|
||||
QVERIFY( context.function( "get_test_value" ) );
|
||||
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 43 );
|
||||
|
||||
//make sure stack falls back to earlier contexts
|
||||
scope2->addFunction( "get_test_value2", new GetTestValueFunction() );
|
||||
QVERIFY( context.hasFunction( "get_test_value2" ) );
|
||||
QVERIFY( context.function( "get_test_value2" ) );
|
||||
QCOMPARE( context.function( "get_test_value2" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::evaluate()
|
||||
{
|
||||
QgsExpression exp( "1 + 2" );
|
||||
QCOMPARE( exp.evaluate().toInt(), 3 );
|
||||
|
||||
QgsExpressionContext context;
|
||||
context << new QgsExpressionContextScope();
|
||||
|
||||
QgsExpressionContextScope* s = context.scope( 0 );
|
||||
s->setVariable( "test", 5 );
|
||||
QCOMPARE( exp.evaluate( &context ).toInt(), 3 );
|
||||
QgsExpression expWithVariable( "var('test')" );
|
||||
QCOMPARE( expWithVariable.evaluate( &context ).toInt(), 5 );
|
||||
s->setVariable( "test", 7 );
|
||||
QCOMPARE( expWithVariable.evaluate( &context ).toInt(), 7 );
|
||||
QgsExpression expWithVariable2( "var('test') + var('test2')" );
|
||||
s->setVariable( "test2", 9 );
|
||||
QCOMPARE( expWithVariable2.evaluate( &context ).toInt(), 16 );
|
||||
|
||||
QgsExpression expWithVariableBad( "var('bad')" );
|
||||
QVERIFY( !expWithVariableBad.evaluate( &context ).isValid() );
|
||||
|
||||
//test shorthand variables
|
||||
QgsExpression expShorthand( "@test" );
|
||||
QCOMPARE( expShorthand.evaluate( &context ).toInt(), 7 );
|
||||
QgsExpression expShorthandBad( "@bad" );
|
||||
QVERIFY( !expShorthandBad.evaluate( &context ).isValid() );
|
||||
|
||||
//test with a function provided by a context
|
||||
QgsExpression::registerFunction( new ModifiableFunction( 0 ) );
|
||||
QgsExpression testExpWContextFunction( "test_function(1)" );
|
||||
QVERIFY( !testExpWContextFunction.evaluate( ).isValid() );
|
||||
|
||||
int val1 = 5;
|
||||
s->addFunction( "test_function", new ModifiableFunction( &val1 ) );
|
||||
testExpWContextFunction.prepare( &context );
|
||||
QCOMPARE( testExpWContextFunction.evaluate( &context ).toInt(), 6 );
|
||||
QCOMPARE( testExpWContextFunction.evaluate( &context ).toInt(), 7 );
|
||||
QCOMPARE( val1, 7 );
|
||||
|
||||
//test with another context to ensure that expressions are evaulated against correct context
|
||||
QgsExpressionContext context2;
|
||||
context2 << new QgsExpressionContextScope();
|
||||
QgsExpressionContextScope* s2 = context2.scope( 0 );
|
||||
int val2 = 50;
|
||||
s2->addFunction( "test_function", new ModifiableFunction( &val2 ) );
|
||||
QCOMPARE( testExpWContextFunction.evaluate( &context2 ).toInt(), 51 );
|
||||
QCOMPARE( testExpWContextFunction.evaluate( &context2 ).toInt(), 52 );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::setFeature()
|
||||
{
|
||||
QgsFeature feature( 50LL );
|
||||
QgsExpressionContextScope scope;
|
||||
scope.setFeature( feature );
|
||||
QVERIFY( scope.hasVariable( QgsExpressionContext::EXPR_FEATURE ) );
|
||||
QCOMPARE(( qvariant_cast<QgsFeature>( scope.variable( QgsExpressionContext::EXPR_FEATURE ) ) ).id(), 50LL );
|
||||
|
||||
//test setting a feature in a context with no scopes
|
||||
QgsExpressionContext emptyContext;
|
||||
emptyContext.setFeature( feature );
|
||||
//setFeature should have created a scope
|
||||
QCOMPARE( emptyContext.scopeCount(), 1 );
|
||||
QVERIFY( emptyContext.hasVariable( QgsExpressionContext::EXPR_FEATURE ) );
|
||||
QCOMPARE(( qvariant_cast<QgsFeature>( emptyContext.variable( QgsExpressionContext::EXPR_FEATURE ) ) ).id(), 50LL );
|
||||
|
||||
QgsExpressionContext contextWithScope;
|
||||
contextWithScope << new QgsExpressionContextScope();
|
||||
contextWithScope.setFeature( feature );
|
||||
QCOMPARE( contextWithScope.scopeCount(), 1 );
|
||||
QVERIFY( contextWithScope.hasVariable( QgsExpressionContext::EXPR_FEATURE ) );
|
||||
QCOMPARE(( qvariant_cast<QgsFeature>( contextWithScope.variable( QgsExpressionContext::EXPR_FEATURE ) ) ).id(), 50LL );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::setFields()
|
||||
{
|
||||
QgsFields fields;
|
||||
QgsField field( "testfield" );
|
||||
fields.append( field );
|
||||
|
||||
QgsExpressionContextScope scope;
|
||||
scope.setFields( fields );
|
||||
QVERIFY( scope.hasVariable( QgsExpressionContext::EXPR_FIELDS ) );
|
||||
QCOMPARE(( qvariant_cast<QgsFields>( scope.variable( QgsExpressionContext::EXPR_FIELDS ) ) ).at( 0 ).name(), QString( "testfield" ) );
|
||||
|
||||
//test setting a fields in a context with no scopes
|
||||
QgsExpressionContext emptyContext;
|
||||
emptyContext.setFields( fields );
|
||||
//setFeature should have created a scope
|
||||
QCOMPARE( emptyContext.scopeCount(), 1 );
|
||||
QVERIFY( emptyContext.hasVariable( QgsExpressionContext::EXPR_FIELDS ) );
|
||||
QCOMPARE(( qvariant_cast<QgsFields>( emptyContext.variable( QgsExpressionContext::EXPR_FIELDS ) ) ).at( 0 ).name(), QString( "testfield" ) );
|
||||
|
||||
QgsExpressionContext contextWithScope;
|
||||
contextWithScope << new QgsExpressionContextScope();
|
||||
contextWithScope.setFields( fields );
|
||||
QCOMPARE( contextWithScope.scopeCount(), 1 );
|
||||
QVERIFY( contextWithScope.hasVariable( QgsExpressionContext::EXPR_FIELDS ) );
|
||||
QCOMPARE(( qvariant_cast<QgsFields>( contextWithScope.variable( QgsExpressionContext::EXPR_FIELDS ) ) ).at( 0 ).name(), QString( "testfield" ) );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::globalScope()
|
||||
{
|
||||
QgsExpressionContextUtils::setGlobalVariable( "test", "testval" );
|
||||
|
||||
QgsExpressionContext context;
|
||||
QgsExpressionContextScope* globalScope = QgsExpressionContextUtils::globalScope();
|
||||
context << globalScope;
|
||||
QCOMPARE( globalScope->name(), tr( "Global" ) );
|
||||
|
||||
QCOMPARE( context.variable( "test" ).toString(), QString( "testval" ) );
|
||||
|
||||
QgsExpression expGlobal( "var('test')" );
|
||||
QCOMPARE( expGlobal.evaluate( &context ).toString(), QString( "testval" ) );
|
||||
|
||||
//test some other recognized global variables
|
||||
QgsExpression expVersion( "var('qgis_version')" );
|
||||
QgsExpression expVersionNo( "var('qgis_version_no')" );
|
||||
QgsExpression expReleaseName( "var('qgis_release_name')" );
|
||||
|
||||
QCOMPARE( expVersion.evaluate( &context ).toString(), QString( QGis::QGIS_VERSION ) );
|
||||
QCOMPARE( expVersionNo.evaluate( &context ).toInt(), QGis::QGIS_VERSION_INT );
|
||||
QCOMPARE( expReleaseName.evaluate( &context ).toString(), QString( QGis::QGIS_RELEASE_NAME ) );
|
||||
|
||||
//test setGlobalVariables
|
||||
QgsStringMap vars;
|
||||
vars.insert( "newvar1", "val1" );
|
||||
vars.insert( "newvar2", "val2" );
|
||||
QgsExpressionContextUtils::setGlobalVariables( vars );
|
||||
QgsExpressionContextScope* globalScope2 = QgsExpressionContextUtils::globalScope();
|
||||
|
||||
QVERIFY( !globalScope2->hasVariable( "test" ) );
|
||||
QCOMPARE( globalScope2->variable( "newvar1" ).toString(), QString( "val1" ) );
|
||||
QCOMPARE( globalScope2->variable( "newvar2" ).toString(), QString( "val2" ) );
|
||||
|
||||
delete globalScope2;
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::projectScope()
|
||||
{
|
||||
QgsExpressionContextUtils::setProjectVariable( "test", "testval" );
|
||||
QgsExpressionContextUtils::setProjectVariable( "testdouble", 5.2 );
|
||||
|
||||
QgsExpressionContext context;
|
||||
QgsExpressionContextScope* scope = QgsExpressionContextUtils::projectScope();
|
||||
context << scope;
|
||||
QCOMPARE( scope->name(), tr( "Project" ) );
|
||||
|
||||
QCOMPARE( context.variable( "test" ).toString(), QString( "testval" ) );
|
||||
QCOMPARE( context.variable( "testdouble" ).toDouble(), 5.2 );
|
||||
|
||||
QgsExpression expProject( "var('test')" );
|
||||
QCOMPARE( expProject.evaluate( &context ).toString(), QString( "testval" ) );
|
||||
|
||||
//test clearing project variables
|
||||
QgsExpressionContextScope* projectScope = QgsExpressionContextUtils::projectScope();
|
||||
QVERIFY( projectScope->hasVariable( "test" ) );
|
||||
QgsProject::instance()->clear();
|
||||
delete projectScope;
|
||||
projectScope = QgsExpressionContextUtils::projectScope();
|
||||
QVERIFY( !projectScope->hasVariable( "test" ) );
|
||||
|
||||
//test a preset project variable
|
||||
QgsProject::instance()->setTitle( "test project" );
|
||||
delete projectScope;
|
||||
projectScope = QgsExpressionContextUtils::projectScope();
|
||||
QCOMPARE( projectScope->variable( "project_title" ).toString(), QString( "test project" ) );
|
||||
delete projectScope;
|
||||
projectScope = 0;
|
||||
|
||||
//test setProjectVariables
|
||||
QgsStringMap vars;
|
||||
vars.insert( "newvar1", "val1" );
|
||||
vars.insert( "newvar2", "val2" );
|
||||
QgsExpressionContextUtils::setProjectVariables( vars );
|
||||
projectScope = QgsExpressionContextUtils::projectScope();
|
||||
|
||||
QVERIFY( !projectScope->hasVariable( "test" ) );
|
||||
QCOMPARE( projectScope->variable( "newvar1" ).toString(), QString( "val1" ) );
|
||||
QCOMPARE( projectScope->variable( "newvar2" ).toString(), QString( "val2" ) );
|
||||
delete projectScope;
|
||||
projectScope = 0;
|
||||
|
||||
//test project scope functions
|
||||
|
||||
//project_color function
|
||||
QgsProjectColorScheme s;
|
||||
QgsNamedColorList colorList;
|
||||
colorList << qMakePair( QColor( 200, 255, 0 ), QString( "vomit yellow" ) );
|
||||
colorList << qMakePair( QColor( 30, 60, 20 ), QString( "murky depths of hades" ) );
|
||||
s.setColors( colorList );
|
||||
QgsExpressionContext contextColors;
|
||||
contextColors << QgsExpressionContextUtils::projectScope();
|
||||
|
||||
QgsExpression expProjectColor( "project_color('murky depths of hades')" );
|
||||
QCOMPARE( expProjectColor.evaluate( &contextColors ).toString(), QString( "30,60,20" ) );
|
||||
//matching color names should be case insensitive
|
||||
QgsExpression expProjectColorCaseInsensitive( "project_color('Murky Depths of hades')" );
|
||||
QCOMPARE( expProjectColorCaseInsensitive.evaluate( &contextColors ).toString(), QString( "30,60,20" ) );
|
||||
QgsExpression badProjectColor( "project_color('dusk falls in san juan del sur')" );
|
||||
QCOMPARE( badProjectColor.evaluate( &contextColors ), QVariant() );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::layerScope()
|
||||
{
|
||||
//test passing no layer - should be no crash
|
||||
QgsExpressionContextScope* layerScope = QgsExpressionContextUtils::layerScope( 0 );
|
||||
QCOMPARE( layerScope->name(), tr( "Layer" ) );
|
||||
QCOMPARE( layerScope->variableCount(), 0 );
|
||||
delete layerScope;
|
||||
layerScope = 0;
|
||||
|
||||
//create a map layer
|
||||
QScopedPointer<QgsVectorLayer> vectorLayer( new QgsVectorLayer( "Point?field=col1:integer&field=col2:integer&field=col3:integer", "test layer", "memory" ) );
|
||||
|
||||
QgsExpressionContext context;
|
||||
context << QgsExpressionContextUtils::layerScope( vectorLayer.data() );
|
||||
|
||||
QCOMPARE( context.variable( "layer_name" ).toString(), vectorLayer->name() );
|
||||
QCOMPARE( context.variable( "layer_id" ).toString(), vectorLayer->id() );
|
||||
|
||||
QgsExpression expProject( "var('layer_name')" );
|
||||
QCOMPARE( expProject.evaluate( &context ).toString(), vectorLayer->name() );
|
||||
|
||||
//check that fields were set
|
||||
QgsFields fromVar = qvariant_cast<QgsFields>( context.variable( QgsExpressionContext::EXPR_FIELDS ) );
|
||||
QCOMPARE( fromVar, vectorLayer->pendingFields() );
|
||||
}
|
||||
|
||||
void TestQgsExpressionContext::featureBasedContext()
|
||||
{
|
||||
QgsFields fields;
|
||||
fields.append( QgsField( "x1" ) );
|
||||
fields.append( QgsField( "x2" ) );
|
||||
fields.append( QgsField( "foo", QVariant::Int ) );
|
||||
|
||||
QgsFeature f;
|
||||
f.initAttributes( 3 );
|
||||
f.setAttribute( 2, QVariant( 20 ) );
|
||||
|
||||
QgsExpressionContext context = QgsExpressionContextUtils::createFeatureBasedContext( f, fields );
|
||||
|
||||
QgsFeature evalFeature = qvariant_cast<QgsFeature>( context.variable( "_feature_" ) );
|
||||
QgsFields evalFields = qvariant_cast<QgsFields>( context.variable( "_fields_" ) );
|
||||
QCOMPARE( evalFeature.attributes(), f.attributes() );
|
||||
QCOMPARE( evalFields, fields );
|
||||
}
|
||||
|
||||
QTEST_MAIN( TestQgsExpressionContext )
|
||||
#include "testqgsexpressioncontext.moc"
|
Loading…
x
Reference in New Issue
Block a user