Skip to content
Permalink
Browse files
Merge pull request #43043 from nirvn/labeling_shields_imp
[feature][labeling] Use fill symbols to render rectangle,square,circle,ellipse background shields
  • Loading branch information
nirvn committed May 4, 2021
2 parents 46287b8 + 65ba359 commit c27e22f5fe8671b3dcc4cfd0468bfeedf63e109a
Showing with 361 additions and 102 deletions.
  1. +74 −0 python/core/auto_generated/textrenderer/qgstextbackgroundsettings.sip.in
  2. +133 −5 src/core/textrenderer/qgstextbackgroundsettings.cpp
  3. +46 −0 src/core/textrenderer/qgstextbackgroundsettings.h
  4. +1 −0 src/core/textrenderer/qgstextformat.cpp
  5. +33 −28 src/core/textrenderer/qgstextrenderer.cpp
  6. +2 −0 src/core/textrenderer/qgstextrenderer_p.h
  7. +2 −1 src/gui/labeling/qgslabelinggui.cpp
  8. +37 −42 src/gui/qgstextformatwidget.cpp
  9. +0 −1 src/gui/qgstextformatwidget.h
  10. +13 −24 src/ui/qgstextformatwidgetbase.ui
  11. +0 −1 tests/src/python/test_qgstextformatwidget.py
  12. +20 −0 tests/src/python/test_qgstextrenderer.py
  13. BIN ...ta/control_images/expected_pal_canvas/sp_background_rect_w_offset/sp_background_rect_w_offset.png
  14. BIN ...ntrol_images/expected_pal_canvas/sp_background_rect_w_offset/sp_background_rect_w_offset_mask.png
  15. BIN ..._images/expected_pal_composer/sp_img_background_rect_w_offset/sp_img_background_rect_w_offset.png
  16. BIN ..._images/expected_pal_composer/sp_pdf_background_rect_w_offset/sp_pdf_background_rect_w_offset.png
  17. BIN ...es/expected_pal_composer/sp_pdf_background_rect_w_offset/sp_pdf_background_rect_w_offset_mask.png
  18. BIN ..._images/expected_pal_composer/sp_svg_background_rect_w_offset/sp_svg_background_rect_w_offset.png
  19. BIN ...es/expected_pal_composer/sp_svg_background_rect_w_offset/sp_svg_background_rect_w_offset_mask.png
  20. BIN tests/testdata/control_images/text_renderer/background_ellipse_pixels/background_ellipse_pixels.png
  21. BIN ...estdata/control_images/text_renderer/background_ellipse_pixels/background_ellipse_pixels_mask.png
  22. BIN tests/testdata/control_images/text_renderer/background_fillsymbol/background_fillsymbol.png
  23. BIN tests/testdata/control_images/text_renderer/background_offset_mm/background_offset_mm.png
  24. BIN tests/testdata/control_images/text_renderer/background_offset_mm/background_offset_mm_mask.png
  25. BIN tests/testdata/control_images/text_renderer/background_opacity/background_opacity.png
  26. BIN tests/testdata/control_images/text_renderer/background_opacity/background_opacity_mask.png
  27. BIN ...ol_images/text_renderer/background_rect_multiline_mapunits/background_rect_multiline_mapunits.png
  28. BIN ...ages/text_renderer/background_rect_multiline_mapunits/background_rect_multiline_mapunits_mask.png
@@ -141,6 +141,38 @@ to the background settings.
.. seealso:: :py:func:`markerSymbol`

.. versionadded:: 3.10
%End

QgsFillSymbol *fillSymbol() const;
%Docstring
Returns the fill symbol to be rendered in the background. Ownership remains with
the background settings.

.. note::

This is only used when the :py:func:`~QgsTextBackgroundSettings.type` is QgsTextBackgroundSettings.ShapeRectangle,
QgsTextBackgroundSettings.ShapeSquare, QgsTextBackgroundSettings.ShapeCircle or
QgsTextBackgroundSettings.ShapeEllipse

.. seealso:: :py:func:`setFillSymbol`

.. versionadded:: 3.20
%End

void setFillSymbol( QgsFillSymbol *symbol /Transfer/ );
%Docstring
Sets the current fill ``symbol`` for the background shape. Ownership is transferred
to the background settings.

.. note::

This is only used when the :py:func:`~QgsTextBackgroundSettings.type` is QgsTextBackgroundSettings.ShapeRectangle,
QgsTextBackgroundSettings.ShapeSquare, QgsTextBackgroundSettings.ShapeCircle or
QgsTextBackgroundSettings.ShapeEllipse

.. seealso:: :py:func:`fillSymbol`

.. versionadded:: 3.20
%End

SizeType sizeType() const;
@@ -447,6 +479,11 @@ Returns the color used for filing the background shape.
.. seealso:: :py:func:`setFillColor`

.. seealso:: :py:func:`strokeColor`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

void setFillColor( const QColor &color );
@@ -458,6 +495,11 @@ Sets the color used for filing the background shape.
.. seealso:: :py:func:`fillColor`

.. seealso:: :py:func:`setStrokeColor`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

QColor strokeColor() const;
@@ -467,6 +509,11 @@ Returns the color used for outlining the background shape.
.. seealso:: :py:func:`setStrokeColor`

.. seealso:: :py:func:`fillColor`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

void setStrokeColor( const QColor &color );
@@ -498,6 +545,11 @@ Sets the width of the shape's stroke (stroke). Units are specified through
.. seealso:: :py:func:`strokeWidth`

.. seealso:: :py:func:`setStrokeWidthUnit`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

QgsUnitTypes::RenderUnit strokeWidthUnit() const;
@@ -518,6 +570,11 @@ Sets the units used for the shape's stroke width.
.. seealso:: :py:func:`strokeWidthUnit`

.. seealso:: :py:func:`setStrokeWidth`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

QgsMapUnitScale strokeWidthMapUnitScale() const;
@@ -540,6 +597,11 @@ Sets the map unit scale object for the shape stroke width. This is only used if
.. seealso:: :py:func:`strokeWidthMapUnitScale`

.. seealso:: :py:func:`setStrokeWidthUnit`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

Qt::PenJoinStyle joinStyle() const;
@@ -556,6 +618,11 @@ Sets the join style used for drawing the background shape.
:param style: join style

.. seealso:: :py:func:`joinStyle`

.. note::

As of QGIS 3.20, using this function is only recommended for SVG backgrounds, while
other background types should be configured through their symbols.
%End

const QgsPaintEffect *paintEffect() const;
@@ -601,9 +668,16 @@ Write settings into a DOM element.
%Docstring
Updates the format by evaluating current values of data defined properties.

.. note::

Since QGIS 3.20, data defined fill color, stroke color, stroke width, and
pen join style will only modify SVG backgrounds. For other background types, the
data defined properties within symbols are to be used.

.. versionadded:: 3.10
%End


QSet<QString> referencedFields( const QgsRenderContext &context ) const;
%Docstring
Returns all field names referenced by the configuration (e.g. from data defined properties).
@@ -18,13 +18,26 @@
#include "qgsvectorlayer.h"
#include "qgspallabeling.h"
#include "qgssymbollayerutils.h"
#include "qgsfillsymbollayer.h"
#include "qgspainting.h"
#include "qgstextrendererutils.h"
#include "qgspainteffectregistry.h"

QgsTextBackgroundSettings::QgsTextBackgroundSettings()
{
d = new QgsTextBackgroundSettingsPrivate();

// Create a default fill symbol to preserve API promise until QGIS 4.0
QgsSimpleFillSymbolLayer *fill = new QgsSimpleFillSymbolLayer( d->fillColor, Qt::SolidPattern, d->strokeColor );
fill->setStrokeWidth( d->strokeWidth );
fill->setStrokeWidthUnit( d->strokeWidthUnits );
fill->setStrokeWidthMapUnitScale( d->strokeWidthMapUnitScale );
fill->setStrokeStyle( !qgsDoubleNear( d->strokeWidth, 0.0 ) ? Qt::SolidLine : Qt::NoPen );
fill->setPenJoinStyle( d->joinStyle );

QgsFillSymbol *fillSymbol = new QgsFillSymbol();
fillSymbol->changeSymbolLayer( 0, fill );
setFillSymbol( fillSymbol );
}

QgsTextBackgroundSettings::QgsTextBackgroundSettings( const QgsTextBackgroundSettings &other ) //NOLINT
@@ -79,6 +92,10 @@ bool QgsTextBackgroundSettings::operator==( const QgsTextBackgroundSettings &oth
|| ( d->markerSymbol && QgsSymbolLayerUtils::symbolProperties( d->markerSymbol.get() ) != QgsSymbolLayerUtils::symbolProperties( other.markerSymbol() ) ) )
return false;

if ( static_cast< bool >( d->fillSymbol ) != static_cast< bool >( other.fillSymbol() )
|| ( d->fillSymbol && QgsSymbolLayerUtils::symbolProperties( d->fillSymbol.get() ) != QgsSymbolLayerUtils::symbolProperties( other.fillSymbol() ) ) )
return false;

return true;
}

@@ -127,6 +144,16 @@ void QgsTextBackgroundSettings::setMarkerSymbol( QgsMarkerSymbol *symbol )
d->markerSymbol.reset( symbol );
}

QgsFillSymbol *QgsTextBackgroundSettings::fillSymbol() const
{
return d->fillSymbol.get();
}

void QgsTextBackgroundSettings::setFillSymbol( QgsFillSymbol *symbol )
{
d->fillSymbol.reset( symbol );
}

QgsTextBackgroundSettings::SizeType QgsTextBackgroundSettings::sizeType() const
{
return d->sizeType;
@@ -275,6 +302,10 @@ QColor QgsTextBackgroundSettings::fillColor() const
void QgsTextBackgroundSettings::setFillColor( const QColor &color )
{
d->fillColor = color;
if ( d->fillSymbol && d->fillSymbol->symbolLayers().at( 0 )->layerType() == QStringLiteral( "SimpleFill" ) )
{
qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) )->setColor( color );
}
}

QColor QgsTextBackgroundSettings::strokeColor() const
@@ -285,6 +316,10 @@ QColor QgsTextBackgroundSettings::strokeColor() const
void QgsTextBackgroundSettings::setStrokeColor( const QColor &color )
{
d->strokeColor = color;
if ( d->fillSymbol && d->fillSymbol->symbolLayers().at( 0 )->layerType() == QStringLiteral( "SimpleFill" ) )
{
qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) )->setStrokeColor( color );
}
}

double QgsTextBackgroundSettings::strokeWidth() const
@@ -295,6 +330,12 @@ double QgsTextBackgroundSettings::strokeWidth() const
void QgsTextBackgroundSettings::setStrokeWidth( double width )
{
d->strokeWidth = width;
if ( d->fillSymbol && d->fillSymbol->symbolLayers().at( 0 )->layerType() == QStringLiteral( "SimpleFill" ) )
{
QgsSimpleFillSymbolLayer *fill = qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) );
fill->setStrokeWidth( width );
fill->setStrokeStyle( !qgsDoubleNear( width, 0.0 ) ? Qt::SolidLine : Qt::NoPen );
}
}

QgsUnitTypes::RenderUnit QgsTextBackgroundSettings::strokeWidthUnit() const
@@ -305,6 +346,10 @@ QgsUnitTypes::RenderUnit QgsTextBackgroundSettings::strokeWidthUnit() const
void QgsTextBackgroundSettings::setStrokeWidthUnit( QgsUnitTypes::RenderUnit units )
{
d->strokeWidthUnits = units;
if ( d->fillSymbol && d->fillSymbol->symbolLayers().at( 0 )->layerType() == QStringLiteral( "SimpleFill" ) )
{
qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) )->setStrokeWidthUnit( units );
}
}

QgsMapUnitScale QgsTextBackgroundSettings::strokeWidthMapUnitScale() const
@@ -315,6 +360,10 @@ QgsMapUnitScale QgsTextBackgroundSettings::strokeWidthMapUnitScale() const
void QgsTextBackgroundSettings::setStrokeWidthMapUnitScale( const QgsMapUnitScale &scale )
{
d->strokeWidthMapUnitScale = scale;
if ( d->fillSymbol && d->fillSymbol->symbolLayers().at( 0 )->layerType() == QStringLiteral( "SimpleFill" ) )
{
qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) )->setStrokeWidthMapUnitScale( scale );
}
}

Qt::PenJoinStyle QgsTextBackgroundSettings::joinStyle() const
@@ -325,6 +374,10 @@ Qt::PenJoinStyle QgsTextBackgroundSettings::joinStyle() const
void QgsTextBackgroundSettings::setJoinStyle( Qt::PenJoinStyle style )
{
d->joinStyle = style;
if ( d->fillSymbol && d->fillSymbol->symbolLayers().at( 0 )->layerType() == QStringLiteral( "SimpleFill" ) )
{
qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) )->setPenJoinStyle( style );
}
}

const QgsPaintEffect *QgsTextBackgroundSettings::paintEffect() const
@@ -589,11 +642,39 @@ void QgsTextBackgroundSettings::readXml( const QDomElement &elem, const QgsReadW
else
setPaintEffect( nullptr );

const QDomElement symbolElem = backgroundElem.firstChildElement( QStringLiteral( "symbol" ) );
if ( !symbolElem.isNull() )
setMarkerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
else
setMarkerSymbol( nullptr );
setMarkerSymbol( nullptr );
setFillSymbol( nullptr );
const QDomNodeList symbols = backgroundElem.elementsByTagName( QStringLiteral( "symbol" ) );
for ( int i = 0; i < symbols.size(); ++i )
{
if ( symbols.at( i ).isElement() )
{
const QDomElement symbolElement = symbols.at( i ).toElement();
const QString symbolElementName = symbolElement.attribute( QStringLiteral( "name" ) );
if ( symbolElementName == QStringLiteral( "markerSymbol" ) )
{
setMarkerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElement, context ) );
}
else if ( symbolElementName == QStringLiteral( "fillSymbol" ) )
{
setFillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElement, context ) );
}
}
}

if ( !d->fillSymbol )
{
QgsSimpleFillSymbolLayer *fill = new QgsSimpleFillSymbolLayer( d->fillColor, Qt::SolidPattern, d->strokeColor );
fill->setStrokeWidth( d->strokeWidth );
fill->setStrokeWidthUnit( d->strokeWidthUnits );
fill->setStrokeWidthMapUnitScale( d->strokeWidthMapUnitScale );
fill->setStrokeStyle( !qgsDoubleNear( d->strokeWidth, 0.0 ) ? Qt::SolidLine : Qt::NoPen );
fill->setPenJoinStyle( d->joinStyle );

QgsFillSymbol *fillSymbol = new QgsFillSymbol();
fillSymbol->changeSymbolLayer( 0, fill );
setFillSymbol( fillSymbol );
}
}

QDomElement QgsTextBackgroundSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
@@ -631,9 +712,50 @@ QDomElement QgsTextBackgroundSettings::writeXml( QDomDocument &doc, const QgsRea
if ( d->markerSymbol )
backgroundElem.appendChild( QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "markerSymbol" ), d->markerSymbol.get(), doc, context ) );

if ( d->fillSymbol )
backgroundElem.appendChild( QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "fillSymbol" ), d->fillSymbol.get(), doc, context ) );

return backgroundElem;
}

void QgsTextBackgroundSettings::upgradeDataDefinedProperties( QgsPropertyCollection &properties )
{
if ( !d->fillSymbol || d->fillSymbol->symbolLayers().at( 0 )->layerType() != QStringLiteral( "SimpleFill" ) )
return;
QgsSimpleFillSymbolLayer *fill = qgis::down_cast< QgsSimpleFillSymbolLayer * >( d->fillSymbol->symbolLayers().at( 0 ) );

if ( d->type != QgsTextBackgroundSettings::ShapeSVG )
{
if ( properties.hasProperty( QgsPalLayerSettings::ShapeFillColor ) &&
!fill->dataDefinedProperties().hasProperty( QgsSymbolLayer::PropertyFillColor ) )
{
fill->dataDefinedProperties().setProperty( QgsSymbolLayer::PropertyFillColor, properties.property( QgsPalLayerSettings::ShapeFillColor ) );
properties.setProperty( QgsPalLayerSettings::ShapeFillColor, QgsProperty() );
}

if ( properties.hasProperty( QgsPalLayerSettings::ShapeStrokeColor ) &&
!fill->dataDefinedProperties().hasProperty( QgsSymbolLayer::PropertyStrokeColor ) )
{
fill->dataDefinedProperties().setProperty( QgsSymbolLayer::PropertyStrokeColor, properties.property( QgsPalLayerSettings::ShapeStrokeColor ) );
properties.setProperty( QgsPalLayerSettings::ShapeStrokeColor, QgsProperty() );
}

if ( properties.hasProperty( QgsPalLayerSettings::ShapeStrokeWidth ) &&
!fill->dataDefinedProperties().hasProperty( QgsSymbolLayer::PropertyStrokeWidth ) )
{
fill->dataDefinedProperties().setProperty( QgsSymbolLayer::PropertyStrokeWidth, properties.property( QgsPalLayerSettings::ShapeStrokeWidth ) );
properties.setProperty( QgsPalLayerSettings::ShapeStrokeWidth, QgsProperty() );
}

if ( properties.hasProperty( QgsPalLayerSettings::ShapeJoinStyle ) &&
!fill->dataDefinedProperties().hasProperty( QgsSymbolLayer::PropertyJoinStyle ) )
{
fill->dataDefinedProperties().setProperty( QgsSymbolLayer::PropertyJoinStyle, properties.property( QgsPalLayerSettings::ShapeJoinStyle ) );
properties.setProperty( QgsPalLayerSettings::ShapeJoinStyle, QgsProperty() );
}
}
}

void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &context, const QgsPropertyCollection &properties )
{
if ( properties.isActive( QgsPalLayerSettings::ShapeDraw ) )
@@ -763,6 +885,8 @@ void QgsTextBackgroundSettings::updateDataDefinedProperties( QgsRenderContext &c
d->opacity = properties.value( QgsPalLayerSettings::ShapeOpacity, context.expressionContext(), d->opacity * 100 ).toDouble() / 100.0;
}

// for non-SVG background types, those data defined properties will not having an impact,
// instead use data defined properties within symbols
if ( properties.isActive( QgsPalLayerSettings::ShapeFillColor ) )
{
context.expressionContext().setOriginalValueVariable( QgsSymbolLayerUtils::encodeColor( d->fillColor ) );
@@ -818,5 +942,9 @@ QSet<QString> QgsTextBackgroundSettings::referencedFields( const QgsRenderContex
{
fields.unite( d->markerSymbol->usedAttributes( context ) );
}
if ( d->fillSymbol )
{
fields.unite( d->fillSymbol->usedAttributes( context ) );
}
return fields;
}

0 comments on commit c27e22f

Please sign in to comment.