Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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 9, 2020
1 parent ad6684b commit 16c2254
Show file tree
Hide file tree
Showing 21 changed files with 417 additions and 94 deletions.
20 changes: 20 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitemscalebar.sip.in
Expand Up @@ -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();
Expand Down
30 changes: 30 additions & 0 deletions python/core/auto_generated/scalebar/qgsscalebarsettings.sip.in
Expand Up @@ -8,6 +8,8 @@





class QgsScaleBarSettings
{
%Docstring
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

};
Expand Down
2 changes: 2 additions & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 25 additions & 1 deletion src/app/layout/qgslayoutscalebarwidget.cpp
Expand Up @@ -20,6 +20,7 @@
#include "qgslayout.h"
#include "qgsguiutils.h"
#include "qgsvectorlayer.h"
#include "qgsnumericformatselectorwidget.h"

#include <QColorDialog>
#include <QFontDialog>
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 )
Expand Down
1 change: 1 addition & 0 deletions src/app/layout/qgslayoutscalebarwidget.h
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -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

Expand Down
23 changes: 23 additions & 0 deletions src/core/layout/qgslayoutitemscalebar.cpp
Expand Up @@ -33,6 +33,8 @@
#include "qgsunittypes.h"
#include "qgssettings.h"
#include "qgsstyleentityvisitor.h"
#include "qgsnumericformat.h"
#include "qgsnumericformatregistry.h"

#include <QDomDocument>
#include <QDomElement>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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" ) );
Expand Down
18 changes: 18 additions & 0 deletions src/core/layout/qgslayoutitemscalebar.h
Expand Up @@ -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.
*/
Expand Down
9 changes: 5 additions & 4 deletions src/core/scalebar/qgsnumericscalebarrenderer.cpp
Expand Up @@ -17,6 +17,7 @@
#include "qgsnumericscalebarrenderer.h"
#include "qgsscalebarsettings.h"
#include "qgslayoututils.h"
#include "qgsnumericformat.h"
#include <QList>
#include <QPainter>

Expand Down Expand Up @@ -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();
}
Expand All @@ -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() );
}
2 changes: 1 addition & 1 deletion src/core/scalebar/qgsnumericscalebarrenderer.h
Expand Up @@ -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;

};

Expand Down
13 changes: 8 additions & 5 deletions src/core/scalebar/qgsscalebarrenderer.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgslayoututils.h"
#include "qgstextrenderer.h"
#include "qgsexpressioncontextutils.h"
#include "qgsnumericformat.h"
#include <QFontMetricsF>
#include <QPainter>

Expand Down Expand Up @@ -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 )
Expand All @@ -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
Expand Down Expand Up @@ -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 ) );
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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() );
}
}

Expand Down

0 comments on commit 16c2254

Please sign in to comment.