Skip to content

Commit cb8ae89

Browse files
authoredSep 29, 2017
Merge pull request #5269 from m-kuhn/represent_value
[expression] represent_value also determines implicitly provided column name
2 parents f981795 + ca5a0bb commit cb8ae89

16 files changed

+364
-292
lines changed
 

‎doc/api_break.dox

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,8 @@ QgsExpression::Function {#qgis_api_break_3_0_QgsExpression_Function}
11541154
- `QgsExpression::Function::usesgeometry()` has been renamed to `QgsExpression::Function::usesGeometry( const NodeFunction* node )`
11551155
- `QStringList QgsExpression::Function::referencedColumns()` has been changed to `QSet<QString> QgsExpression::Function::referencedColumns( const NodeFunction* node )`
11561156
- `QgsExpression::Function::helptext()` has been renamed to `helpText()`
1157+
- `QgsExpression::Function::func` has an additional parameter `node` that
1158+
provides access to the original node.
11571159

11581160
QgsExpressionItem {#qgis_api_break_3_0_QgsExpressionItem}
11591161
-----------------

‎python/core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def __init__(self, func, name, args, group, helptext='', usesGeometry=True,
8484
self.uses_geometry = usesGeometry
8585
self.referenced_columns = referencedColumns
8686

87-
def func(self, values, context, parent):
87+
def func(self, values, context, parent, node):
8888
feature = None
8989
if context:
9090
feature = context.feature()

‎python/core/expression/qgsexpressionfunction.sip

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,17 +239,18 @@ The help text for the function.
239239
:rtype: str
240240
%End
241241

242-
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) = 0;
242+
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) = 0;
243243
%Docstring
244244
Returns result of evaluating the function.
245245
\param values list of values passed to the function
246246
\param context context expression is being evaluated against
247247
\param parent parent expression
248+
\param node expression node
248249
:return: result of function
249250
:rtype: QVariant
250251
%End
251252

252-
virtual QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent );
253+
virtual QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node );
253254
%Docstring
254255
:rtype: QVariant
255256
%End

‎python/core/qgsexpressioncontext.sip

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class QgsScopedExpressionFunction : QgsExpressionFunction
5353
.. versionadded:: 3.0
5454
%End
5555

56-
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) = 0;
56+
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) = 0;
5757

5858
virtual QgsScopedExpressionFunction *clone() const = 0 /Factory/;
5959
%Docstring

‎resources/function_help/json/represent_value

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
"type": "function",
44
"description": "Returns the configured representation value for a field value. It depends on the configured widget type. Often, this is useful for 'Value Map' widgets.",
55
"arguments": [
6-
{"arg":"fieldName", "description": "The field name for which the widget configuration should be loaded."},
7-
{"arg":"value", "description": "The value which should be resolved. Most likely a field." }
6+
{"arg":"value", "description": "The value which should be resolved. Most likely a field." },
7+
{"arg":"fieldName", "description": "The field name for which the widget configuration should be loaded. (Optional)"}
88
],
99
"examples": [
10-
{ "expression":"represent_value('field_with_value_map', \"field_with_value_map\")", "returns":"Description for value"}
10+
{ "expression":"represent_value(\"field_with_value_map\")", "returns":"Description for value"},
11+
{ "expression":"represent_value('static value', 'field_name')", "returns":"Description for static value"}
1112
]
1213
}

‎src/core/expression/qgsexpression.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,15 @@ bool QgsExpression::isValid() const
240240
return d->mRootNode;
241241
}
242242

243-
bool QgsExpression::hasParserError() const { return !d->mParserErrorString.isNull(); }
243+
bool QgsExpression::hasParserError() const
244+
{
245+
return !d->mParserErrorString.isNull();
246+
}
244247

245-
QString QgsExpression::parserErrorString() const { return d->mParserErrorString; }
248+
QString QgsExpression::parserErrorString() const
249+
{
250+
return d->mParserErrorString;
251+
}
246252

247253
QSet<QString> QgsExpression::referencedColumns() const
248254
{

‎src/core/expression/qgsexpressionfunction.cpp

Lines changed: 298 additions & 256 deletions
Large diffs are not rendered by default.

‎src/core/expression/qgsexpressionfunction.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class CORE_EXPORT QgsExpressionFunction
4040

4141
/** Function definition for evaluation against an expression context, using a list of values as parameters to the function.
4242
*/
43-
typedef QVariant( *FcnEval )( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) SIP_SKIP;
43+
typedef QVariant( *FcnEval )( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) SIP_SKIP;
4444

4545
/** \ingroup core
4646
* Represents a single parameter passed to a function.
@@ -271,11 +271,12 @@ class CORE_EXPORT QgsExpressionFunction
271271
* \param values list of values passed to the function
272272
* \param context context expression is being evaluated against
273273
* \param parent parent expression
274+
* \param node expression node
274275
* \returns result of function
275276
*/
276-
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) = 0;
277+
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) = 0;
277278

278-
virtual QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent );
279+
virtual QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node );
279280

280281
bool operator==( const QgsExpressionFunction &other ) const;
281282

@@ -402,9 +403,9 @@ class QgsStaticExpressionFunction : public QgsExpressionFunction
402403
* \param parent parent expression
403404
* \returns result of function
404405
*/
405-
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) override
406+
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) override
406407
{
407-
return mFnc ? mFnc( values, context, parent ) : QVariant();
408+
return mFnc ? mFnc( values, context, parent, node ) : QVariant();
408409
}
409410

410411
virtual QStringList aliases() const override;
@@ -471,9 +472,9 @@ class QgsWithVariableExpressionFunction : public QgsExpressionFunction
471472

472473
bool isStatic( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const override;
473474

474-
QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent ) override;
475+
QVariant run( QgsExpressionNode::NodeList *args, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) override;
475476

476-
QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) override;
477+
QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) override;
477478

478479
bool prepare( const QgsExpressionNodeFunction *node, QgsExpression *parent, const QgsExpressionContext *context ) const override;
479480

‎src/core/expression/qgsexpressionnodeimpl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ QVariant QgsExpressionNodeFunction::evalNode( QgsExpression *parent, const QgsEx
846846
QString name = QgsExpression::QgsExpression::Functions()[mFnIndex]->name();
847847
QgsExpressionFunction *fd = context && context->hasFunction( name ) ? context->function( name ) : QgsExpression::QgsExpression::Functions()[mFnIndex];
848848

849-
QVariant res = fd->run( mArgs, context, parent );
849+
QVariant res = fd->run( mArgs, context, parent, this );
850850
ENSURE_NO_EVAL_ERROR;
851851

852852
// everything went fine

‎src/core/expression/qgsexpressionnodeimpl.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,6 @@ class CORE_EXPORT QgsExpressionNodeFunction : public QgsExpressionNode
241241
private:
242242
int mFnIndex;
243243
NodeList *mArgs = nullptr;
244-
245244
};
246245

247246
/** \ingroup core

‎src/core/qgsexpressioncontext.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ class GetNamedProjectColor : public QgsScopedExpressionFunction
626626
}
627627
}
628628

629-
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) override
629+
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
630630
{
631631
QString colorName = values.at( 0 ).toString().toLower();
632632
if ( mColors.contains( colorName ) )
@@ -657,7 +657,7 @@ class GetComposerItemVariables : public QgsScopedExpressionFunction
657657
, mComposition( c )
658658
{}
659659

660-
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) override
660+
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
661661
{
662662
if ( !mComposition )
663663
return QVariant();
@@ -692,7 +692,7 @@ class GetLayerVisibility : public QgsScopedExpressionFunction
692692
, mLayers( layers )
693693
{}
694694

695-
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) override
695+
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
696696
{
697697
if ( mLayers.isEmpty() )
698698
{
@@ -729,7 +729,7 @@ class GetProcessingParameterValue : public QgsScopedExpressionFunction
729729
, mParams( params )
730730
{}
731731

732-
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression * ) override
732+
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
733733
{
734734
return mParams.value( values.at( 0 ).toString() );
735735
}

‎src/core/qgsexpressioncontext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class CORE_EXPORT QgsScopedExpressionFunction : public QgsExpressionFunction
9191
, mReferencedColumns( referencedColumns )
9292
{}
9393

94-
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent ) override = 0;
94+
virtual QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *parent, const QgsExpressionNodeFunction *node ) override = 0;
9595

9696
/** Returns a clone of the function.
9797
*/

‎src/providers/virtual/qgsvirtuallayersqlitemodule.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,8 @@ void qgisFunctionWrapper( sqlite3_context *ctxt, int nArgs, sqlite3_value **args
777777
};
778778
}
779779

780-
QgsExpression parentExpr( QLatin1String( "" ) );
781-
QVariant ret = foo->func( variants, &qgisFunctionExpressionContext, &parentExpr );
780+
QgsExpression parentExpr = QgsExpression( QString() );
781+
QVariant ret = foo->func( variants, &qgisFunctionExpressionContext, &parentExpr, nullptr );
782782
if ( parentExpr.hasEvalError() )
783783
{
784784
QByteArray ba = parentExpr.evalErrorString().toUtf8();

‎tests/src/core/testqgsexpression.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ class TestQgsExpression: public QObject
386386

387387
// Usage on a value map
388388
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mPointsLayer ) );
389-
QgsExpression expression( "represent_value('Pilots', \"Pilots\")" );
389+
QgsExpression expression( "represent_value(\"Pilots\", 'Pilots')" );
390390
if ( expression.hasParserError() )
391391
qDebug() << expression.parserErrorString();
392392
Q_ASSERT( !expression.hasParserError() );
@@ -401,7 +401,7 @@ class TestQgsExpression: public QObject
401401
QCOMPARE( expression.evaluate( &context ).toString(), QStringLiteral( "one" ) );
402402

403403
// Usage on a simple string
404-
QgsExpression expression2( "represent_value('Class', \"Class\")" );
404+
QgsExpression expression2( "represent_value(\"Class\", 'Class')" );
405405
if ( expression2.hasParserError() )
406406
qDebug() << expression2.parserErrorString();
407407
Q_ASSERT( !expression2.hasParserError() );
@@ -412,6 +412,26 @@ class TestQgsExpression: public QObject
412412
mPointsLayer->getFeatures( QgsFeatureRequest().setFilterExpression( "Class = 'Jet'" ) ).nextFeature( feature );
413413
context.setFeature( feature );
414414
QCOMPARE( expression2.evaluate( &context ).toString(), QStringLiteral( "Jet" ) );
415+
416+
// Test with implicit field name discovery
417+
QgsExpression expression3( "represent_value(\"Pilots\")" );
418+
if ( expression3.hasParserError() )
419+
qDebug() << expression.parserErrorString();
420+
Q_ASSERT( !expression3.hasParserError() );
421+
if ( expression3.hasEvalError() )
422+
qDebug() << expression3.evalErrorString();
423+
Q_ASSERT( !expression3.hasEvalError() );
424+
expression3.prepare( &context );
425+
mPointsLayer->getFeatures( QgsFeatureRequest().setFilterExpression( "Pilots = 1" ) ).nextFeature( feature );
426+
context.setFeature( feature );
427+
QCOMPARE( expression.evaluate( &context ).toString(), QStringLiteral( "one" ) );
428+
429+
430+
QgsExpression expression4( "represent_value('Class')" );
431+
if ( expression4.hasParserError() )
432+
qDebug() << expression4.parserErrorString();
433+
Q_ASSERT( !expression4.hasParserError() );
434+
Q_ASSERT( expression4.hasEvalError() );
415435
}
416436

417437
void evaluation_data()

‎tests/src/core/testqgsexpressioncontext.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class TestQgsExpressionContext : public QObject
6262
GetTestValueFunction()
6363
: QgsScopedExpressionFunction( QStringLiteral( "get_test_value" ), 1, QStringLiteral( "test" ) ) {}
6464

65-
virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) override
65+
virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
6666
{
6767
return 42;
6868
}
@@ -80,7 +80,7 @@ class TestQgsExpressionContext : public QObject
8080
GetTestValueFunction2()
8181
: QgsScopedExpressionFunction( QStringLiteral( "get_test_value" ), 1, QStringLiteral( "test" ) ) {}
8282

83-
virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) override
83+
virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
8484
{
8585
return 43;
8686
}
@@ -99,7 +99,7 @@ class TestQgsExpressionContext : public QObject
9999
, mVal( v )
100100
{}
101101

102-
virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression * ) override
102+
virtual QVariant func( const QVariantList &, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override
103103
{
104104
if ( !mVal )
105105
return QVariant();
@@ -228,7 +228,7 @@ void TestQgsExpressionContext::contextScopeFunctions()
228228
QVERIFY( scope.hasFunction( "get_test_value" ) );
229229
QVERIFY( scope.function( "get_test_value" ) );
230230
QgsExpressionContext temp;
231-
QCOMPARE( scope.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
231+
QCOMPARE( scope.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 );
232232

233233
//test functionNames
234234
scope.addFunction( QStringLiteral( "get_test_value2" ), new GetTestValueFunction() );
@@ -371,27 +371,27 @@ void TestQgsExpressionContext::contextStackFunctions()
371371
QVERIFY( context.hasFunction( "get_test_value" ) );
372372
QVERIFY( context.function( "get_test_value" ) );
373373
QgsExpressionContext temp;
374-
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
374+
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 );
375375

376376
//add a second scope, should override the first
377377
context << new QgsExpressionContextScope();
378378
//test without setting function first...
379379
QVERIFY( context.hasFunction( "get_test_value" ) );
380380
QVERIFY( context.function( "get_test_value" ) );
381-
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
381+
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 );
382382

383383
//then set the variable so it overrides
384384
QgsExpressionContextScope *scope2 = context.scope( 1 );
385385
scope2->addFunction( QStringLiteral( "get_test_value" ), new GetTestValueFunction2() );
386386
QVERIFY( context.hasFunction( "get_test_value" ) );
387387
QVERIFY( context.function( "get_test_value" ) );
388-
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0 ).toInt(), 43 );
388+
QCOMPARE( context.function( "get_test_value" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 43 );
389389

390390
//make sure stack falls back to earlier contexts
391391
scope2->addFunction( QStringLiteral( "get_test_value2" ), new GetTestValueFunction() );
392392
QVERIFY( context.hasFunction( "get_test_value2" ) );
393393
QVERIFY( context.function( "get_test_value2" ) );
394-
QCOMPARE( context.function( "get_test_value2" )->func( QVariantList(), &temp, 0 ).toInt(), 42 );
394+
QCOMPARE( context.function( "get_test_value2" )->func( QVariantList(), &temp, 0, nullptr ).toInt(), 42 );
395395

396396
//test functionNames
397397
QStringList names = context.functionNames();

‎tests/src/python/test_qgsexpression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def testAutoArgsAreExpanded(self):
9797
self.assertEqual(args, 3)
9898
values = [1, 2, 3]
9999
exp = QgsExpression("")
100-
result = function.func(values, None, exp)
100+
result = function.func(values, None, exp, None)
101101
# Make sure there is no eval error
102102
self.assertEqual(exp.evalErrorString(), "")
103103
self.assertEqual(result, (1, 2, 3))

0 commit comments

Comments
 (0)