Skip to content

Commit

Permalink
Avoid creating multiple render contexts when calculating legend
Browse files Browse the repository at this point in the history
node size

Partial fix for qgis slowdown when using layer with large
number of legend nodes
  • Loading branch information
nyalldawson committed Oct 19, 2016
1 parent cf60049 commit c9251c5
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 12 deletions.
2 changes: 1 addition & 1 deletion python/core/layertree/qgslayertreemodel.sip
Expand Up @@ -166,7 +166,7 @@ class QgsLayerTreeModel : QAbstractItemModel
//! Get hints about map view - to be used in legend nodes. Arguments that are not null will receive values.
//! If there are no valid map view data (from previous call to setLegendMapViewData()), returned values are zeros.
//! @note added in 2.6
void legendMapViewData( double *mapUnitsPerPixel /Out/, int *dpi /Out/, double *scale /Out/ );
void legendMapViewData( double *mapUnitsPerPixel /Out/, int *dpi /Out/, double *scale /Out/ ) const;

//! Get map of map layer style overrides (key: layer ID, value: style name) where a different style should be used instead of the current one
//! @note added in 2.10
Expand Down
17 changes: 15 additions & 2 deletions python/core/layertree/qgslayertreemodellegendnode.sip
Expand Up @@ -141,10 +141,23 @@ class QgsSymbolLegendNode : QgsLayerTreeModelLegendNode
//! @note added in 2.10
QSize iconSize() const;

//! Get the minimum icon size to prevent cropping
//! @note added in 2.10
/**
* Calculates the minimum icon size to prevent cropping. When evaluating
* the size for multiple icons it is more efficient to create a single
* render context in advance and use the variant which accepts a QgsRenderContext
* argument.
* @note added in 2.10
*/
QSize minimumIconSize() const;

/**
* Calculates the minimum icon size to prevent cropping. When evaluating
* the size for multiple icons it is more efficient to create a single
* render context in advance and call this method instead of minimumIconSize().
* @note added in QGIS 2.18
*/
QSize minimumIconSize( QgsRenderContext &context ) const;

/** Returns the symbol used by the legend node.
* @see setSymbol()
* @note added in QGIS 2.14
Expand Down
22 changes: 20 additions & 2 deletions src/core/layertree/qgslayertreemodel.cpp
Expand Up @@ -663,7 +663,7 @@ void QgsLayerTreeModel::setLegendMapViewData( double mapUnitsPerPixel, int dpi,
refreshScaleBasedLayers();
}

void QgsLayerTreeModel::legendMapViewData( double* mapUnitsPerPixel, int* dpi, double* scale )
void QgsLayerTreeModel::legendMapViewData( double* mapUnitsPerPixel, int* dpi, double* scale ) const
{
if ( mapUnitsPerPixel ) *mapUnitsPerPixel = mLegendMapViewMupp;
if ( dpi ) *dpi = mLegendMapViewDpi;
Expand Down Expand Up @@ -1269,6 +1269,22 @@ void QgsLayerTreeModel::tryBuildLegendTree( LayerLegendData& data )
}
}

QgsRenderContext* QgsLayerTreeModel::createTemporaryRenderContext() const
{
double scale = 0.0;
double mupp = 0.0;
int dpi = 0;
legendMapViewData( &mupp, &dpi, &scale );
bool validData = !qgsDoubleNear( mupp, 0.0 ) && dpi != 0 && !qgsDoubleNear( scale, 0.0 );

// setup temporary render context
QScopedPointer<QgsRenderContext> context( new QgsRenderContext );
context->setScaleFactor( dpi / 25.4 );
context->setRendererScale( scale );
context->setMapToPixel( QgsMapToPixel( mupp ) );
return validData ? context.take() : nullptr;
}


QgsLayerTreeModelLegendNode* QgsLayerTreeModel::index2legendNode( const QModelIndex& index )
{
Expand Down Expand Up @@ -1470,6 +1486,8 @@ void QgsLayerTreeModel::invalidateLegendMapBasedData()
// we do that here because for symbols with size defined in map units
// the symbol sizes changes depends on the zoom level

QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );

Q_FOREACH ( const LayerLegendData& data, mLegend )
{
QList<QgsSymbolLegendNode*> symbolNodes;
Expand All @@ -1479,7 +1497,7 @@ void QgsLayerTreeModel::invalidateLegendMapBasedData()
QgsSymbolLegendNode* n = dynamic_cast<QgsSymbolLegendNode*>( legendNode );
if ( n )
{
const QSize sz( n->minimumIconSize() );
const QSize sz( n->minimumIconSize( *context ) );
const QString parentKey( n->data( QgsLayerTreeModelLegendNode::ParentRuleKeyRole ).toString() );
widthMax[parentKey] = qMax( sz.width(), widthMax.contains( parentKey ) ? widthMax[parentKey] : 0 );
n->setIconSize( sz );
Expand Down
8 changes: 7 additions & 1 deletion src/core/layertree/qgslayertreemodel.h
Expand Up @@ -31,6 +31,7 @@ class QgsMapHitTest;
class QgsMapLayer;
class QgsMapSettings;
class QgsExpression;
class QgsRenderContext;

/** \ingroup core
* The QgsLayerTreeModel class is model implementation for Qt item views framework.
Expand Down Expand Up @@ -191,7 +192,7 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
//! Get hints about map view - to be used in legend nodes. Arguments that are not null will receive values.
//! If there are no valid map view data (from previous call to setLegendMapViewData()), returned values are zeros.
//! @note added in 2.6
void legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale );
void legendMapViewData( double *mapUnitsPerPixel, int *dpi, double *scale ) const;

//! Get map of map layer style overrides (key: layer ID, value: style name) where a different style should be used instead of the current one
//! @note added in 2.10
Expand Down Expand Up @@ -328,6 +329,11 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
int mLegendMapViewDpi;
double mLegendMapViewScale;
QTimer mDeferLegendInvalidationTimer;

private:

//! Returns a temporary render context
QgsRenderContext* createTemporaryRenderContext() const;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayerTreeModel::Flags )
Expand Down
12 changes: 8 additions & 4 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Expand Up @@ -157,23 +157,27 @@ Qt::ItemFlags QgsSymbolLegendNode::flags() const


QSize QgsSymbolLegendNode::minimumIconSize() const
{
QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );
return minimumIconSize( *context );
}

QSize QgsSymbolLegendNode::minimumIconSize( QgsRenderContext& context ) const
{
QSize minSz( 16, 16 );
if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Marker )
{
QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );
minSz = QgsImageOperation::nonTransparentImageRect(
QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), QSize( 512, 512 ),
context.data() ).toImage(),
&context ).toImage(),
minSz,
true ).size();
}
else if ( mItem.symbol() && mItem.symbol()->type() == QgsSymbol::Line )
{
QScopedPointer<QgsRenderContext> context( createTemporaryRenderContext() );
minSz = QgsImageOperation::nonTransparentImageRect(
QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), QSize( minSz.width(), 512 ),
context.data() ).toImage(),
&context ).toImage(),
minSz,
true ).size();
}
Expand Down
17 changes: 15 additions & 2 deletions src/core/layertree/qgslayertreemodellegendnode.h
Expand Up @@ -171,10 +171,23 @@ class CORE_EXPORT QgsSymbolLegendNode : public QgsLayerTreeModelLegendNode
//! @note added in 2.10
QSize iconSize() const { return mIconSize; }

//! Get the minimum icon size to prevent cropping
//! @note added in 2.10
/**
* Calculates the minimum icon size to prevent cropping. When evaluating
* the size for multiple icons it is more efficient to create a single
* render context in advance and use the variant which accepts a QgsRenderContext
* argument.
* @note added in 2.10
*/
QSize minimumIconSize() const;

/**
* Calculates the minimum icon size to prevent cropping. When evaluating
* the size for multiple icons it is more efficient to create a single
* render context in advance and call this method instead of minimumIconSize().
* @note added in QGIS 2.18
*/
QSize minimumIconSize( QgsRenderContext &context ) const;

/** Returns the symbol used by the legend node.
* @see setSymbol()
* @note added in QGIS 2.14
Expand Down

0 comments on commit c9251c5

Please sign in to comment.