Skip to content

Commit

Permalink
Allow customisation of color ramp legend items in a print layout legend
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 18, 2020
1 parent 55dbd9c commit 24549f0
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 132 deletions.
Expand Up @@ -55,7 +55,6 @@ Constructor for QgsColorRampLegendNode.
virtual QSizeF drawSymbolText( const QgsLegendSettings &settings, ItemContext *ctx, QSizeF symbolSize ) const;



void setIconSize( QSize size );
%Docstring
Set the icon ``size``, which controls how large the ramp will render in a layer tree widget.
Expand All @@ -73,6 +72,20 @@ Returns the icon size, which is how large the ramp will render in a layer tree w
const QgsColorRamp *ramp() const;
%Docstring
Returns the color ramp used by the node.
%End

QgsColorRampLegendNodeSettings settings() const;
%Docstring
Returns the node's settings.

.. seealso:: :py:func:`setSettings`
%End

void setSettings( const QgsColorRampLegendNodeSettings &settings );
%Docstring
Sets the node's ``settings``.

.. seealso:: :py:func:`settings`
%End

};
Expand Down
33 changes: 32 additions & 1 deletion python/core/auto_generated/qgsmaplayerlegend.sip.in
Expand Up @@ -147,7 +147,7 @@ symbol width or height from :py:class:`QgsLegendSettings`.

static void setLegendNodeCustomSymbol( QgsLayerTreeLayer *nodeLayer, int originalIndex, const QgsSymbol *symbol );
%Docstring
Sets a custom legend ``symbol`` size for the legend node belonging to ``nodeLayer`` at the specified ``originalIndex``.
Sets a custom legend ``symbol`` for the legend node belonging to ``nodeLayer`` at the specified ``originalIndex``.

If ``symbol`` is non-``None``, it will be used in place of the default symbol when rendering
the legend node.
Expand All @@ -171,6 +171,37 @@ Caller takes ownership of the returned symbol.
.. versionadded:: 3.14
%End

static void setLegendNodeColorRampSettings( QgsLayerTreeLayer *nodeLayer, int originalIndex, const QgsColorRampLegendNodeSettings *settings );
%Docstring
Sets a custom legend color ramp ``settings`` for the legend node belonging to ``nodeLayer`` at the specified ``originalIndex``.

If the corresponding legend node is not a QgsColorRampLegendNode then calling this method will have no effect.

If ``settings`` is non-``None``, they will be used in place of the default settigns when rendering
the legend node.

.. seealso:: :py:func:`legendNodeColorRampSettings`

.. versionadded:: 3.18
%End

static QgsColorRampLegendNodeSettings *legendNodeColorRampSettings( QgsLayerTreeLayer *nodeLayer, int originalIndex ) /Factory/;
%Docstring
Returns the custom legend color ramp settings for the legend node belonging to ``nodeLayer`` at the specified ``originalIndex``.

If the corresponding legend node is not a QgsColorRampLegendNode then calling this method will return ``None``.

If the returned value is non-``None``, they will be used in place of the default settings when rendering
the legend node.

Caller takes ownership of the returned settings.

.. seealso:: :py:func:`setLegendNodeColorRampSettings`

.. versionadded:: 3.18
%End


static void setLegendNodeColumnBreak( QgsLayerTreeLayer *nodeLayer, int originalIndex, bool columnBreakBeforeNode );
%Docstring
Sets whether a forced column break should occur before the node.
Expand Down
49 changes: 36 additions & 13 deletions src/core/layertree/qgscolorramplegendnode.cpp
Expand Up @@ -39,14 +39,9 @@ QgsColorRampLegendNode::QgsColorRampLegendNode( QgsLayerTreeLayer *nodeLayer, Qg
: QgsLayerTreeModelLegendNode( nodeLayer, parent )
, mRamp( ramp )
, mSettings( settings )
, mMinimumValue( minimumValue )
, mMaximumValue( maximumValue )
{
QgsNumericFormatContext numericContext;
if ( mSettings.minimumLabel().isEmpty() )
mSettings.setMinimumLabel( settings.numericFormat()->formatDouble( minimumValue, numericContext ) );

if ( mSettings.maximumLabel().isEmpty() )
mSettings.setMaximumLabel( settings.numericFormat()->formatDouble( maximumValue, numericContext ) );

const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
mIconSize = QSize( iconSize, iconSize * 6 );

Expand All @@ -58,6 +53,34 @@ const QgsColorRamp *QgsColorRampLegendNode::ramp() const
return mRamp.get();
}

QgsColorRampLegendNodeSettings QgsColorRampLegendNode::settings() const
{
return mSettings;
}

void QgsColorRampLegendNode::setSettings( const QgsColorRampLegendNodeSettings &settings )
{
mSettings = settings;
}

QString QgsColorRampLegendNode::labelForMinimum() const
{
if ( !mSettings.minimumLabel().isEmpty() )
return mSettings.prefix() + mSettings.minimumLabel() + mSettings.suffix();

QgsNumericFormatContext numericContext;
return mSettings.prefix() + mSettings.numericFormat()->formatDouble( mMinimumValue, numericContext ) + mSettings.suffix();
}

QString QgsColorRampLegendNode::labelForMaximum() const
{
if ( !mSettings.maximumLabel().isEmpty() )
return mSettings.prefix() + mSettings.maximumLabel() + mSettings.suffix();

QgsNumericFormatContext numericContext;
return mSettings.prefix() + mSettings.numericFormat()->formatDouble( mMaximumValue, numericContext ) + mSettings.suffix();
}

QVariant QgsColorRampLegendNode::data( int role ) const
{
if ( role == Qt::DisplayRole )
Expand Down Expand Up @@ -86,8 +109,8 @@ QVariant QgsColorRampLegendNode::data( int role ) const

const QFont font = data( Qt::FontRole ).value< QFont >();

const QString minLabel = mSettings.prefix() + mSettings.minimumLabel() + mSettings.suffix();
const QString maxLabel = mSettings.prefix() + mSettings.maximumLabel() + mSettings.suffix();
const QString minLabel = labelForMinimum();
const QString maxLabel = labelForMaximum();

const QFontMetrics fm( font );
const int maxTextWidth = std::max( fm.boundingRect( minLabel ).width(), fm.boundingRect( maxLabel ).width() );
Expand Down Expand Up @@ -152,8 +175,8 @@ QSizeF QgsColorRampLegendNode::drawSymbol( const QgsLegendSettings &settings, It
QgsTextFormat format = QgsTextFormat::fromQFont( symbolLabelFont );
format.setColor( settings.fontColor() );

const QString minLabel = mSettings.prefix() + mSettings.minimumLabel() + mSettings.suffix();
const QString maxLabel = mSettings.prefix() + mSettings.maximumLabel() + mSettings.suffix();
const QString minLabel = labelForMinimum();
const QString maxLabel = labelForMaximum();

double minHeightMm = QgsTextRenderer::textHeight( *context, format, QStringList() << minLabel << maxLabel, QgsTextRenderer::Rect ) / context->scaleFactor();

Expand Down Expand Up @@ -259,8 +282,8 @@ QSizeF QgsColorRampLegendNode::drawSymbolText( const QgsLegendSettings &settings
QgsTextFormat format = QgsTextFormat::fromQFont( symbolLabelFont );
format.setColor( settings.fontColor() );

const QString minLabel = mSettings.prefix() + mSettings.minimumLabel() + mSettings.suffix();
const QString maxLabel = mSettings.prefix() + mSettings.maximumLabel() + mSettings.suffix();
const QString minLabel = labelForMinimum();
const QString maxLabel = labelForMaximum();

const double height = symbolSize.height();
const double width = symbolSize.width();
Expand Down
20 changes: 19 additions & 1 deletion src/core/layertree/qgscolorramplegendnode.h
Expand Up @@ -65,7 +65,6 @@ 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;


/**
* Set the icon \a size, which controls how large the ramp will render in a layer tree widget.
*
Expand All @@ -85,13 +84,32 @@ class CORE_EXPORT QgsColorRampLegendNode : public QgsLayerTreeModelLegendNode
*/
const QgsColorRamp *ramp() const;

/**
* Returns the node's settings.
*
* \see setSettings()
*/
QgsColorRampLegendNodeSettings settings() const;

/**
* Sets the node's \a settings.
*
* \see settings()
*/
void setSettings( const QgsColorRampLegendNodeSettings &settings );

private:
QString labelForMinimum() const;
QString labelForMaximum() const;

std::unique_ptr< QgsColorRamp > mRamp;

mutable QPixmap mPixmap; // cached symbol preview
QSize mIconSize;

QgsColorRampLegendNodeSettings mSettings;
double mMinimumValue = 0;
double mMaximumValue = 0;

};

Expand Down
42 changes: 42 additions & 0 deletions src/core/qgsmaplayerlegend.cpp
Expand Up @@ -225,6 +225,40 @@ QgsSymbol *QgsMapLayerLegendUtils::legendNodeCustomSymbol( QgsLayerTreeLayer *no
return QgsSymbolLayerUtils::loadSymbol( elem, rwContext );
}

void QgsMapLayerLegendUtils::setLegendNodeColorRampSettings( QgsLayerTreeLayer *nodeLayer, int originalIndex, const QgsColorRampLegendNodeSettings *settings )
{
if ( settings )
{
QDomDocument doc;
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
QDomElement elem = doc.createElement( QStringLiteral( "rampSettings" ) );
settings->writeXml( doc, elem, rwContext );
doc.appendChild( elem );
nodeLayer->setCustomProperty( "legend/custom-ramp-settings-" + QString::number( originalIndex ), doc.toString() );
}
else
nodeLayer->removeCustomProperty( "legend/custom-ramp-settings-" + QString::number( originalIndex ) );
}

QgsColorRampLegendNodeSettings *QgsMapLayerLegendUtils::legendNodeColorRampSettings( QgsLayerTreeLayer *nodeLayer, int originalIndex )
{
const QString settingsDef = nodeLayer->customProperty( "legend/custom-ramp-settings-" + QString::number( originalIndex ) ).toString();
if ( settingsDef.isEmpty() )
return nullptr;

QDomDocument doc;
doc.setContent( settingsDef );
const QDomElement elem = doc.documentElement();

QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );

QgsColorRampLegendNodeSettings settings;
settings.readXml( elem, rwContext );
return new QgsColorRampLegendNodeSettings( settings );
}

void QgsMapLayerLegendUtils::setLegendNodeColumnBreak( QgsLayerTreeLayer *nodeLayer, int originalIndex, bool columnBreakBeforeNode )
{
if ( columnBreakBeforeNode )
Expand Down Expand Up @@ -256,6 +290,14 @@ void QgsMapLayerLegendUtils::applyLayerNodeProperties( QgsLayerTreeLayer *nodeLa

symbolNode->setCustomSymbol( QgsMapLayerLegendUtils::legendNodeCustomSymbol( nodeLayer, i ) );
}
else if ( QgsColorRampLegendNode *colorRampNode = dynamic_cast< QgsColorRampLegendNode * >( legendNode ) )
{
std::unique_ptr< QgsColorRampLegendNodeSettings > settings( QgsMapLayerLegendUtils::legendNodeColorRampSettings( nodeLayer, i ) );
if ( settings )
{
colorRampNode->setSettings( *settings );
}
}

const QSizeF userSize = QgsMapLayerLegendUtils::legendNodeSymbolSize( nodeLayer, i );
if ( userSize.isValid() )
Expand Down
32 changes: 31 additions & 1 deletion src/core/qgsmaplayerlegend.h
Expand Up @@ -31,6 +31,7 @@ class QgsPointCloudLayer;
class QgsReadWriteContext;
class QgsVectorLayer;
class QgsLegendPatchShape;
class QgsColorRampLegendNodeSettings;
class QgsSymbol;

#include "qgis_core.h"
Expand Down Expand Up @@ -150,7 +151,7 @@ class CORE_EXPORT QgsMapLayerLegendUtils
static QSizeF legendNodeSymbolSize( QgsLayerTreeLayer *nodeLayer, int originalIndex );

/**
* Sets a custom legend \a symbol size for the legend node belonging to \a nodeLayer at the specified \a originalIndex.
* Sets a custom legend \a symbol for the legend node belonging to \a nodeLayer at the specified \a originalIndex.
*
* If \a symbol is non-NULLPTR, it will be used in place of the default symbol when rendering
* the legend node.
Expand All @@ -173,6 +174,35 @@ class CORE_EXPORT QgsMapLayerLegendUtils
*/
static QgsSymbol *legendNodeCustomSymbol( QgsLayerTreeLayer *nodeLayer, int originalIndex ) SIP_FACTORY;

/**
* Sets a custom legend color ramp \a settings for the legend node belonging to \a nodeLayer at the specified \a originalIndex.
*
* If the corresponding legend node is not a QgsColorRampLegendNode then calling this method will have no effect.
*
* If \a settings is non-NULLPTR, they will be used in place of the default settigns when rendering
* the legend node.
*
* \see legendNodeColorRampSettings()
* \since QGIS 3.18
*/
static void setLegendNodeColorRampSettings( QgsLayerTreeLayer *nodeLayer, int originalIndex, const QgsColorRampLegendNodeSettings *settings );

/**
* Returns the custom legend color ramp settings for the legend node belonging to \a nodeLayer at the specified \a originalIndex.
*
* If the corresponding legend node is not a QgsColorRampLegendNode then calling this method will return NULLPTR.
*
* If the returned value is non-NULLPTR, they will be used in place of the default settings when rendering
* the legend node.
*
* Caller takes ownership of the returned settings.
*
* \see setLegendNodeColorRampSettings()
* \since QGIS 3.18
*/
static QgsColorRampLegendNodeSettings *legendNodeColorRampSettings( QgsLayerTreeLayer *nodeLayer, int originalIndex ) SIP_FACTORY;


/**
* Sets whether a forced column break should occur before the node.
*
Expand Down
26 changes: 26 additions & 0 deletions src/gui/layout/qgslayoutlegendwidget.cpp
Expand Up @@ -42,6 +42,7 @@
#include "qgsexpressioncontextutils.h"
#include "qgslegendpatchshapewidget.h"
#include "qgslayertreefilterproxymodel.h"
#include "qgscolorramplegendnodewidget.h"

#include <QMenu>
#include <QMessageBox>
Expand Down Expand Up @@ -1586,6 +1587,16 @@ QgsLayoutLegendNodeWidget::QgsLayoutLegendNodeWidget( QgsLayoutItemLegend *legen
mPatchShapeButton->hide();
}

if ( QgsColorRampLegendNode *colorRampNode = dynamic_cast< QgsColorRampLegendNode * >( mLegendNode ) )
{
mLabelGroup->hide();
mColorRampLegendWidget->setSettings( colorRampNode->settings() );
}
else
{
mColorRampLegendWidget->hide();
}

if ( mLegendNode )
{
switch ( static_cast< QgsLayerTreeModelLegendNode::NodeTypes >( mLegendNode->data( QgsLayerTreeModelLegendNode::NodeTypeRole ).toInt() ) )
Expand Down Expand Up @@ -1619,6 +1630,8 @@ QgsLayoutLegendNodeWidget::QgsLayoutLegendNodeWidget( QgsLayoutItemLegend *legen
connect( mColumnBreakBeforeCheckBox, &QCheckBox::toggled, this, &QgsLayoutLegendNodeWidget::columnBreakToggled );

connect( mColumnSplitBehaviorComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsLayoutLegendNodeWidget::columnSplitChanged );

connect( mColorRampLegendWidget, &QgsColorRampLegendNodeWidget::widgetChanged, this, &QgsLayoutLegendNodeWidget::colorRampLegendChanged );
}

void QgsLayoutLegendNodeWidget::labelChanged()
Expand Down Expand Up @@ -1791,6 +1804,19 @@ void QgsLayoutLegendNodeWidget::customSymbolChanged()
mLegend->endCommand();
}

void QgsLayoutLegendNodeWidget::colorRampLegendChanged()
{
mLegend->beginCommand( tr( "Edit Legend Item" ) );

QgsColorRampLegendNodeSettings settings = mColorRampLegendWidget->settings();
QgsMapLayerLegendUtils::setLegendNodeColorRampSettings( mLayer, mOriginalLegendNodeIndex, &settings );
mLegend->model()->refreshLayerLegend( mLayer );

mLegend->adjustBoxSize();
mLegend->updateFilterByMap();
mLegend->endCommand();
}

void QgsLayoutLegendNodeWidget::columnBreakToggled( bool checked )
{
mLegend->beginCommand( tr( "Edit Legend Columns" ) );
Expand Down
1 change: 1 addition & 0 deletions src/gui/layout/qgslayoutlegendwidget.h
Expand Up @@ -197,6 +197,7 @@ class GUI_EXPORT QgsLayoutLegendNodeWidget: public QgsPanelWidget, private Ui::Q
void insertExpression();
void sizeChanged( double );
void customSymbolChanged();
void colorRampLegendChanged();
void columnBreakToggled( bool checked );
void columnSplitChanged();

Expand Down

0 comments on commit 24549f0

Please sign in to comment.