Skip to content
Permalink
Browse files

[FEATURE][layouts] Add user control over scalebar numeric formats

Gives users control over all the formatting properties for the numbers
in scalebars, including whether they want thousand separators, decimal
places, scientific notation, etc

Fixes #21341
  • Loading branch information
nyalldawson committed Jan 8, 2020
1 parent ad6684b commit 16c22541414181055b0ed9fd0af939b9b0839133
@@ -538,6 +538,26 @@ Possibilities are: 'Single Box', 'Double Box', 'Line Ticks Middle',
Returns the scale bar style name.

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

const QgsNumericFormat *numericFormat() const;
%Docstring
Returns the numeric format used for numbers in the scalebar.

.. seealso:: :py:func:`setNumericFormat`

.. versionadded:: 3.12
%End

void setNumericFormat( QgsNumericFormat *format /Transfer/ );
%Docstring
Sets the numeric ``format`` used for numbers in the scalebar.

Ownership of ``format`` is transferred to the scalebar.

.. seealso:: :py:func:`numericFormat`

.. versionadded:: 3.12
%End

void update();
@@ -8,6 +8,8 @@





class QgsScaleBarSettings
{
%Docstring
@@ -52,6 +54,14 @@ for scalebar drawing with QgsScaleBarRenderer.
Constructor for QgsScaleBarSettings.
%End

~QgsScaleBarSettings();

QgsScaleBarSettings( const QgsScaleBarSettings &other );
%Docstring
Copy constructor
%End


int numberOfSegments() const;
%Docstring
Returns the number of segments included in the scalebar.
@@ -514,6 +524,26 @@ Returns the cap style used for drawing lines in the scalebar.
Sets the cap ``style`` used when drawing the lines in the scalebar.

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

const QgsNumericFormat *numericFormat() const;
%Docstring
Returns the numeric format used for numbers in the scalebar.

.. seealso:: :py:func:`setNumericFormat`

.. versionadded:: 3.12
%End

void setNumericFormat( QgsNumericFormat *format /Transfer/ );
%Docstring
Sets the numeric ``format`` used for numbers in the scalebar.

Ownership of ``format`` is transferred to the settings.

.. seealso:: :py:func:`numericFormat`

.. versionadded:: 3.12
%End

};
@@ -442,6 +442,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/core/labeling
${CMAKE_SOURCE_DIR}/src/core/layertree
${CMAKE_SOURCE_DIR}/src/core/locator
${CMAKE_SOURCE_DIR}/src/core/numericformats
${CMAKE_SOURCE_DIR}/src/core/pal
${CMAKE_SOURCE_DIR}/src/core/providers/memory
${CMAKE_SOURCE_DIR}/src/core/raster
@@ -454,6 +455,7 @@ INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/src/gui/attributetable
${CMAKE_SOURCE_DIR}/src/gui/auth
${CMAKE_SOURCE_DIR}/src/gui/labeling
${CMAKE_SOURCE_DIR}/src/gui/numericformats
${CMAKE_SOURCE_DIR}/src/gui/ogr
${CMAKE_SOURCE_DIR}/src/gui/numericformats
${CMAKE_SOURCE_DIR}/src/gui/processing
@@ -20,6 +20,7 @@
#include "qgslayout.h"
#include "qgsguiutils.h"
#include "qgsvectorlayer.h"
#include "qgsnumericformatselectorwidget.h"

#include <QColorDialog>
#include <QFontDialog>
@@ -50,7 +51,7 @@ QgsLayoutScaleBarWidget::QgsLayoutScaleBarWidget( QgsLayoutItemScaleBar *scaleBa
connect( mLineJoinStyleCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutScaleBarWidget::mLineJoinStyleCombo_currentIndexChanged );
connect( mLineCapStyleCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLayoutScaleBarWidget::mLineCapStyleCombo_currentIndexChanged );
connect( mMinWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutScaleBarWidget::mMinWidthSpinBox_valueChanged );
connect( mMaxWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutScaleBarWidget::mMaxWidthSpinBox_valueChanged );
connect( mNumberFormatPushButton, &QPushButton::clicked, this, &QgsLayoutScaleBarWidget::changeNumberFormat );
setPanelTitle( tr( "Scalebar Properties" ) );

mFontButton->registerExpressionContextGenerator( this );
@@ -362,6 +363,29 @@ void QgsLayoutScaleBarWidget::textFormatChanged()
mScalebar->update();
}

void QgsLayoutScaleBarWidget::changeNumberFormat()
{
if ( !mScalebar )
{
return;
}

QgsNumericFormatSelectorWidget *widget = new QgsNumericFormatSelectorWidget( this );
widget->setPanelTitle( tr( "Number Format" ) );
widget->setFormat( mScalebar->numericFormat() );
connect( widget, &QgsNumericFormatSelectorWidget::changed, this, [ = ]
{
mScalebar->beginCommand( tr( "Set Scalebar Number Format" ) );
disconnectUpdateSignal();
mScalebar->setNumericFormat( widget->format() );
connectUpdateSignal();
mScalebar->endCommand();
mScalebar->update();
} );
openPanel( widget );
return;
}

void QgsLayoutScaleBarWidget::mFillColorButton_colorChanged( const QColor &newColor )
{
if ( !mScalebar )
@@ -70,6 +70,7 @@ class QgsLayoutScaleBarWidget: public QgsLayoutItemBaseWidget, public QgsExpress
void segmentSizeRadioChanged( QAbstractButton *radio );
void mapChanged( QgsLayoutItem *item );
void textFormatChanged();
void changeNumberFormat();

private:
QPointer< QgsLayoutItemScaleBar > mScalebar;
@@ -182,6 +182,7 @@ SET(QGIS_CORE_SRCS
scalebar/qgsdoubleboxscalebarrenderer.cpp
scalebar/qgsnumericscalebarrenderer.cpp
scalebar/qgsscalebarrenderer.cpp
scalebar/qgsscalebarsettings.cpp
scalebar/qgssingleboxscalebarrenderer.cpp
scalebar/qgsticksscalebarrenderer.cpp

@@ -33,6 +33,8 @@
#include "qgsunittypes.h"
#include "qgssettings.h"
#include "qgsstyleentityvisitor.h"
#include "qgsnumericformat.h"
#include "qgsnumericformatregistry.h"

#include <QDomDocument>
#include <QDomElement>
@@ -550,6 +552,16 @@ QString QgsLayoutItemScaleBar::style() const
}
}

const QgsNumericFormat *QgsLayoutItemScaleBar::numericFormat() const
{
return mSettings.numericFormat();
}

void QgsLayoutItemScaleBar::setNumericFormat( QgsNumericFormat *format )
{
mSettings.setNumericFormat( format );
}

QFont QgsLayoutItemScaleBar::font() const
{
return mSettings.textFormat().font();
@@ -600,6 +612,10 @@ bool QgsLayoutItemScaleBar::writePropertiesToElement( QDomElement &composerScale
composerScaleBarElem.setAttribute( QStringLiteral( "lineJoinStyle" ), QgsSymbolLayerUtils::encodePenJoinStyle( mSettings.lineJoinStyle() ) );
composerScaleBarElem.setAttribute( QStringLiteral( "lineCapStyle" ), QgsSymbolLayerUtils::encodePenCapStyle( mSettings.lineCapStyle() ) );

QDomElement numericFormatElem = doc.createElement( QStringLiteral( "numericFormat" ) );
mSettings.numericFormat()->writeXml( numericFormatElem, doc, rwContext );
composerScaleBarElem.appendChild( numericFormatElem );

//style
if ( mStyle )
{
@@ -692,6 +708,13 @@ bool QgsLayoutItemScaleBar::readPropertiesFromElement( const QDomElement &itemEl
}
}

QDomNodeList numericFormatNodeList = itemElem.elementsByTagName( QStringLiteral( "numericFormat" ) );
if ( !numericFormatNodeList.isEmpty() )
{
QDomElement numericFormatElem = numericFormatNodeList.at( 0 ).toElement();
mSettings.setNumericFormat( QgsApplication::numericFormatRegistry()->createFromXml( numericFormatElem, context ) );
}

//colors
//fill color
QDomNodeList fillColorList = itemElem.elementsByTagName( QStringLiteral( "fillColor" ) );
@@ -459,6 +459,24 @@ class CORE_EXPORT QgsLayoutItemScaleBar: public QgsLayoutItem
*/
QString style() const;

/**
* Returns the numeric format used for numbers in the scalebar.
*
* \see setNumericFormat()
* \since QGIS 3.12
*/
const QgsNumericFormat *numericFormat() const;

/**
* Sets the numeric \a format used for numbers in the scalebar.
*
* Ownership of \a format is transferred to the scalebar.
*
* \see numericFormat()
* \since QGIS 3.12
*/
void setNumericFormat( QgsNumericFormat *format SIP_TRANSFER );

/**
* Adjusts the scale bar box size and updates the item.
*/
@@ -17,6 +17,7 @@
#include "qgsnumericscalebarrenderer.h"
#include "qgsscalebarsettings.h"
#include "qgslayoututils.h"
#include "qgsnumericformat.h"
#include <QList>
#include <QPainter>

@@ -52,7 +53,7 @@ void QgsNumericScaleBarRenderer::draw( QgsRenderContext &context, const QgsScale
//text destination is item's rect, excluding the margin
QRectF painterRect( margin, margin, context.convertToPainterUnits( scaleContext.size.width(), QgsUnitTypes::RenderMillimeters ) - 2 * margin,
context.convertToPainterUnits( scaleContext.size.height(), QgsUnitTypes::RenderMillimeters ) - 2 * margin );
QgsTextRenderer::drawText( painterRect, 0, hAlign, QStringList() << scaleText( scaleContext.scale ), context, settings.textFormat() );
QgsTextRenderer::drawText( painterRect, 0, hAlign, QStringList() << scaleText( settings, scaleContext.scale ), context, settings.textFormat() );

painter->restore();
}
@@ -62,14 +63,14 @@ QSizeF QgsNumericScaleBarRenderer::calculateBoxSize( const QgsScaleBarSettings &
{
QFont font = settings.textFormat().toQFont();

double textWidth = QgsLayoutUtils::textWidthMM( font, scaleText( scaleContext.scale ) );
double textWidth = QgsLayoutUtils::textWidthMM( font, scaleText( settings, scaleContext.scale ) );
double textHeight = QgsLayoutUtils::fontAscentMM( font );

return QSizeF( 2 * settings.boxContentSpace() + 2 * settings.pen().width() + textWidth,
textHeight + 2 * settings.boxContentSpace() );
}

QString QgsNumericScaleBarRenderer::scaleText( double scale ) const
QString QgsNumericScaleBarRenderer::scaleText( const QgsScaleBarSettings &settings, double scale ) const
{
return "1:" + QStringLiteral( "%L1" ).arg( scale, 0, 'f', 0 );
return "1:" + settings.numericFormat()->formatDouble( scale, QgsNumericFormatContext() );
}
@@ -48,7 +48,7 @@ class CORE_EXPORT QgsNumericScaleBarRenderer: public QgsScaleBarRenderer
private:

//! Returns the text for the scale bar or an empty string in case of error
QString scaleText( double scale ) const;
QString scaleText( const QgsScaleBarSettings &settings, double scale ) const;

};

@@ -19,6 +19,7 @@
#include "qgslayoututils.h"
#include "qgstextrenderer.h"
#include "qgsexpressioncontextutils.h"
#include "qgsnumericformat.h"
#include <QFontMetricsF>
#include <QPainter>

@@ -65,6 +66,8 @@ void QgsScaleBarRenderer::drawDefaultLabels( QgsRenderContext &context, const Qg
break;
}

QgsNumericFormatContext numericContext;

for ( int i = 0; i < positions.size(); ++i )
{
if ( segmentCounter == 0 && nSegmentsLeft > 0 )
@@ -79,7 +82,7 @@ void QgsScaleBarRenderer::drawDefaultLabels( QgsRenderContext &context, const Qg

if ( segmentCounter >= nSegmentsLeft )
{
currentNumericLabel = QString::number( currentLabelNumber / settings.mapUnitsPerScaleBarUnit() );
currentNumericLabel = settings.numericFormat()->formatDouble( currentLabelNumber / settings.mapUnitsPerScaleBarUnit(), numericContext );
}

//don't draw label for intermediate left segments or the zero label when it needs to be skipped
@@ -120,7 +123,7 @@ void QgsScaleBarRenderer::drawDefaultLabels( QgsRenderContext &context, const Qg
// note: this label is NOT centered over the end of the bar - rather the numeric portion
// of it is, without considering the unit label suffix. That's drawn at the end after
// horizontally centering just the numeric portion.
currentNumericLabel = QString::number( currentLabelNumber / settings.mapUnitsPerScaleBarUnit() );
currentNumericLabel = settings.numericFormat()->formatDouble( currentLabelNumber / settings.mapUnitsPerScaleBarUnit(), numericContext );
scaleScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "scale_value" ), currentNumericLabel, true, false ) );
QPointF pos;
pos.setY( fontMetrics.ascent() + scaledBoxContentSpace + ( settings.labelVerticalPlacement() == QgsScaleBarSettings::LabelBelowSegment ? scaledHeight + scaledLabelBarSpace : 0 ) );
@@ -165,7 +168,7 @@ QSizeF QgsScaleBarRenderer::calculateBoxSize( const QgsScaleBarSettings &setting

//consider last number and label
double largestLabelNumber = settings.numberOfSegments() * settings.unitsPerSegment() / settings.mapUnitsPerScaleBarUnit();
QString largestNumberLabel = QString::number( largestLabelNumber );
QString largestNumberLabel = settings.numericFormat()->formatDouble( largestLabelNumber, QgsNumericFormatContext() );
QString largestLabel = largestNumberLabel + ' ' + settings.unitLabel();
double largestLabelWidth;
if ( settings.labelHorizontalPlacement() == QgsScaleBarSettings::LabelCenteredSegment )
@@ -197,11 +200,11 @@ QString QgsScaleBarRenderer::firstLabelString( const QgsScaleBarSettings &settin
{
if ( settings.numberOfSegmentsLeft() > 0 )
{
return QString::number( settings.unitsPerSegment() / settings.mapUnitsPerScaleBarUnit() );
return settings.numericFormat()->formatDouble( settings.unitsPerSegment() / settings.mapUnitsPerScaleBarUnit(), QgsNumericFormatContext() );
}
else
{
return QStringLiteral( "0" );
return settings.numericFormat()->formatDouble( 0, QgsNumericFormatContext() );
}
}

0 comments on commit 16c2254

Please sign in to comment.
You can’t perform that action at this time.