Skip to content

Commit

Permalink
Add method to QgsProperty to determine if a property is effectively
Browse files Browse the repository at this point in the history
a static value in a specified QgsExpressionContext

Allows us to short-cut some calculations, e.g. by pre-evaluating
the property once during a QgsSymbol startRender instead of once
for every feature rendered.
  • Loading branch information
nyalldawson committed Nov 15, 2021
1 parent fb9dff2 commit e0447a1
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 0 deletions.
17 changes: 17 additions & 0 deletions python/core/auto_generated/qgsproperty.sip.in
Expand Up @@ -255,6 +255,23 @@ Returns the property type.
Returns whether the property is currently active.

.. seealso:: :py:func:`setActive`
%End

bool isStaticValueInContext( const QgsExpressionContext &context, QVariant &staticValue /Out/ ) const;
%Docstring
Returns ``True`` if the property is effectively a static value
in the specified ``context``.

I.e. if the property type is QgsProperty.ExpressionBasedProperty with
a fixed value expression ('some static value'), this method will return
``True``.

:param context: expression context

:return: - ``True`` if property is a static value
- staticValue: will be set to evaluated static value if property is effectively a static value

.. versionadded:: 3.24
%End

void setActive( bool active );
Expand Down
33 changes: 33 additions & 0 deletions src/core/qgsproperty.cpp
Expand Up @@ -21,6 +21,7 @@
#include "qgsfeature.h"
#include "qgssymbollayerutils.h"
#include "qgscolorramp.h"
#include "qgsexpressionnodeimpl.h"

#include <QRegularExpression>

Expand Down Expand Up @@ -292,6 +293,38 @@ bool QgsProperty::isActive() const
return d->type != InvalidProperty && d->active;
}

bool QgsProperty::isStaticValueInContext( const QgsExpressionContext &context, QVariant &staticValue ) const
{
staticValue = QVariant();
switch ( d->type )
{
case InvalidProperty:
return true;

case StaticProperty:
staticValue = d->staticValue;
return true;

case FieldBasedProperty:
return false;

case ExpressionBasedProperty:
{
QgsExpression exp = d->expression;
if ( exp.prepare( &context ) && exp.rootNode() )
{
if ( exp.rootNode()->hasCachedStaticValue() )
{
staticValue = exp.rootNode()->cachedStaticValue();
return true;
}
}
return false;
}
}
return false;
}

void QgsProperty::setActive( bool active )
{
d.detach();
Expand Down
16 changes: 16 additions & 0 deletions src/core/qgsproperty.h
Expand Up @@ -301,6 +301,22 @@ class CORE_EXPORT QgsProperty
*/
bool isActive() const;

/**
* Returns TRUE if the property is effectively a static value
* in the specified \a context.
*
* I.e. if the property type is QgsProperty::ExpressionBasedProperty with
* a fixed value expression ('some static value'), this method will return
* TRUE.
*
* \param context expression context
* \param staticValue will be set to evaluated static value if property is effectively a static value
* \returns TRUE if property is a static value
*
* \since QGIS 3.24
*/
bool isStaticValueInContext( const QgsExpressionContext &context, QVariant &staticValue SIP_OUT ) const;

/**
* Sets whether the property is currently active.
* \see isActive()
Expand Down
47 changes: 47 additions & 0 deletions tests/src/core/testqgsproperty.cpp
Expand Up @@ -80,6 +80,7 @@ class TestQgsProperty : public QObject
void fieldBasedProperty(); //test for QgsFieldBasedProperty
void expressionBasedProperty(); //test for QgsExpressionBasedProperty
void equality();
void isStaticValueInContext();
void propertyTransformer(); //test for QgsPropertyTransformer
void propertyTransformerFromExpression(); // text converting expression into QgsPropertyTransformer
void genericNumericTransformer();
Expand Down Expand Up @@ -686,6 +687,52 @@ void TestQgsProperty::equality()
QVERIFY( !( dd1 != dd2 ) );
}

void TestQgsProperty::isStaticValueInContext()
{
// test the QgsProperty::isStaticValueInContext logic
QgsExpressionContext context;
QgsProperty p;
QVariant v;
v = 5; // set an initial value so we can be sure it's cleared
// an invalid property is static -- its value won't change
QVERIFY( p.isStaticValueInContext( context, v ) );
QVERIFY( !v.isValid() );

// a static value IS static (duh)
p = QgsProperty::fromValue( 55 );
QVERIFY( p.isStaticValueInContext( context, v ) );
QCOMPARE( v.toInt(), 55 );

// a field based property is NOT static
p = QgsProperty::fromField( QStringLiteral( "xxx" ) );
QVERIFY( !p.isStaticValueInContext( context, v ) );
QVERIFY( !v.isValid() );

// an expression based property may or may not be static
// start with a non-static expression
p = QgsProperty::fromExpression( QStringLiteral( "\"xxx\"" ) );
v = 5;
QVERIFY( !p.isStaticValueInContext( context, v ) );
QVERIFY( !v.isValid() );

// should still be non-static, even with valid fields
QgsFields fields;
fields.append( QgsField( QStringLiteral( "xxx" ), QVariant::Int ) );
context.setFields( fields );
v = 5;
QVERIFY( !p.isStaticValueInContext( context, v ) );
QVERIFY( !v.isValid() );

// an expression which IS static
QgsExpressionContextScope *scope = new QgsExpressionContextScope();
scope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "my_var" ), 123, true, true ) );
context.appendScope( scope );
p = QgsProperty::fromExpression( QStringLiteral( "@my_var * 2" ) );
v = 5;
QVERIFY( p.isStaticValueInContext( context, v ) );
QCOMPARE( v.toInt(), 246 );
}

void TestQgsProperty::propertyTransformer()
{
const QgsExpressionContext context;
Expand Down

0 comments on commit e0447a1

Please sign in to comment.