Skip to content
2 changes: 1 addition & 1 deletion python/PyQt6/core/auto_additions/qgscolorramplegendnode.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The following has been generated automatically from src/core/layertree/qgscolorramplegendnode.h
try:
QgsColorRampLegendNode.__overridden_methods__ = ['data', 'drawSymbol', 'drawSymbolText', 'exportSymbolToJson']
QgsColorRampLegendNode.__overridden_methods__ = ['data', 'drawSymbol', 'drawSymbolText', 'exportSymbolToJson', 'invalidateDisplayData']
QgsColorRampLegendNode.__group__ = ['layertree']
except (NameError, AttributeError):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@
pass
try:
QgsLayerTreeModelLegendNode.__attribute_docs__ = {'dataChanged': 'Emitted on internal data change so the layer tree model can forward the\nsignal to views\n', 'sizeChanged': 'Emitted when the size of this node changes.\n\n.. versionadded:: 3.16\n'}
QgsLayerTreeModelLegendNode.__virtual_methods__ = ['flags', 'setData', 'isEmbeddedInParent', 'setEmbeddedInParent', 'userLabel', 'setUserLabel', 'userPatchSize', 'setUserPatchSize', 'setColumnBreak', 'columnBreak', 'isScaleOK', 'invalidateMapBasedData', 'draw', 'drawSymbol', 'exportSymbolToJson', 'drawSymbolText']
QgsLayerTreeModelLegendNode.__virtual_methods__ = ['flags', 'setData', 'isEmbeddedInParent', 'setEmbeddedInParent', 'userLabel', 'setUserLabel', 'userPatchSize', 'setUserPatchSize', 'setColumnBreak', 'columnBreak', 'isScaleOK', 'invalidateMapBasedData', 'invalidateDisplayData', 'draw', 'drawSymbol', 'exportSymbolToJson', 'drawSymbolText']
QgsLayerTreeModelLegendNode.__abstract_methods__ = ['data']
QgsLayerTreeModelLegendNode.__group__ = ['layertree']
except (NameError, AttributeError):
pass
try:
QgsSymbolLegendNode.__overridden_methods__ = ['flags', 'data', 'setData', 'drawSymbol', 'exportSymbolToJson', 'setEmbeddedInParent', 'setUserLabel', 'isScaleOK', 'invalidateMapBasedData']
QgsSymbolLegendNode.__overridden_methods__ = ['flags', 'data', 'setData', 'invalidateDisplayData', 'drawSymbol', 'exportSymbolToJson', 'setEmbeddedInParent', 'setUserLabel', 'isScaleOK', 'invalidateMapBasedData']
QgsSymbolLegendNode.__group__ = ['layertree']
except (NameError, AttributeError):
pass
Expand All @@ -92,7 +92,7 @@
except (NameError, AttributeError):
pass
try:
QgsDataDefinedSizeLegendNode.__overridden_methods__ = ['data', 'draw']
QgsDataDefinedSizeLegendNode.__overridden_methods__ = ['data', 'invalidateDisplayData', 'draw']
QgsDataDefinedSizeLegendNode.__group__ = ['layertree']
except (NameError, AttributeError):
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ Constructor for QgsColorRampLegendNode.

virtual QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const;

virtual void invalidateDisplayData();


void setIconSize( QSize size );
%Docstring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,18 @@ which corresponds exactly to the view's screen properties in which this
model is used.

.. versionadded:: 3.32
%End

void setTargetScreenProperties( const QSet< QgsScreenProperties > &properties );
%Docstring
Sets all target screen ``properties`` to use when generating icons for
Qt.DecorationRole data.

This allows icons to be generated at an icon device pixel ratio and DPI
which corresponds exactly to the view's screen properties in which this
model is used.

.. versionadded:: 4.2
%End

QSet< QgsScreenProperties > targetScreenProperties() const;
Expand Down Expand Up @@ -395,6 +407,13 @@ progress.
.. seealso:: :py:func:`hitTestCompleted`

.. versionadded:: 3.32
%End

void invalidateDisplayData();
%Docstring
Invalidates all cached display data throughout the model.

.. versionadded:: 4.2
%End

signals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ Returns whether a forced column break should occur before the node.
%Docstring
Notification from model that information from associated map view has
changed. Default implementation does nothing.
%End

virtual void invalidateDisplayData();
%Docstring
Invalidates cached display data for the node.

The default implementation does nothing.

.. versionadded:: 4.2
%End

struct ItemContext
Expand Down Expand Up @@ -348,6 +357,7 @@ Constructor for QgsSymbolLegendNode.

virtual bool setData( const QVariant &value, int role );

virtual void invalidateDisplayData();

virtual QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const;

Expand Down Expand Up @@ -729,6 +739,7 @@ definition of the node's appearance

virtual QVariant data( int role ) const;

virtual void invalidateDisplayData();

virtual ItemMetrics draw( const QgsLegendSettings &settings, ItemContext &ctx );

Expand Down
43 changes: 32 additions & 11 deletions src/core/layertree/qgscolorramplegendnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ QVariant QgsColorRampLegendNode::data( int role ) const
}
else if ( role == Qt::DecorationRole )
{
if ( mPixmap.isNull() || mPixmap.size() != mIconSize )
if ( mPixmap.isNull() )
{
const QFont font = data( Qt::FontRole ).value< QFont >();

Expand All @@ -126,30 +126,42 @@ QVariant QgsColorRampLegendNode::data( int role ) const
const QRect minBoundingRect = fm.boundingRect( minLabel );
const QRect maxBoundingRect = fm.boundingRect( maxLabel );

const int minLabelWidth = minBoundingRect.width();
const int maxLabelWidth = maxBoundingRect.width();
const int maxTextWidth = std::max( minLabelWidth, maxLabelWidth );
const int labelGapFromRamp = fm.boundingRect( u"x"_s ).width();
const int extraAllowance = labelGapFromRamp * 0.4; // extra allowance to avoid text clipping on right
QRect labelRect;
QgsScreenProperties targetScreen = model() && !model()->targetScreenProperties().isEmpty() ? *model()->targetScreenProperties().begin() : QgsScreenProperties();
double devicePixelRatio = 1;
if ( targetScreen.isValid() )
{
devicePixelRatio = targetScreen.devicePixelRatio();
}

const double minLabelWidth = minBoundingRect.width() * devicePixelRatio;
const double maxLabelWidth = maxBoundingRect.width() * devicePixelRatio;
const double maxTextWidth = std::max( minLabelWidth, maxLabelWidth );
const double labelGapFromRamp = fm.boundingRect( u"x"_s ).width() * devicePixelRatio;
const double extraAllowance = labelGapFromRamp * 0.4; // extra allowance to avoid text clipping on right
QRectF labelRect;
QSize rampSize;
switch ( mSettings.orientation() )
{
case Qt::Vertical:
labelRect = QRect( mIconSize.width() + labelGapFromRamp, 0, maxTextWidth + extraAllowance, mIconSize.height() );
labelRect = QRectF( ( mIconSize.width() + labelGapFromRamp ) / devicePixelRatio, 0, ( maxTextWidth + extraAllowance ) / devicePixelRatio, mIconSize.height() / devicePixelRatio );
mPixmap = QPixmap( mIconSize.width() + maxTextWidth + labelGapFromRamp + extraAllowance, mIconSize.height() );
rampSize = mIconSize;
break;

case Qt::Horizontal:
labelRect
= QRect( 0, mIconSize.height() + labelGapFromRamp, std::max( mIconSize.width(), minLabelWidth + maxLabelWidth + labelGapFromRamp ), std::max( minBoundingRect.height(), maxBoundingRect.height() ) + extraAllowance );
mPixmap = QPixmap( std::max( mIconSize.width(), minLabelWidth + maxLabelWidth + labelGapFromRamp ), mIconSize.height() + maxTextWidth + labelGapFromRamp + extraAllowance );
labelRect = QRectF(
0,
( mIconSize.height() + labelGapFromRamp ) / devicePixelRatio,
std::max( static_cast<double>( mIconSize.width() ), minLabelWidth + maxLabelWidth + labelGapFromRamp ) / devicePixelRatio,
( std::max( minBoundingRect.height() * devicePixelRatio, maxBoundingRect.height() * devicePixelRatio ) + extraAllowance ) / devicePixelRatio
);
mPixmap = QPixmap( std::max( static_cast< double >( mIconSize.width() ), minLabelWidth + maxLabelWidth + labelGapFromRamp ), mIconSize.height() + maxTextWidth + labelGapFromRamp + extraAllowance );
rampSize = QSize( labelRect.width(), mIconSize.height() );
break;
}

mPixmap.fill( Qt::transparent );
mPixmap.setDevicePixelRatio( devicePixelRatio );

QPixmap pix;

Expand All @@ -170,6 +182,8 @@ QVariant QgsColorRampLegendNode::data( int role ) const
pix.fill( Qt::transparent );
}

pix.setDevicePixelRatio( devicePixelRatio );

QPainter p( &mPixmap );
p.drawPixmap( 0, 0, pix );
p.setFont( font );
Expand Down Expand Up @@ -538,3 +552,10 @@ QJsonObject QgsColorRampLegendNode::exportSymbolToJson( const QgsLegendSettings

return json;
}

void QgsColorRampLegendNode::invalidateDisplayData()
{
mPixmap = QPixmap();
const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
mIconSize = mSettings.orientation() == Qt::Vertical ? QSize( iconSize, iconSize * 6 ) : QSize( iconSize * 6, iconSize );
}
1 change: 1 addition & 0 deletions src/core/layertree/qgscolorramplegendnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class CORE_EXPORT QgsColorRampLegendNode : public QgsLayerTreeModelLegendNode
QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const override;
QSizeF drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const override;
QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const override;
void invalidateDisplayData() override;

/**
* Set the icon \a size, which controls how large the ramp will render in a layer tree widget.
Expand Down
41 changes: 41 additions & 0 deletions src/core/layertree/qgslayertreemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,12 @@ void QgsLayerTreeModel::addTargetScreenProperties( const QgsScreenProperties &pr
mTargetScreenProperties.insert( properties );
}

void QgsLayerTreeModel::setTargetScreenProperties( const QSet<QgsScreenProperties> &properties )
{
mTargetScreenProperties = properties;
invalidateDisplayData();
}

QSet<QgsScreenProperties> QgsLayerTreeModel::targetScreenProperties() const
{
return mTargetScreenProperties;
Expand All @@ -822,6 +828,41 @@ bool QgsLayerTreeModel::hitTestInProgress() const
return static_cast< bool >( mHitTestTask );
}

void QgsLayerTreeModel::invalidateDisplayData()
{
std::function< void( QgsLayerTreeNode * ) > invalidateNode;
invalidateNode = [this, &invalidateNode]( QgsLayerTreeNode *node ) {
if ( !node )
return;

switch ( node->nodeType() )
{
case QgsLayerTreeNode::NodeLayer:
{
auto layerNode = qobject_cast< QgsLayerTreeLayer * >( node );
const QList<QgsLayerTreeModelLegendNode *> legendNodes = layerLegendNodes( layerNode );
for ( QgsLayerTreeModelLegendNode *legendNode : legendNodes )
{
legendNode->invalidateDisplayData();
}

break;
}

case QgsLayerTreeNode::NodeGroup:
case QgsLayerTreeNode::NodeCustom:
break;
}

const QList<QgsLayerTreeNode *> children = node->children();
for ( QgsLayerTreeNode *childNode : children )
{
invalidateNode( childNode );
}
};
invalidateNode( mRootNode );
}

void QgsLayerTreeModel::nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo )
{
beginInsertRows( node2index( node ), indexFrom, indexTo );
Expand Down
17 changes: 17 additions & 0 deletions src/core/layertree/qgslayertreemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
*/
void addTargetScreenProperties( const QgsScreenProperties &properties );

/**
* Sets all target screen \a properties to use when generating icons for Qt::DecorationRole data.
*
* This allows icons to be generated at an icon device pixel ratio and DPI which
* corresponds exactly to the view's screen properties in which this model is used.
*
* \since QGIS 4.2
*/
void setTargetScreenProperties( const QSet< QgsScreenProperties > &properties );

/**
* Returns the target screen properties to use when generating icons.
*
Expand Down Expand Up @@ -342,6 +352,13 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
*/
bool hitTestInProgress() const;

/**
* Invalidates all cached display data throughout the model.
*
* \since QGIS 4.2
*/
void invalidateDisplayData();

signals:

/**
Expand Down
10 changes: 10 additions & 0 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,11 @@ bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
return true;
}

void QgsSymbolLegendNode::invalidateDisplayData()
{
mPixmap = QPixmap();
}

QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const
{
QgsSymbol *s = mCustomSymbol ? mCustomSymbol.get() : mItem.symbol();
Expand Down Expand Up @@ -1513,6 +1518,11 @@ QVariant QgsDataDefinedSizeLegendNode::data( int role ) const
return QVariant();
}

void QgsDataDefinedSizeLegendNode::invalidateDisplayData()
{
mImage = QImage();
}

QgsLayerTreeModelLegendNode::ItemMetrics QgsDataDefinedSizeLegendNode::draw( const QgsLegendSettings &settings, QgsLayerTreeModelLegendNode::ItemContext &ctx )
{
// setup temporary render context if none specified
Expand Down
13 changes: 11 additions & 2 deletions src/core/layertree/qgslayertreemodellegendnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ class CORE_EXPORT QgsLayerTreeModelLegendNode : public QObject
*/
virtual void invalidateMapBasedData() {}

/**
* Invalidates cached display data for the node.
*
* The default implementation does nothing.
*
* \since QGIS 4.2
*/
virtual void invalidateDisplayData() {}

struct ItemContext
{
Q_NOWARN_DEPRECATED_PUSH //because of deprecated members
Expand Down Expand Up @@ -426,7 +435,7 @@ class CORE_EXPORT QgsSymbolLegendNode : public QgsLayerTreeModelLegendNode
Qt::ItemFlags flags() const override;
QVariant data( int role ) const override;
bool setData( const QVariant &value, int role ) override;

void invalidateDisplayData() override;
QSizeF drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const override;

QJsonObject exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context ) const override;
Expand Down Expand Up @@ -833,7 +842,7 @@ class CORE_EXPORT QgsDataDefinedSizeLegendNode : public QgsLayerTreeModelLegendN
~QgsDataDefinedSizeLegendNode() override;

QVariant data( int role ) const override;

void invalidateDisplayData() override;
ItemMetrics draw( const QgsLegendSettings &settings, ItemContext &ctx ) override;

#ifdef SIP_RUN
Expand Down
3 changes: 1 addition & 2 deletions src/core/maprenderer/qgsmaprendererjob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1181,13 +1181,12 @@ LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter *painter, QgsLabe
return job;

// if we can use the cache, let's do it and avoid rendering!
bool hasCache = canUseLabelCache && mCache && mCache->hasCacheImage( LABEL_CACHE_ID );
bool hasCache = canUseLabelCache && mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) && mCache->cacheImage( LABEL_CACHE_ID ).devicePixelRatio() == mSettings.devicePixelRatio();
if ( hasCache )
{
job.cached = true;
job.complete = true;
job.img = new QImage( mCache->cacheImage( LABEL_CACHE_ID ) );
Q_ASSERT( job.img->devicePixelRatio() == mSettings.devicePixelRatio() );
job.context.setPainter( nullptr );
}
else
Expand Down
4 changes: 2 additions & 2 deletions src/core/symbology/qgssymbollayerutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1172,7 +1172,7 @@ QPixmap QgsSymbolLayerUtils::colorRampPreviewPixmap( QgsColorRamp *ramp, QSize s
const QPen pen( ramp->color( static_cast< double >( i ) / size.width() ) );
painter.setPen( pen );
const int x = flipDirection ? size.width() - i - 1 : i;
painter.drawLine( x, 0 + padding, x, size.height() - 1 - padding );
painter.drawLine( QLineF( x, 0 + padding, x, size.height() - 1 - padding ) );
}
break;
}
Expand All @@ -1184,7 +1184,7 @@ QPixmap QgsSymbolLayerUtils::colorRampPreviewPixmap( QgsColorRamp *ramp, QSize s
const QPen pen( ramp->color( static_cast< double >( i ) / size.height() ) );
painter.setPen( pen );
const int y = flipDirection ? size.height() - i - 1 : i;
painter.drawLine( 0 + padding, y, size.width() - 1 - padding, y );
painter.drawLine( QLineF( 0 + padding, y, size.width() - 1 - padding, y ) );
}
break;
}
Expand Down
5 changes: 4 additions & 1 deletion src/gui/layertree/qgslayertreeview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "qgslayertreeviewdefaultactions.h"
#include "qgsmaplayer.h"
#include "qgsmessagebar.h"
#include "qgsscreenhelper.h"

#include <QApplication>
#include <QContextMenuEvent>
Expand Down Expand Up @@ -96,7 +97,9 @@ void QgsLayerTreeViewBase::setLayerTreeModel( QgsLayerTreeModel *model )

mLayerTreeModel = model;

mLayerTreeModel->addTargetScreenProperties( QgsScreenProperties( screen() ) );
mLayerTreeModel->setTargetScreenProperties( { QgsScreenProperties( screen() ) } );
auto screenHelper = new QgsScreenHelper( this );
connect( screenHelper, &QgsScreenHelper::screenDpiChanged, this, [this] { mLayerTreeModel->setTargetScreenProperties( { QgsScreenProperties( screen() ) } ); } );

connect( mLayerTreeModel->rootGroup(), &QgsLayerTreeNode::expandedChanged, this, &QgsLayerTreeViewBase::onExpandedChanged );

Expand Down
Loading
Loading