Skip to content

Commit f6e0ce7

Browse files
committedAug 22, 2015
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.
1 parent f5de327 commit f6e0ce7

15 files changed

+2525
-252
lines changed
 

‎python/core/core.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
%Include qgseditorwidgetconfig.sip
3838
%Include qgserror.sip
3939
%Include qgsexpression.sip
40+
%Include qgsexpressioncontext.sip
4041
%Include qgsfeature.sip
4142
%Include qgsfeatureiterator.sip
4243
%Include qgsfeaturerequest.sip

‎python/core/qgsexpression.sip

Lines changed: 89 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ class QgsExpression
1717
const QgsExpression::Node* rootNode() const;
1818

1919
//! Get the expression ready for evaluation - find out column indexes.
20-
bool prepare( const QgsFields &fields );
20+
bool prepare( const QgsFields &fields ) /Deprecated/;
21+
22+
/** Get the expression ready for evaluation - find out column indexes.
23+
* @param context context for preparing expression
24+
* @note added in QGIS 2.12
25+
*/
26+
bool prepare( const QgsExpressionContext *context );
2127

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

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

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

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

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

59+
/** Evaluate the feature and return the result.
60+
* @note this method does not expect that prepare() has been called on this instance
61+
* @note added in QGIS 2.12
62+
*/
63+
QVariant evaluate();
64+
65+
/** Evaluate the expression against the specified context and return the result.
66+
* @param context context for evaluating expression
67+
* @note prepare() should be called before calling this method.
68+
* @note added in QGIS 2.12
69+
*/
70+
QVariant evaluate( const QgsExpressionContext* context );
71+
5372
//! Returns true if an error occurred when evaluating last input
5473
bool hasEvalError() const;
5574
//! Returns evaluation error
5675
QString evalErrorString() const;
5776
//! Set evaluation error (used internally by evaluation functions)
58-
void setEvalErrorString( QString str );
77+
void setEvalErrorString( const QString& str );
5978

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

80-
static bool isValid( const QString& text, const QgsFields& fields, QString &errorMessage );
99+
static bool isValid( const QString& text, const QgsFields& fields, QString &errorMessage ) /Deprecated/;
100+
101+
/** Tests whether a string is a valid expression.
102+
* @param text string to test
103+
* @param context optional expression context
104+
* @param errorMessage will be filled with any error message from the validation
105+
* @returns true if string is a valid expression
106+
* @note added in QGIS 2.12
107+
*/
108+
static bool isValid( const QString& text, const QgsExpressionContext* context, QString &errorMessage );
81109

82110
void setScale( double scale );
83111

@@ -114,14 +142,32 @@ class QgsExpression
114142
QgsVectorLayer *layer,
115143
const QMap<QString, QVariant> *substitutionMap = 0,
116144
const QgsDistanceArea* distanceArea = 0
117-
);
145+
) /Deprecated/;
146+
147+
/** This function replaces each expression between [% and %]
148+
in the string with the result of its evaluation with the specified context
149+
150+
Additional substitutions can be passed through the substitutionMap parameter
151+
@param action
152+
@param context expression context
153+
@param substitutionMap
154+
@param distanceArea optional QgsDistanceArea. If specified, the QgsDistanceArea is used for distance
155+
and area conversion
156+
@note added in QGIS 2.12
157+
*/
158+
static QString replaceExpressionText( const QString &action, const QgsExpressionContext* context,
159+
const QMap<QString, QVariant> *substitutionMap = 0,
160+
const QgsDistanceArea* distanceArea = 0
161+
);
118162

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

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

234-
virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) = 0;
280+
virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) /Deprecated/;
281+
282+
/** Returns result of evaluating the function.
283+
* @param values list of values passed to the function
284+
* @param context context expression is being evaluated against
285+
* @param parent parent expression
286+
* @returns result of function
287+
*/
288+
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent );
235289

236290
virtual bool handlesNull() const;
237291
};
@@ -243,7 +297,7 @@ class QgsExpression
243297
static bool unregisterFunction( QString name );
244298

245299
// tells whether the identifier is a name of existing function
246-
static bool isFunctionName( QString name );
300+
static bool isFunctionName( const QString& name );
247301

248302
// return index of the function in Functions array
249303
static int functionIndex( const QString& name );
@@ -297,11 +351,21 @@ class QgsExpression
297351
virtual QgsExpression::NodeType nodeType() const = 0;
298352
// abstract virtual eval function
299353
// errors are reported to the parent
300-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) = 0;
354+
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f ) /Deprecated/;
355+
356+
/** Evaluation function for nodes. Errors are reported to the parent.
357+
* @note added in QGIS 2.12
358+
*/
359+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
301360

302361
// abstract virtual preparation function
303362
// errors are reported to the parent
304-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) = 0;
363+
virtual bool prepare( QgsExpression* parent, const QgsFields &fields ) /Deprecated/;
364+
365+
/** Preparation function for nodes. Errors are reported to the parent.
366+
* @note added in QGIS 2.12
367+
*/
368+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
305369

306370
virtual QString dump() const = 0;
307371

@@ -357,8 +421,8 @@ class QgsExpression
357421
QgsExpression::Node* operand() const;
358422

359423
virtual QgsExpression::NodeType nodeType() const;
360-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
361-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
424+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
425+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
362426
virtual QString dump() const;
363427

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

379443
virtual QgsExpression::NodeType nodeType() const;
380-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
381-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
444+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
445+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
382446
virtual QString dump() const;
383447

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

402466
virtual QgsExpression::NodeType nodeType() const;
403-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
404-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
467+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
468+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
405469
virtual QString dump() const;
406470

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

422486
virtual QgsExpression::NodeType nodeType() const;
423-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
424-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
487+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
488+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
425489
virtual QString dump() const;
426490

427491
virtual QStringList referencedColumns() const;
@@ -437,8 +501,8 @@ class QgsExpression
437501
const QVariant& value() const;
438502

439503
virtual QgsExpression::NodeType nodeType() const;
440-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
441-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
504+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
505+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
442506
virtual QString dump() const;
443507

444508
virtual QStringList referencedColumns() const;
@@ -454,8 +518,8 @@ class QgsExpression
454518
QString name() const;
455519

456520
virtual QgsExpression::NodeType nodeType() const;
457-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
458-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
521+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
522+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
459523
virtual QString dump() const;
460524

461525
virtual QStringList referencedColumns() const;
@@ -482,8 +546,8 @@ class QgsExpression
482546
~NodeCondition();
483547

484548
virtual QgsExpression::NodeType nodeType() const;
485-
virtual QVariant eval( QgsExpression* parent, const QgsFeature* f );
486-
virtual bool prepare( QgsExpression* parent, const QgsFields &fields );
549+
virtual QVariant eval( QgsExpression* parent, const QgsExpressionContext* context );
550+
virtual bool prepare( QgsExpression* parent, const QgsExpressionContext* context );
487551
virtual QString dump() const;
488552

489553
virtual QStringList referencedColumns() const;

‎python/core/qgsexpressioncontext.sip

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
/** \ingroup core
2+
* \class QgsScopedExpressionFunction
3+
* \brief Expression function for use within a QgsExpressionContextScope. This differs from a
4+
* standard QgsExpression::Function in that it requires an implemented
5+
* clone() method.
6+
* \note added in QGIS 2.12
7+
*/
8+
9+
class QgsScopedExpressionFunction : QgsExpression::Function
10+
{
11+
%TypeHeaderCode
12+
#include <qgsexpressioncontext.h>
13+
%End
14+
public:
15+
QgsScopedExpressionFunction( QString fnname,
16+
int params,
17+
QString group,
18+
QString helpText = QString(),
19+
bool usesGeometry = false,
20+
QStringList referencedColumns = QStringList(),
21+
bool lazyEval = false,
22+
bool handlesNull = false );
23+
24+
virtual ~QgsScopedExpressionFunction();
25+
26+
virtual QVariant func( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) = 0;
27+
28+
/** Returns a clone of the function.
29+
*/
30+
virtual QgsScopedExpressionFunction* clone() const = 0 /Factory/;
31+
32+
};
33+
34+
/** \ingroup core
35+
* \class QgsExpressionContextScope
36+
* \brief Single scope for storing variables and functions for use within a QgsExpressionContext.
37+
* Examples include a project's scope, which could contain information about the current project such as
38+
* the project file's location. QgsExpressionContextScope can encapsulate both variables (static values)
39+
* and functions(which are calculated only when an expression is evaluated).
40+
* \note added in QGIS 2.12
41+
*/
42+
43+
class QgsExpressionContextScope
44+
{
45+
%TypeHeaderCode
46+
#include <qgsexpressioncontext.h>
47+
%End
48+
public:
49+
50+
/** Single variable definition for use within a QgsExpressionContextScope.
51+
*/
52+
struct StaticVariable
53+
{
54+
/** Constructor for StaticVariable.
55+
* @param name variable name (should be unique within the QgsExpressionContextScope)
56+
* @param value intial variable value
57+
* @param readOnly true if variable should not be editable by users
58+
*/
59+
StaticVariable( const QString& name = QString(), const QVariant& value = QVariant(), bool readOnly = false );
60+
61+
/** Variable name */
62+
QString name;
63+
64+
/** Variable value */
65+
QVariant value;
66+
67+
/** True if variable should not be editable by users */
68+
bool readOnly;
69+
};
70+
71+
/** Constructor for QgsExpressionContextScope
72+
* @param name friendly display name for the context scope
73+
*/
74+
QgsExpressionContextScope( const QString& name = QString() );
75+
76+
QgsExpressionContextScope( const QgsExpressionContextScope& other );
77+
78+
~QgsExpressionContextScope();
79+
80+
/** Returns the friendly display name of the context scope.
81+
*/
82+
QString name() const;
83+
84+
/** Convenience method for setting a variable in the context scope by name and value. If a variable
85+
* with the same name is already set then its value is overwritten, otherwise a new variable is added to the scope.
86+
* @param name variable name
87+
* @param value variable value
88+
* @see addVariable()
89+
*/
90+
void setVariable( const QString& name, const QVariant& value );
91+
92+
/** Adds a variable into the context scope. If a variable with the same name is already set then its
93+
* value is overwritten, otherwise a new variable is added to the scope.
94+
* @param variable definition of variable to insert
95+
* @see setVariable()
96+
* @see addFunction()
97+
*/
98+
void addVariable( const QgsExpressionContextScope::StaticVariable& variable );
99+
100+
/** Removes a variable from the context scope, if found.
101+
* @param name name of variable to remove
102+
* @returns true if variable was removed from the scope, false if matching variable was not
103+
* found within the scope
104+
*/
105+
bool removeVariable( const QString& name );
106+
107+
/** Tests whether a variable with the specified name exists in the scope.
108+
* @param name variable name
109+
* @returns true if matching variable was found in the scope
110+
* @see variable()
111+
* @see hasFunction()
112+
*/
113+
bool hasVariable( const QString& name ) const;
114+
115+
/** Retrieves a variable's value from the scope.
116+
* @param name variable name
117+
* @returns variable value, or invalid QVariant if matching variable could not be found
118+
* @see hasVariable()
119+
* @see function()
120+
*/
121+
QVariant variable( const QString& name ) const;
122+
123+
/** Returns a list of variable names contained within the scope.
124+
*/
125+
QStringList variableNames() const;
126+
127+
/** Tests whether the specified variable is read only and should not be editable
128+
* by users.
129+
* @param name variable name
130+
* @returns true if variable is read only
131+
*/
132+
bool isReadOnly( const QString& name ) const;
133+
134+
/** Returns the count of variables contained within the scope.
135+
*/
136+
int variableCount() const;
137+
138+
/** Tests whether a function with the specified name exists in the scope.
139+
* @param name function name
140+
* @returns true if matching function was found in the scope
141+
* @see function()
142+
* @see hasFunction()
143+
*/
144+
bool hasFunction( const QString &name ) const;
145+
146+
/** Retrieves a function from the scope.
147+
* @param name function name
148+
* @returns function, or null if matching function could not be found
149+
* @see hasFunction()
150+
* @see variable()
151+
*/
152+
QgsExpression::Function* function( const QString &name ) const;
153+
154+
/** Adds a function to the scope.
155+
* @param name function name
156+
* @param function function to insert. Ownership is transferred to the scope.
157+
* @see addVariable()
158+
*/
159+
void addFunction( const QString& name, QgsScopedExpressionFunction* function /Transfer/ );
160+
161+
/** Convenience function for setting a feature for the scope. Any existing
162+
* feature set by the scope will be overwritten.
163+
* @param feature feature for scope
164+
*/
165+
void setFeature( const QgsFeature& feature );
166+
167+
/** Convenience function for setting a fields for the scope. Any existing
168+
* fields set by the scope will be overwritten.
169+
* @param fields fields for scope
170+
*/
171+
void setFields( const QgsFields& fields );
172+
};
173+
174+
175+
/** \ingroup core
176+
* \class QgsExpressionContext
177+
* \brief Expression contexts are used to encapsulate the parameters around which a QgsExpression should
178+
* be evaluated. QgsExpressions can then utilise the information stored within a context to contextualise
179+
* their evaluated result. A QgsExpressionContext consists of a stack of QgsExpressionContextScope objects,
180+
* where scopes added later to the stack will override conflicting variables and functions from scopes
181+
* lower in the stack.
182+
* \note added in QGIS 2.12
183+
*/
184+
185+
class QgsExpressionContext
186+
{
187+
%TypeHeaderCode
188+
#include <qgsexpressioncontext.h>
189+
%End
190+
public:
191+
192+
QgsExpressionContext( );
193+
QgsExpressionContext( const QgsExpressionContext& other );
194+
195+
~QgsExpressionContext();
196+
197+
/** Check whether a variable is specified by any scope within the context.
198+
* @param name variable name
199+
* @returns true if variable is set
200+
* @see variable()
201+
* @see variableNames()
202+
*/
203+
bool hasVariable( const QString& name ) const;
204+
205+
/** Fetches a matching variable from the context. The variable will be fetched
206+
* from the last scope contained within the context which has a matching
207+
* variable set.
208+
* @param name variable name
209+
* @returns variable value if matching variable exists in the context, otherwise an invalid QVariant
210+
* @see hasVariable()
211+
* @see variableNames()
212+
*/
213+
QVariant variable( const QString& name ) const;
214+
215+
/** Returns the currently active scope from the context for a specified variable name.
216+
* As scopes later in the stack override earlier contexts, this will be the last matching
217+
* scope which contains a matching variable.
218+
* @param name variable name
219+
* @returns matching scope containing variable, or null if none found
220+
*/
221+
QgsExpressionContextScope* activeScopeForVariable( const QString& name );
222+
223+
/** Returns the currently active scope from the context for a specified variable name.
224+
* As scopes later in the stack override earlier contexts, this will be the last matching
225+
* scope which contains a matching variable.
226+
* @param name variable name
227+
* @returns matching scope containing variable, or null if none found
228+
*/
229+
//const QgsExpressionContextScope* activeScopeForVariable( const QString& name ) const;
230+
231+
/** Returns the scope at the specified index within the context.
232+
* @param index index of scope
233+
* @returns matching scope, or null if none found
234+
* @see lastScope()
235+
*/
236+
QgsExpressionContextScope* scope( int index );
237+
238+
/** Returns the last scope added to the context.
239+
* @see scope()
240+
*/
241+
QgsExpressionContextScope* lastScope();
242+
243+
/** Returns a list of scopes contained within the stack.
244+
* @returns list of pointers to scopes
245+
*/
246+
QList< QgsExpressionContextScope* > scopes();
247+
248+
/** Returns the index of the specified scope if it exists within the context.
249+
* @param scope scope to find
250+
* @returns index of scope, or -1 if scope was not found within the context.
251+
*/
252+
int indexOfScope( QgsExpressionContextScope* scope ) const;
253+
254+
/** Returns a list of variables names set by all scopes in the context.
255+
* @returns list of unique variable names
256+
* @see hasVariable
257+
* @see variable
258+
*/
259+
QStringList variableNames() const;
260+
261+
/** Returns whether a variable is read only, and should not be modifiable by users.
262+
* @param name variable name
263+
* @returns true if variable is read only. Read only status will be taken from last
264+
* matching scope which contains a matching variable.
265+
*/
266+
bool isReadOnly( const QString& name ) const;
267+
268+
/** Checks whether a specified function is contained in the context.
269+
* @param name function name
270+
* @returns true if context provides a matching function
271+
* @see function
272+
*/
273+
bool hasFunction( const QString& name ) const;
274+
275+
/** Fetches a matching function from the context. The function will be fetched
276+
* from the last scope contained within the context which has a matching
277+
* function set.
278+
* @param name function name
279+
* @returns function if contained by the context, otherwise null.
280+
* @see hasFunction
281+
*/
282+
QgsExpression::Function* function( const QString& name ) const;
283+
284+
/** Returns the number of scopes contained in the context.
285+
*/
286+
int scopeCount() const;
287+
288+
/** Appends a scope to the end of the context. This scope will override
289+
* any matching variables or functions provided by existing scopes within the
290+
* context. Ownership of the scope is transferred to the stack.
291+
* @param scope expression context to append to context
292+
*/
293+
void appendScope( QgsExpressionContextScope* scope /Transfer/ );
294+
295+
/** Appends a scope to the end of the context. This scope will override
296+
* any matching variables or functions provided by existing scopes within the
297+
* context. Ownership of the scope is transferred to the stack.
298+
*/
299+
QgsExpressionContext& operator<< ( QgsExpressionContextScope* scope /Transfer/ );
300+
301+
/** Convenience function for setting a feature for the context. The feature
302+
* will be set within the last scope of the context, so will override any
303+
* existing features within the context.
304+
* @param feature feature for context
305+
*/
306+
void setFeature( const QgsFeature& feature );
307+
308+
/** Convenience function for setting a fields for the context. The fields
309+
* will be set within the last scope of the context, so will override any
310+
* existing fields within the context.
311+
* @param fields fields for context
312+
*/
313+
void setFields( const QgsFields& fields );
314+
315+
};
316+
317+
/** \ingroup core
318+
* \class QgsExpressionContextUtils
319+
* \brief Contains utilities for working with QgsExpressionContext objects, including methods
320+
* for creating scopes for specific uses (eg project scopes, layer scopes).
321+
* \note added in QGIS 2.12
322+
*/
323+
324+
class QgsExpressionContextUtils
325+
{
326+
%TypeHeaderCode
327+
#include <qgsexpressioncontext.h>
328+
%End
329+
public:
330+
331+
/** Creates a new scope which contains variables and functions relating to the global QGIS context.
332+
* For instance, QGIS version numbers and variables specified through QGIS options.
333+
* @see setGlobalVariable()
334+
*/
335+
static QgsExpressionContextScope* globalScope() /Factory/;
336+
337+
/** Sets a global context variable. This variable will be contained within scopes retrieved via
338+
* globalScope().
339+
* @param name variable name
340+
* @param value variable value
341+
* @see setGlobalVariable()
342+
* @see globalScope()
343+
*/
344+
static void setGlobalVariable( const QString& name, const QVariant& value );
345+
346+
/** Sets all global context variables. Existing global variables will be removed and replaced
347+
* with the variables specified.
348+
* @param variables new set of global variables
349+
* @see setGlobalVariable()
350+
* @see globalScope()
351+
*/
352+
static void setGlobalVariables( const QgsStringMap& variables );
353+
354+
/** Creates a new scope which contains variables and functions relating to the current QGIS project.
355+
* For instance, project path and title, and variables specified through the project properties.
356+
* @see setProjectVariable()
357+
*/
358+
static QgsExpressionContextScope* projectScope() /Factory/;
359+
360+
/** Sets a project context variable. This variable will be contained within scopes retrieved via
361+
* projectScope().
362+
* @param name variable name
363+
* @param value variable value
364+
* @see setProjectVariables()
365+
* @see projectScope()
366+
*/
367+
static void setProjectVariable( const QString& name, const QVariant& value );
368+
369+
/** Sets all project context variables. Existing project variables will be removed and replaced
370+
* with the variables specified.
371+
* @param variables new set of project variables
372+
* @see setProjectVariable()
373+
* @see projectScope()
374+
*/
375+
static void setProjectVariables( const QgsStringMap& variables );
376+
377+
/** Creates a new scope which contains variables and functions relating to a QgsMapLayer.
378+
* For instance, layer name, id and fields.
379+
*/
380+
static QgsExpressionContextScope* layerScope( QgsMapLayer* layer ) /Factory/;
381+
382+
/** Helper function for creating an expression context which contains just a feature and fields
383+
* collection. Generally this method should not be used as the created context does not include
384+
* standard scopes such as the global and project scopes.
385+
*/
386+
static QgsExpressionContext createFeatureBasedContext( const QgsFeature& feature, const QgsFields& fields );
387+
388+
/** Registers all known core functions provided by QgsExpressionContextScope objects.
389+
*/
390+
static void registerContextFunctions();
391+
};
392+

‎python/core/qgsproject.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ class QgsProject : QObject
7575
QString fileName() const;
7676
//@}
7777

78+
/** Returns QFileInfo object for the project's associated file.
79+
* @note added in QGIS 2.9
80+
*/
81+
QFileInfo fileInfo() const;
82+
7883
/** Clear the project
7984
* @note added in 2.4
8085
*/

‎src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ SET(QGIS_CORE_SRCS
9494
qgsdistancearea.cpp
9595
qgserror.cpp
9696
qgsexpression.cpp
97+
qgsexpressioncontext.cpp
9798
qgsexpression_texts.cpp
9899
qgsexpressionfieldbuffer.cpp
99100
qgsfeature.cpp
@@ -537,6 +538,7 @@ SET(QGIS_CORE_HDRS
537538
qgserror.h
538539
qgsexception.h
539540
qgsexpression.h
541+
qgsexpressioncontext.h
540542
qgsexpressionfieldbuffer.h
541543
qgsfeature.h
542544
qgsfeature_p.h

‎src/core/qgsexpression.cpp

Lines changed: 340 additions & 190 deletions
Large diffs are not rendered by default.

‎src/core/qgsexpression.h

Lines changed: 139 additions & 34 deletions
Large diffs are not rendered by default.

‎src/core/qgsexpressioncontext.cpp

Lines changed: 523 additions & 0 deletions
Large diffs are not rendered by default.

‎src/core/qgsexpressioncontext.h

Lines changed: 426 additions & 0 deletions
Large diffs are not rendered by default.

‎src/core/qgsexpressionlexer.ll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ col_next [A-Za-z0-9_]|{non_ascii}
108108
column_ref {col_first}{col_next}*
109109

110110
special_col "$"{column_ref}
111+
variable "@"{column_ref}
111112

112113
col_str_char "\"\""|[^\"]
113114
column_ref_quoted "\""{col_str_char}*"\""
@@ -195,6 +196,8 @@ string "'"{str_char}*"'"
195196
196197
{special_col} { TEXT; return SPECIAL_COL; }
197198
199+
{variable} { TEXT; return VARIABLE; }
200+
198201
{column_ref} { TEXT; return QgsExpression::isFunctionName(*yylval->text) ? FUNCTION : COLUMN_REF; }
199202
200203
{column_ref_quoted} { TEXT_FILTER(stripColumnRef); return COLUMN_REF; }

‎src/core/qgsexpressionparser.yy

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct expression_parser_context
108108
// tokens for conditional expressions
109109
%token CASE WHEN THEN ELSE END
110110

111-
%token <text> STRING COLUMN_REF FUNCTION SPECIAL_COL
111+
%token <text> STRING COLUMN_REF FUNCTION SPECIAL_COL VARIABLE
112112

113113
%token COMMA
114114

@@ -253,6 +253,17 @@ expression:
253253
}
254254
}
255255

256+
// variables
257+
| VARIABLE
258+
{
259+
// @var is equivalent to var( "var" )
260+
QgsExpression::NodeList* args = new QgsExpression::NodeList();
261+
QgsExpression::NodeLiteral* literal = new QgsExpression::NodeLiteral( QString(*$1).mid(1) );
262+
args->append( literal );
263+
$$ = new QgsExpression::NodeFunction( QgsExpression::functionIndex( "var" ), args );
264+
delete $1;
265+
}
266+
256267
// literals
257268
| NUMBER_FLOAT { $$ = new QgsExpression::NodeLiteral( QVariant($1) ); }
258269
| NUMBER_INT { $$ = new QgsExpression::NodeLiteral( QVariant($1) ); }

‎src/core/qgsproject.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <QDomNode>
4444
#include <QObject>
4545
#include <QTextStream>
46+
#include <QDir>
4647

4748
// canonical project instance
4849
QgsProject *QgsProject::theProject_ = 0;
@@ -333,7 +334,6 @@ QgsProject::QgsProject()
333334
// whenever layers are added to or removed from the registry,
334335
// layer tree will be updated
335336
mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this );
336-
337337
} // QgsProject ctor
338338

339339

@@ -402,7 +402,12 @@ void QgsProject::setFileName( QString const &name )
402402
QString QgsProject::fileName() const
403403
{
404404
return imp_->file.fileName();
405-
} // QString QgsProject::fileName() const
405+
}
406+
407+
QFileInfo QgsProject::fileInfo() const
408+
{
409+
return QFileInfo( imp_->file );
410+
}
406411

407412
void QgsProject::clear()
408413
{

‎src/core/qgsproject.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <QList>
2828
#include <QObject>
2929
#include <QPair>
30+
#include <QFileInfo>
3031

3132
//for the snap settings
3233
#include "qgssnapper.h"
@@ -121,6 +122,11 @@ class CORE_EXPORT QgsProject : public QObject
121122
QString fileName() const;
122123
//@}
123124

125+
/** Returns QFileInfo object for the project's associated file.
126+
* @note added in QGIS 2.9
127+
*/
128+
QFileInfo fileInfo() const;
129+
124130
/** Clear the project
125131
* @note added in 2.4
126132
*/

‎tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ ENDMACRO (ADD_QGIS_TEST)
7878
# Tests:
7979

8080
ADD_QGIS_TEST(qgistest testqgis.cpp)
81+
ADD_QGIS_TEST(expressioncontext testqgsexpressioncontext.cpp)
8182
ADD_QGIS_TEST(clippertest testqgsclipper.cpp)
8283
ADD_QGIS_TEST(distanceareatest testqgsdistancearea.cpp)
8384
ADD_QGIS_TEST(applicationtest testqgsapplication.cpp)

‎tests/src/core/testqgsexpressioncontext.cpp

Lines changed: 579 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.