Skip to content

Commit

Permalink
Reduce number of expression evaluations in legend rendering
Browse files Browse the repository at this point in the history
...by caching previous results and using them, until the layout is refreshed
  • Loading branch information
nyalldawson committed Jun 2, 2020
1 parent 08542ef commit 4a125f6
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 4 deletions.
7 changes: 7 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitemlegend.sip.in
Expand Up @@ -41,6 +41,13 @@ Alternative constructor.



void clearCachedData( QgsLayerTreeNode *node ) const;
%Docstring
Clears any previously cached data for the specified ``node``.

.. versionadded:: 3.14
%End

signals:

void refreshLegend();
Expand Down
38 changes: 34 additions & 4 deletions src/core/layout/qgslayoutitemlegend.cpp
Expand Up @@ -168,6 +168,7 @@ void QgsLayoutItemLegend::finalizeRestoreFromXml()
void QgsLayoutItemLegend::refresh()
{
QgsLayoutItem::refresh();
clearLegendCachedData();
onAtlasFeature();
}

Expand Down Expand Up @@ -780,6 +781,26 @@ void QgsLayoutItemLegend::setModelStyleOverrides( const QMap<QString, QString> &

}

void QgsLayoutItemLegend::clearLegendCachedData()
{
std::function< void( QgsLayerTreeNode * ) > clearNodeCache;
clearNodeCache = [&]( QgsLayerTreeNode * node )
{
mLegendModel->clearCachedData( node );
if ( QgsLayerTree::isGroup( node ) )
{
QgsLayerTreeGroup *group = QgsLayerTree::toGroup( node );
const QList< QgsLayerTreeNode * > children = group->children();
for ( QgsLayerTreeNode *child : children )
{
clearNodeCache( child );
}
}
};

clearNodeCache( mLegendModel->rootGroup() );
}

void QgsLayoutItemLegend::mapLayerStyleOverridesChanged()
{
if ( !mMap )
Expand Down Expand Up @@ -1022,7 +1043,10 @@ QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
QgsLayerTreeLayer *nodeLayer = QgsLayerTree::isLayer( node ) ? QgsLayerTree::toLayer( node ) : nullptr;
if ( nodeLayer && ( role == Qt::DisplayRole || role == Qt::EditRole ) )
{
QString name;
QString name = node->customProperty( QStringLiteral( "cached_name" ) ).toString();
if ( !name.isEmpty() )
return name;

QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );

//finding the first label that is stored
Expand All @@ -1038,13 +1062,13 @@ QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
if ( vlayer && vlayer->featureCount() >= 0 )
{
name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
node->setCustomProperty( QStringLiteral( "cached_name" ), name );
return name;
}
}

bool evaluate = vlayer ? !nodeLayer->labelExpression().isEmpty() : false;

if ( evaluate || name.contains( "[%" ) )
const bool evaluate = ( vlayer && !nodeLayer->labelExpression().isEmpty() ) || name.contains( "[%" );
if ( evaluate )
{
QgsExpressionContext expressionContext;
if ( vlayer )
Expand All @@ -1069,6 +1093,7 @@ QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
else if ( QgsSymbolLegendNode *symnode = qobject_cast<QgsSymbolLegendNode *>( legendnodes.first() ) )
name = symnode->evaluateLabel( expressionContext );
}
node->setCustomProperty( QStringLiteral( "cached_name" ), name );
return name;
}
return QgsLayerTreeModel::data( index, role );
Expand All @@ -1095,6 +1120,11 @@ QList<QgsLayerTreeModelLegendNode *> QgsLegendModel::layerLegendNodes( QgsLayerT
return lst;
}

void QgsLegendModel::clearCachedData( QgsLayerTreeNode *node ) const
{
node->removeCustomProperty( QStringLiteral( "cached_name" ) );
}

void QgsLegendModel::forceRefresh()
{
emit refreshLegend();
Expand Down
15 changes: 15 additions & 0 deletions src/core/layout/qgslayoutitemlegend.h
Expand Up @@ -65,6 +65,12 @@ class CORE_EXPORT QgsLegendModel : public QgsLayerTreeModel
*/
QList<QgsLayerTreeModelLegendNode *> layerLegendNodes( QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent = false ) const SIP_SKIP;

/**
* Clears any previously cached data for the specified \a node.
* \since QGIS 3.14
*/
void clearCachedData( QgsLayerTreeNode *node ) const;

signals:

/**
Expand All @@ -89,6 +95,12 @@ class CORE_EXPORT QgsLegendModel : public QgsLayerTreeModel
*/
QgsLayoutItemLegend *mLayoutLegend = nullptr;

/**
* Evaluate the expression or symbol expressions of a given layer node.
* \since QGIS 3.14
*/
QString evaluateLayerExpressions( QgsLayerTreeLayer *nodeLayer ) const;

};


Expand Down Expand Up @@ -549,6 +561,9 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem

void setModelStyleOverrides( const QMap<QString, QString> &overrides );

//! Clears any data cached for the legend model
void clearLegendCachedData();

std::unique_ptr< QgsLegendModel > mLegendModel;
std::unique_ptr< QgsLayerTreeGroup > mCustomLayerTree;

Expand Down

0 comments on commit 4a125f6

Please sign in to comment.