Skip to content

Commit 93db66e

Browse files
committed
Allow properties/collections to be prepared in advance
1 parent ef3f61f commit 93db66e

10 files changed

+135
-7
lines changed

python/core/qgsproperty.sip

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ class QgsAbstractProperty
6969
*/
7070
void setActive( bool active );
7171

72+
/**
73+
* Prepares the property against a specified expression context. Calling prepare before evaluating the
74+
* property multiple times allows precalculation of expensive setup tasks such as parsing expressions.
75+
* Returns true if preparation was successful.
76+
*/
77+
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
78+
7279
/** Returns the set of any fields referenced by the property.
7380
* @param context expression context the property will be evaluated against.
7481
*/
@@ -237,6 +244,7 @@ class QgsFieldBasedProperty : QgsAbstractProperty
237244
*/
238245
QString field() const;
239246

247+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
240248
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const;
241249

242250
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const;
@@ -283,6 +291,7 @@ class QgsExpressionBasedProperty : QgsAbstractProperty
283291
*/
284292
QString expressionString() const;
285293

294+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
286295
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const;
287296

288297
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const;

python/core/qgspropertycollection.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ class QgsAbstractPropertyCollection
126126
*/
127127
int valueAsInt( int key, const QgsExpressionContext& context, int defaultValue = 0 ) const;
128128

129+
/**
130+
* Prepares the collection against a specified expression context. Calling prepare before evaluating the
131+
* collection's properties multiple times allows precalculation of expensive setup tasks such as parsing expressions.
132+
* Returns true if preparation was successful.
133+
*/
134+
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const = 0;
135+
129136
/**
130137
* Returns the set of any fields referenced by the active properties from the collection.
131138
* @param context expression context the properties will be evaluated against.
@@ -215,6 +222,7 @@ class QgsPropertyCollection : QgsAbstractPropertyCollection
215222
bool hasProperty( int key ) const;
216223
QgsAbstractProperty* property( int key );
217224
QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const;
225+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
218226
QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const;
219227
bool isActive( int key ) const;
220228
bool hasActiveProperties() const;
@@ -336,6 +344,8 @@ class QgsPropertyCollectionStack : QgsAbstractPropertyCollection
336344
*/
337345
QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const;
338346

347+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
348+
339349
/** Returns the set of any fields referenced by the active properties from the stack.
340350
* @param context expression context the properties will be evaluated against.
341351
*/

src/core/qgsdiagramrenderer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ void QgsDiagramLayerSettings::writeXml( QDomElement& layerElem, QDomDocument& do
143143
layerElem.appendChild( diagramLayerElem );
144144
}
145145

146+
bool QgsDiagramLayerSettings::prepare( const QgsExpressionContext& context ) const
147+
{
148+
return mProperties.prepare( context );
149+
}
150+
146151
void QgsDiagramLayerSettings::init()
147152
{
148153
if ( sPropertyNameMap.isEmpty() )

src/core/qgsdiagramrenderer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ class CORE_EXPORT QgsDiagramLayerSettings
241241
void readXml( const QDomElement& elem, const QgsVectorLayer* layer );
242242
void writeXml( QDomElement& layerElem, QDomDocument& doc, const QgsVectorLayer* layer ) const;
243243

244+
/**
245+
* Prepares the diagrams for a specified expression context. Calling prepare before rendering
246+
* multiple diagrams allows precalculation of expensive setup tasks such as parsing expressions.
247+
* Returns true if preparation was successful.
248+
* @note added in QGIS 3.0
249+
*/
250+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const;
251+
244252
/** Returns the set of any fields referenced by the layer's diagrams.
245253
* @param context expression context the diagrams will be drawn using
246254
* @note added in QGIS 2.16

src/core/qgsproperty.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,39 @@ bool QgsStaticProperty::readXml( const QDomElement &propertyElem, const QDomDocu
233233
QgsFieldBasedProperty::QgsFieldBasedProperty( const QString& field, bool isActive )
234234
: QgsAbstractProperty( isActive )
235235
, mField( field )
236-
{
236+
{}
237+
238+
QgsFieldBasedProperty::QgsFieldBasedProperty( const QgsFieldBasedProperty& other )
239+
: QgsAbstractProperty( other )
240+
, mField( other.mField )
241+
// don't copy cached field index!
242+
{}
237243

244+
QgsFieldBasedProperty& QgsFieldBasedProperty::operator=( const QgsFieldBasedProperty & other )
245+
{
246+
QgsAbstractProperty::operator=( other );
247+
mActive = other.mActive;
248+
mField = other.mField;
249+
mCachedFieldIdx = -1;
250+
return *this;
238251
}
239252

240253
QgsFieldBasedProperty* QgsFieldBasedProperty::clone()
241254
{
242255
return new QgsFieldBasedProperty( *this );
243256
}
244257

258+
bool QgsFieldBasedProperty::prepare( const QgsExpressionContext& context ) const
259+
{
260+
if ( !mActive )
261+
return true;
262+
263+
// cache field index to avoid subsequent lookups
264+
QgsFields f = context.fields();
265+
mCachedFieldIdx = f.lookupField( mField );
266+
return true;
267+
}
268+
245269
QVariant QgsFieldBasedProperty::propertyValue( const QgsExpressionContext& context, const QVariant& defaultValue ) const
246270
{
247271
if ( !mActive )
@@ -251,6 +275,10 @@ QVariant QgsFieldBasedProperty::propertyValue( const QgsExpressionContext& conte
251275
if ( !f.isValid() )
252276
return defaultValue;
253277

278+
//shortcut the field lookup
279+
if ( mCachedFieldIdx >= 0 )
280+
return f.attribute( mCachedFieldIdx );
281+
254282
int fieldIdx = f.fieldNameIndex( mField );
255283
if ( fieldIdx < 0 )
256284
return defaultValue;
@@ -296,7 +324,6 @@ bool QgsFieldBasedProperty::readXml( const QDomElement& propertyElem, const QDom
296324
QgsExpressionBasedProperty::QgsExpressionBasedProperty( const QString& expression, bool isActive )
297325
: QgsAbstractProperty( isActive )
298326
, mExpressionString( expression )
299-
, mPrepared( false )
300327
, mExpression( expression )
301328
{
302329

src/core/qgsproperty.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ class CORE_EXPORT QgsAbstractProperty
9393
*/
9494
void setActive( bool active ) { mActive = active; }
9595

96+
/**
97+
* Prepares the property against a specified expression context. Calling prepare before evaluating the
98+
* property multiple times allows precalculation of expensive setup tasks such as parsing expressions.
99+
* Returns true if preparation was successful.
100+
*/
101+
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const { Q_UNUSED( context ); return true; }
102+
96103
/**
97104
* Returns the set of any fields referenced by the property.
98105
* @param context expression context the property will be evaluated against.
@@ -262,6 +269,13 @@ class CORE_EXPORT QgsFieldBasedProperty : public QgsAbstractProperty
262269
*/
263270
QgsFieldBasedProperty( const QString& field = QString(), bool isActive = false );
264271

272+
/**
273+
* Copy constructor
274+
*/
275+
QgsFieldBasedProperty( const QgsFieldBasedProperty& other );
276+
277+
QgsFieldBasedProperty& operator=( const QgsFieldBasedProperty& other );
278+
265279
virtual Type propertyType() const override { return FieldBasedProperty; }
266280

267281
virtual QgsFieldBasedProperty* clone() override;
@@ -279,6 +293,7 @@ class CORE_EXPORT QgsFieldBasedProperty : public QgsAbstractProperty
279293
*/
280294
QString field() const { return mField; }
281295

296+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
282297
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
283298
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const override;
284299
bool readXml( const QDomElement& propertyElem, const QDomDocument& doc ) override;
@@ -290,6 +305,7 @@ class CORE_EXPORT QgsFieldBasedProperty : public QgsAbstractProperty
290305
private:
291306

292307
QString mField;
308+
mutable int mCachedFieldIdx = -1;
293309

294310
};
295311

@@ -328,6 +344,7 @@ class CORE_EXPORT QgsExpressionBasedProperty : public QgsAbstractProperty
328344
*/
329345
QString expressionString() const { return mExpressionString; }
330346

347+
bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
331348
virtual QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
332349
bool writeXml( QDomElement& propertyElem, QDomDocument& doc ) const override;
333350
bool readXml( const QDomElement& propertyElem, const QDomDocument& doc ) override;
@@ -339,13 +356,11 @@ class CORE_EXPORT QgsExpressionBasedProperty : public QgsAbstractProperty
339356
private:
340357

341358
QString mExpressionString;
342-
mutable bool mPrepared;
359+
mutable bool mPrepared = false;
343360
mutable QgsExpression mExpression;
344361
//! Cached set of referenced columns
345362
mutable QSet< QString > mReferencedCols;
346363

347-
bool prepare( const QgsExpressionContext& context ) const;
348-
349364
};
350365

351366
/**

src/core/qgspropertycollection.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ QVariant QgsPropertyCollection::value( int key, const QgsExpressionContext& cont
171171
return prop->value( context, defaultValue );
172172
}
173173

174+
bool QgsPropertyCollection::prepare( const QgsExpressionContext& context ) const
175+
{
176+
bool result = true;
177+
QHash<int, QgsAbstractProperty*>::const_iterator it = mProperties.constBegin();
178+
for ( ; it != mProperties.constEnd(); ++it )
179+
{
180+
if ( !it.value()->isActive() )
181+
continue;
182+
183+
result = result && it.value()->prepare( context );
184+
}
185+
return result;
186+
}
187+
174188
QSet< QString > QgsPropertyCollection::referencedFields( const QgsExpressionContext &context ) const
175189
{
176190
QSet< QString > cols;
@@ -439,6 +453,16 @@ QSet< QString > QgsPropertyCollectionStack::referencedFields( const QgsExpressio
439453
return cols;
440454
}
441455

456+
bool QgsPropertyCollectionStack::prepare( const QgsExpressionContext& context ) const
457+
{
458+
bool result = true;
459+
Q_FOREACH ( QgsPropertyCollection* collection, mStack )
460+
{
461+
result = result && collection->prepare( context );
462+
}
463+
return result;
464+
}
465+
442466
QSet<int> QgsPropertyCollectionStack::propertyKeys() const
443467
{
444468
QSet<int> keys;

src/core/qgspropertycollection.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class CORE_EXPORT QgsAbstractPropertyCollection
3838

3939
QgsAbstractPropertyCollection( const QString& name = QString() );
4040

41+
virtual ~QgsAbstractPropertyCollection() = default;
42+
4143
/**
4244
* Returns the name of the property collection.
4345
* @see setName()
@@ -100,7 +102,6 @@ class CORE_EXPORT QgsAbstractPropertyCollection
100102
*/
101103
virtual QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const = 0;
102104

103-
104105
/**
105106
* Calculates the current value of the property with the specified key and interprets it as a color.
106107
* @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
140141
*/
141142
int valueAsInt( int key, const QgsExpressionContext& context, int defaultValue = 0 ) const;
142143

144+
/**
145+
* Prepares the collection against a specified expression context. Calling prepare before evaluating the
146+
* collection's properties multiple times allows precalculation of expensive setup tasks such as parsing expressions.
147+
* Returns true if preparation was successful.
148+
*/
149+
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const = 0;
150+
143151
/**
144152
* Returns the set of any fields referenced by the active properties from the collection.
145153
* @param context expression context the properties will be evaluated against.
@@ -231,6 +239,7 @@ class CORE_EXPORT QgsPropertyCollection : public QgsAbstractPropertyCollection
231239
QgsAbstractProperty* property( int key ) override;
232240
const QgsAbstractProperty* property( int key ) const override;
233241
QVariant value( int key, const QgsExpressionContext& context, const QVariant& defaultValue = QVariant() ) const override;
242+
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
234243
QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
235244
bool isActive( int key ) const override;
236245
bool hasActiveProperties() const override;
@@ -386,6 +395,7 @@ class CORE_EXPORT QgsPropertyCollectionStack : public QgsAbstractPropertyCollect
386395
* @param context expression context the properties will be evaluated against.
387396
*/
388397
QSet< QString > referencedFields( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
398+
virtual bool prepare( const QgsExpressionContext& context = QgsExpressionContext() ) const override;
389399

390400
QSet<int> propertyKeys() const override;
391401
bool hasProperty( int key ) const override;

src/core/qgsvectorlayerdiagramprovider.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,12 @@ bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QS
178178

179179
s2.setRenderer( mDiagRenderer );
180180

181+
bool result = s2.prepare( context.expressionContext() );
182+
181183
//add attributes needed by the diagram renderer
182184
attributeNames.unite( s2.referencedFields( context.expressionContext() ) );
183185

184-
return true;
186+
return result;
185187
}
186188

187189

tests/src/core/testqgsproperty.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ void TestQgsProperty::fieldBasedProperty()
318318
QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
319319
QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
320320

321+
//test preparation
322+
QgsFieldBasedProperty property3( QString( "field1" ), true );
323+
QVERIFY( property3.prepare( context ) );
324+
QCOMPARE( property3.value( context, -1 ).toInt(), 5 );
325+
321326
//saving and restoring
322327

323328
//create a test dom element
@@ -411,6 +416,13 @@ void TestQgsProperty::expressionBasedProperty()
411416
QCOMPARE( defaultProperty.value( context, -1 ).toInt(), -1 );
412417
QVERIFY( defaultProperty.referencedFields( context ).isEmpty() );
413418

419+
//preparation
420+
QgsExpressionBasedProperty property3( QString( "\"field1\" + \"field2\"" ), true );
421+
QVERIFY( property3.prepare( context ) );
422+
QCOMPARE( property3.value( context, -1 ).toInt(), 12 );
423+
QgsExpressionBasedProperty property4( QString( "\"field1\" + " ), true );
424+
QVERIFY( !property4.prepare( context ) );
425+
414426
//saving and restoring
415427

416428
//create a test dom element
@@ -776,6 +788,9 @@ void TestQgsProperty::propertyCollection()
776788
QVERIFY( collection.hasActiveProperties() );
777789
QVERIFY( !collection.hasActiveDynamicProperties() );
778790

791+
//preparation
792+
QVERIFY( collection.prepare( context ) );
793+
779794
//test bad property
780795
QVERIFY( !collection.property( Property2 ) );
781796
QVERIFY( !collection.value( Property2, context ).isValid() );
@@ -995,6 +1010,9 @@ void TestQgsProperty::collectionStack()
9951010
QVERIFY( !stack.hasActiveDynamicProperties() );
9961011
QVERIFY( stack.hasActiveProperties() );
9971012

1013+
//preparation
1014+
QVERIFY( stack.prepare( context ) );
1015+
9981016
//test adding active property later in the stack
9991017
QgsStaticProperty* property3 = new QgsStaticProperty( "value3", true );
10001018
collection2->setProperty( Property1, property3 );

0 commit comments

Comments
 (0)