Improve legend performance

Only recalculate the position of the icons for affected legend nodes.
Avoids brute forcing over complex legends as soon as something is changed.

Fixes #38890
This commit is contained in:
Matthias Kuhn 2020-09-20 13:18:16 +02:00 committed by Nyall Dawson
parent 61a1801e3d
commit 673d6727ee
3 changed files with 19 additions and 7 deletions

View File

@ -415,6 +415,7 @@ Filter nodes from QgsMapLayerLegend according to the current filtering rules
};
QFlags<QgsLayerTreeModel::Flag> operator|(QgsLayerTreeModel::Flag f1, QFlags<QgsLayerTreeModel::Flag> f2);

View File

@ -1318,7 +1318,8 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL )
int count = legendTree ? legendTree->children[nullptr].count() : filteredLstNew.count();
if ( !filteredLstNew.isEmpty() ) beginInsertRows( node2index( nodeL ), 0, count - 1 );
if ( !filteredLstNew.isEmpty() )
beginInsertRows( node2index( nodeL ), 0, count - 1 );
LayerLegendData data;
data.originalNodes = lstNew;
@ -1328,10 +1329,12 @@ void QgsLayerTreeModel::addLegendToLayer( QgsLayerTreeLayer *nodeL )
mLegend[nodeL] = data;
if ( !filteredLstNew.isEmpty() ) endInsertRows();
if ( !filteredLstNew.isEmpty() )
endInsertRows();
// invalidate map based data even if the data is not map-based to make sure
// the symbol sizes are computed at least once
mInvalidatedNodes.insert( nodeL );
legendInvalidateMapBasedData();
}
@ -1575,7 +1578,7 @@ void QgsLayerTreeModel::legendInvalidateMapBasedData()
if ( !testFlag( DeferredLegendInvalidation ) )
invalidateLegendMapBasedData();
else
mDeferLegendInvalidationTimer.start( 1000 );
mDeferLegendInvalidationTimer.start( 10 );
}
void QgsLayerTreeModel::invalidateLegendMapBasedData()
@ -1583,15 +1586,17 @@ void QgsLayerTreeModel::invalidateLegendMapBasedData()
// we have varying icon sizes, and we want icon to be centered and
// text to be left aligned, so we have to compute the max width of icons
//
// we do that for nodes who share a common parent
// we do that for nodes which share a common parent
//
// we do that here because for symbols with size defined in map units
// the symbol sizes changes depends on the zoom level
std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
for ( const LayerLegendData &data : qgis::as_const( mLegend ) )
for ( QgsLayerTreeLayer *layerNode : qgis::as_const( mInvalidatedNodes ) )
{
const LayerLegendData &data = mLegend.value( layerNode );
QList<QgsSymbolLegendNode *> symbolNodes;
QMap<QString, int> widthMax;
for ( QgsLayerTreeModelLegendNode *legendNode : qgis::as_const( data.originalNodes ) )
@ -1606,8 +1611,7 @@ void QgsLayerTreeModel::invalidateLegendMapBasedData()
symbolNodes.append( n );
}
}
const auto constSymbolNodes = symbolNodes;
for ( QgsSymbolLegendNode *n : constSymbolNodes )
for ( QgsSymbolLegendNode *n : qgis::as_const( symbolNodes ) )
{
const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
Q_ASSERT( widthMax[parentKey] > 0 );
@ -1618,6 +1622,7 @@ void QgsLayerTreeModel::invalidateLegendMapBasedData()
legendNode->invalidateMapBasedData();
}
mInvalidatedNodes.clear();
}
// Legend nodes routines - end

View File

@ -435,6 +435,12 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
//! Per layer data about layer's legend nodes
QHash<QgsLayerTreeLayer *, LayerLegendData> mLegend;
/**
* Keep track of layer nodes for which the legend
* size needs to be recalculated
*/
QSet<QgsLayerTreeLayer *> mInvalidatedNodes;
QFont mFontLayer;
QFont mFontGroup;