Allow properties/collections to be prepared in advance

This commit is contained in:
Nyall Dawson 2017-01-10 13:33:17 +10:00
parent ef3f61fc17
commit 93db66ed1f
10 changed files with 135 additions and 7 deletions

View File

@ -69,6 +69,13 @@ class QgsAbstractProperty
*/
void setActive( bool active );
/**
* Prepares the property against a specified expression context. Calling prepare before evaluating the
* property multiple times allows precalculation of expensive setup tasks such as parsing expressions.
* Returns true if preparation was successful.
*/
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
/** Returns the set of any fields referenced by the property.
* @param context expression context the property will be evaluated against.
*/
@ -237,6 +244,7 @@ class QgsFieldBasedProperty : QgsAbstractProperty
*/
QString field() const;
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const;
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const;
@ -283,6 +291,7 @@ class QgsExpressionBasedProperty : QgsAbstractProperty
*/
QString expressionString() const;
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const;
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const;

View File

@ -126,6 +126,13 @@ class QgsAbstractPropertyCollection
*/
int valueAsInt( int key, const QgsExpressionContext& context, int defaultValue = 0 ) const;
/**
* Prepares the collection against a specified expression context. Calling prepare before evaluating the
* collection's properties multiple times allows precalculation of expensive setup tasks such as parsing expressions.
* Returns true if preparation was successful.
*/
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const = 0;
/**
* Returns the set of any fields referenced by the active properties from the collection.
* @param context expression context the properties will be evaluated against.
@ -215,6 +222,7 @@ class QgsPropertyCollection : QgsAbstractPropertyCollection
bool hasProperty( int key ) const;
QgsAbstractProperty* property( int key );
QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const;
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const;
bool isActive( int key ) const;
bool hasActiveProperties() const;
@ -336,6 +344,8 @@ class QgsPropertyCollectionStack : QgsAbstractPropertyCollection
*/
QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const;
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
/** Returns the set of any fields referenced by the active properties from the stack.
* @param context expression context the properties will be evaluated against.
*/

View File

@ -143,6 +143,11 @@ void QgsDiagramLayerSettings::writeXml( QDomElement& layerElem, QDomDocument& do
layerElem.appendChild( diagramLayerElem );
}
bool QgsDiagramLayerSettings::prepare( const QgsExpressionContext& context ) const
{
return mProperties.prepare( context );
}
void QgsDiagramLayerSettings::init()
{
if ( sPropertyNameMap.isEmpty() )

View File

@ -241,6 +241,14 @@ class CORE_EXPORT QgsDiagramLayerSettings
void readXml( const QDomElement& elem, const QgsVectorLayer* layer );
void writeXml( QDomElement& layerElem, QDomDocument& doc, const QgsVectorLayer* layer ) const;
/**
* Prepares the diagrams for a specified expression context. Calling prepare before rendering
* multiple diagrams allows precalculation of expensive setup tasks such as parsing expressions.
* Returns true if preparation was successful.
* @note added in QGIS 3.0
*/
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
/** Returns the set of any fields referenced by the layer's diagrams.
* @param context expression context the diagrams will be drawn using
* @note added in QGIS 2.16

View File

@ -233,8 +233,21 @@ bool QgsStaticProperty::readXml( const QDomElement &propertyElem, const QDomDocu
QgsFieldBasedProperty::QgsFieldBasedProperty( const QString& field, bool isActive )
: QgsAbstractProperty( isActive )
, mField( field )
{
{}
QgsFieldBasedProperty::QgsFieldBasedProperty( const QgsFieldBasedProperty& other )
: QgsAbstractProperty( other )
, mField( other.mField )
// don't copy cached field index!
{}
QgsFieldBasedProperty& QgsFieldBasedProperty::operator=( const QgsFieldBasedProperty & other )
{
QgsAbstractProperty::operator=( other );
mActive = other.mActive;
mField = other.mField;
mCachedFieldIdx = -1;
return *this;
}
QgsFieldBasedProperty* QgsFieldBasedProperty::clone()
@ -242,6 +255,17 @@ QgsFieldBasedProperty* QgsFieldBasedProperty::clone()
return new QgsFieldBasedProperty( *this );
}
bool QgsFieldBasedProperty::prepare( const QgsExpressionContext& context ) const
{
if ( !mActive )
return true;
// cache field index to avoid subsequent lookups
QgsFields f = context.fields();
mCachedFieldIdx = f.lookupField( mField );
return true;
}
QVariant QgsFieldBasedProperty::propertyValue( const QgsExpressionContext& context, const QVariant& defaultValue ) const
{
if ( !mActive )
@ -251,6 +275,10 @@ QVariant QgsFieldBasedProperty::propertyValue( const QgsExpressionContext& conte
if ( !f.isValid() )
return defaultValue;
//shortcut the field lookup
if ( mCachedFieldIdx >= 0 )
return f.attribute( mCachedFieldIdx );
int fieldIdx = f.fieldNameIndex( mField );
if ( fieldIdx < 0 )
return defaultValue;
@ -296,7 +324,6 @@ bool QgsFieldBasedProperty::readXml( const QDomElement& propertyElem, const QDom
QgsExpressionBasedProperty::QgsExpressionBasedProperty( const QString& expression, bool isActive )
: QgsAbstractProperty( isActive )
, mExpressionString( expression )
, mPrepared( false )
, mExpression( expression )
{

View File

@ -93,6 +93,13 @@ class CORE_EXPORT QgsAbstractProperty
*/
void setActive( bool active ) { mActive = active; }
/**
* Prepares the property against a specified expression context. Calling prepare before evaluating the
* property multiple times allows precalculation of expensive setup tasks such as parsing expressions.
* Returns true if preparation was successful.
*/
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const { Q_UNUSED( context ); return true; }
/**
* Returns the set of any fields referenced by the property.
* @param context expression context the property will be evaluated against.
@ -262,6 +269,13 @@ class CORE_EXPORT QgsFieldBasedProperty : public QgsAbstractProperty
*/
QgsFieldBasedProperty( const QString& field = QString(), bool isActive = false );
/**
* Copy constructor
*/
QgsFieldBasedProperty( const QgsFieldBasedProperty& other );
QgsFieldBasedProperty& operator=( const QgsFieldBasedProperty& other );
virtual Type propertyType() const override { return FieldBasedProperty; }
virtual QgsFieldBasedProperty* clone() override;
@ -279,6 +293,7 @@ class CORE_EXPORT QgsFieldBasedProperty : public QgsAbstractProperty
*/
QString field() const { return mField; }
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const override;
bool readXml( const QDomElement& propertyElem, const QDomDocument& doc ) override;
@ -290,6 +305,7 @@ class CORE_EXPORT QgsFieldBasedProperty : public QgsAbstractProperty
private:
QString mField;
mutable int mCachedFieldIdx = -1;
};
@ -328,6 +344,7 @@ class CORE_EXPORT QgsExpressionBasedProperty : public QgsAbstractProperty
*/
QString expressionString() const { return mExpressionString; }
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const override;
bool readXml( const QDomElement& propertyElem, const QDomDocument& doc ) override;
@ -339,13 +356,11 @@ class CORE_EXPORT QgsExpressionBasedProperty : public QgsAbstractProperty
private:
QString mExpressionString;
mutable bool mPrepared;
mutable bool mPrepared = false;
mutable QgsExpression mExpression;
//! Cached set of referenced columns
mutable QSet< QString > mReferencedCols;
bool prepare( const QgsExpressionContext& context ) const;
};
/**

View File

@ -171,6 +171,20 @@ QVariant QgsPropertyCollection::value( int key, const QgsExpressionContext& cont
return prop->value( context, defaultValue );
}
bool QgsPropertyCollection::prepare( const QgsExpressionContext& context ) const
{
bool result = true;
QHash<int, QgsAbstractProperty*>::const_iterator it = mProperties.constBegin();
for ( ; it != mProperties.constEnd(); ++it )
{
if ( !it.value()->isActive() )
continue;
result = result && it.value()->prepare( context );
}
return result;
}
QSet< QString > QgsPropertyCollection::referencedFields( const QgsExpressionContext &context ) const
{
QSet< QString > cols;
@ -439,6 +453,16 @@ QSet< QString > QgsPropertyCollectionStack::referencedFields( const QgsExpressio
return cols;
}
bool QgsPropertyCollectionStack::prepare( const QgsExpressionContext& context ) const
{
bool result = true;
Q_FOREACH ( QgsPropertyCollection* collection, mStack )
{
result = result && collection->prepare( context );
}
return result;
}
QSet<int> QgsPropertyCollectionStack::propertyKeys() const
{
QSet<int> keys;

View File

@ -38,6 +38,8 @@ class CORE_EXPORT QgsAbstractPropertyCollection
QgsAbstractPropertyCollection( const QString& name = QString() );
virtual ~QgsAbstractPropertyCollection() = default;
/**
* Returns the name of the property collection.
* @see setName()
@ -100,7 +102,6 @@ class CORE_EXPORT QgsAbstractPropertyCollection
*/
virtual QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const = 0;
/**
* Calculates the current value of the property with the specified key and interprets it as a color.
* @param key integer key for property to return. The intended use case is that a context specific enum is cast to
@ -140,6 +141,13 @@ class CORE_EXPORT QgsAbstractPropertyCollection
*/
int valueAsInt( int key, const QgsExpressionContext& context, int defaultValue = 0 ) const;
/**
* Prepares the collection against a specified expression context. Calling prepare before evaluating the
* collection's properties multiple times allows precalculation of expensive setup tasks such as parsing expressions.
* Returns true if preparation was successful.
*/
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const = 0;
/**
* Returns the set of any fields referenced by the active properties from the collection.
* @param context expression context the properties will be evaluated against.
@ -231,6 +239,7 @@ class CORE_EXPORT QgsPropertyCollection : public QgsAbstractPropertyCollection
QgsAbstractProperty* property( int key ) override;
const QgsAbstractProperty* property( int key ) const override;
QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const override;
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
bool isActive( int key ) const override;
bool hasActiveProperties() const override;
@ -386,6 +395,7 @@ class CORE_EXPORT QgsPropertyCollectionStack : public QgsAbstractPropertyCollect
* @param context expression context the properties will be evaluated against.
*/
QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
QSet<int> propertyKeys() const override;
bool hasProperty( int key ) const override;

View File

@ -178,10 +178,12 @@ bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QS
s2.setRenderer( mDiagRenderer );
bool result = s2.prepare( context.expressionContext() );
//add attributes needed by the diagram renderer
attributeNames.unite( s2.referencedFields( context.expressionContext() ) );
return true;
return result;
}

View File

@ -318,6 +318,11 @@ void TestQgsProperty::fieldBasedProperty()
QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
//test preparation
QgsFieldBasedProperty property3( QString( "field1" ), true );
QVERIFY( property3.prepare( context ) );
QCOMPARE( property3.value( context, -1 ).toInt(), 5 );
//saving and restoring
//create a test dom element
@ -411,6 +416,13 @@ void TestQgsProperty::expressionBasedProperty()
QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
//preparation
QgsExpressionBasedProperty property3( QString( "\"field1\" + \"field2\"" ), true );
QVERIFY( property3.prepare( context ) );
QCOMPARE( property3.value( context, -1 ).toInt(), 12 );
QgsExpressionBasedProperty property4( QString( "\"field1\" + " ), true );
QVERIFY( !property4.prepare( context ) );
//saving and restoring
//create a test dom element
@ -776,6 +788,9 @@ void TestQgsProperty::propertyCollection()
QVERIFY( collection.hasActiveProperties() );
QVERIFY( !collection.hasActiveDynamicProperties() );
//preparation
QVERIFY( collection.prepare( context ) );
//test bad property
QVERIFY( !collection.property( Property2 ) );
QVERIFY( !collection.value( Property2, context ).isValid() );
@ -995,6 +1010,9 @@ void TestQgsProperty::collectionStack()
QVERIFY( !stack.hasActiveDynamicProperties() );
QVERIFY( stack.hasActiveProperties() );
//preparation
QVERIFY( stack.prepare( context ) );
//test adding active property later in the stack
QgsStaticProperty* property3 = new QgsStaticProperty( "value3", true );
collection2->setProperty( Property1, property3 );