Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Move default legend patch shape handling to QgsStyle
  • Loading branch information
nyalldawson committed Apr 21, 2020
1 parent e5be0de commit ae8e5cf
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 51 deletions.
Expand Up @@ -116,11 +116,6 @@ The default behavior is to respect the geometry()'s aspect ratio.
Converts the patch shape to a set of QPolygonF objects representing
how the patch should be drawn for a symbol of the given ``type`` at the specified ``size`` (as
geometry parts and rings).
%End

static QList< QList< QPolygonF > > defaultPatch( QgsSymbol::SymbolType type, QSizeF size );
%Docstring
Returns the default patch geometry for the given symbol ``type`` and ``size`` as a set of QPolygonF objects (parts and rings).
%End

void readXml( const QDomElement &element, const QgsReadWriteContext &context );
Expand Down
18 changes: 18 additions & 0 deletions python/core/auto_generated/symbology/qgsstyle.sip.in
Expand Up @@ -552,6 +552,24 @@ Removes label settings from the style.
Changes a label setting's name.

.. versionadded:: 3.10
%End

QgsLegendPatchShape defaultPatch( QgsSymbol::SymbolType type, QSizeF size ) const;
%Docstring
Returns the default legend patch shape for the given symbol ``type``.

.. seealso:: :py:func:`defaultPatchAsQPolygonF`

.. versionadded:: 3.14
%End

QList< QList< QPolygonF > > defaultPatchAsQPolygonF( QgsSymbol::SymbolType type, QSizeF size ) const;
%Docstring
Returns the default patch geometry for the given symbol ``type`` and ``size`` as a set of QPolygonF objects (parts and rings).

.. seealso:: :py:func:`defaultPatch`

.. versionadded:: 3.14
%End

bool createDatabase( const QString &filename );
Expand Down
28 changes: 3 additions & 25 deletions src/core/layertree/qgslegendpatchshape.cpp
Expand Up @@ -19,6 +19,7 @@ email : nyall dot dawson at gmail dot com
#include "qgsmultilinestring.h"
#include "qgslinestring.h"
#include "qgspolygon.h"
#include "qgsstyle.h"

QgsLegendPatchShape::QgsLegendPatchShape( QgsSymbol::SymbolType type, const QgsGeometry &geometry, bool preserveAspectRatio )
: mSymbolType( type )
Expand Down Expand Up @@ -83,7 +84,7 @@ QPolygonF curveToPolygonF( const QgsCurve *curve )
QList<QList<QPolygonF> > QgsLegendPatchShape::toQPolygonF( QgsSymbol::SymbolType type, QSizeF size ) const
{
if ( isNull() || type != mSymbolType )
return defaultPatch( type, size );
return QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( type, size );

// scale and translate to desired size

Expand Down Expand Up @@ -128,7 +129,7 @@ QList<QList<QPolygonF> > QgsLegendPatchShape::toQPolygonF( QgsSymbol::SymbolType
}
else
{
points << QPointF( size.width() / 2, size.height() / 2 );
points << QPointF( static_cast< int >( size.width() ) / 2, static_cast< int >( size.height() ) / 2 );
}
return QList< QList<QPolygonF> >() << ( QList< QPolygonF >() << points );
}
Expand Down Expand Up @@ -176,29 +177,6 @@ QList<QList<QPolygonF> > QgsLegendPatchShape::toQPolygonF( QgsSymbol::SymbolType
return QList< QList<QPolygonF> >();
}

QList<QList<QPolygonF> > QgsLegendPatchShape::defaultPatch( QgsSymbol::SymbolType type, QSizeF size )
{
switch ( type )
{
case QgsSymbol::Marker:
return QList< QList< QPolygonF > >() << ( QList< QPolygonF >() << ( QPolygonF() << QPointF( static_cast< int >( size.width() ) / 2,
static_cast< int >( size.height() ) / 2 ) ) );

case QgsSymbol::Line:
// we're adding 0.5 to get rid of blurred preview:
// drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
return QList< QList<QPolygonF> >() << ( QList< QPolygonF >() << ( QPolygonF() << QPointF( 0, static_cast< int >( size.height() ) / 2 + 0.5 ) << QPointF( size.width(), static_cast< int >( size.height() ) / 2 + 0.5 ) ) );

case QgsSymbol::Fill:
return QList< QList<QPolygonF> >() << ( QList< QPolygonF> () << ( QRectF( QPointF( 0, 0 ), QPointF( static_cast< int >( size.width() ), static_cast< int >( size.height() ) ) ) ) );

case QgsSymbol::Hybrid:
return QList< QList<QPolygonF> >();
}

return QList< QList<QPolygonF> >();
}

void QgsLegendPatchShape::readXml( const QDomElement &element, const QgsReadWriteContext & )
{
mGeometry = QgsGeometry::fromWkt( element.attribute( QStringLiteral( "wkt" ) ) );
Expand Down
5 changes: 0 additions & 5 deletions src/core/layertree/qgslegendpatchshape.h
Expand Up @@ -126,11 +126,6 @@ class CORE_EXPORT QgsLegendPatchShape
*/
QList< QList< QPolygonF > > toQPolygonF( QgsSymbol::SymbolType type, QSizeF size ) const;

/**
* Returns the default patch geometry for the given symbol \a type and \a size as a set of QPolygonF objects (parts and rings).
*/
static QList< QList< QPolygonF > > defaultPatch( QgsSymbol::SymbolType type, QSizeF size );

/**
* Read settings from a DOM \a element.
* \see writeXml()
Expand Down
58 changes: 58 additions & 0 deletions src/core/symbology/qgsstyle.cpp
Expand Up @@ -22,6 +22,9 @@
#include "qgslogger.h"
#include "qgsreadwritecontext.h"
#include "qgssettings.h"
#include "qgslegendpatchshape.h"
#include "qgslinestring.h"
#include "qgspolygon.h"

#include <QDomDocument>
#include <QDomElement>
Expand Down Expand Up @@ -921,6 +924,61 @@ bool QgsStyle::renameLabelSettings( const QString &oldName, const QString &newNa
return result;
}

QgsLegendPatchShape QgsStyle::defaultPatch( QgsSymbol::SymbolType type, QSizeF size ) const
{
if ( type == QgsSymbol::Hybrid )
return QgsLegendPatchShape();

if ( mDefaultPatchCache[ type ].contains( size ) )
return mDefaultPatchCache[ type ].value( size );

QgsGeometry geom;
switch ( type )
{
case QgsSymbol::Marker:
geom = QgsGeometry( qgis::make_unique< QgsPoint >( static_cast< int >( size.width() ) / 2, static_cast< int >( size.height() ) / 2 ) );
break;

case QgsSymbol::Line:
{
// we're adding 0.5 to get rid of blurred preview:
// drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
double y = static_cast< int >( size.height() ) / 2 + 0.5;
geom = QgsGeometry( qgis::make_unique< QgsLineString >( ( QVector< double >() << 0 << size.width() ),
( QVector< double >() << y << y ) ) );
break;
}

case QgsSymbol::Fill:
{
geom = QgsGeometry( qgis::make_unique< QgsPolygon >(
new QgsLineString( QVector< double >() << 0 << static_cast< int >( size.width() ) << static_cast< int >( size.width() ) << 0 << 0,
QVector< double >() << static_cast< int >( size.height() ) << static_cast< int >( size.height() ) << 0 << 0 << static_cast< int >( size.height() ) ) ) );
break;
}

case QgsSymbol::Hybrid:
break;
}

QgsLegendPatchShape res = QgsLegendPatchShape( type, geom, true );
mDefaultPatchCache[ type ][size ] = res;
return res;
}

QList<QList<QPolygonF> > QgsStyle::defaultPatchAsQPolygonF( QgsSymbol::SymbolType type, QSizeF size ) const
{
if ( type == QgsSymbol::Hybrid )
return QList<QList<QPolygonF> >();

if ( mDefaultPatchQPolygonFCache[ type ].contains( size ) )
return mDefaultPatchQPolygonFCache[ type ].value( size );

QList<QList<QPolygonF> > res = defaultPatch( type, size ).toQPolygonF( type, size );
mDefaultPatchQPolygonFCache[ type ][size ] = res;
return res;
}

QStringList QgsStyle::symbolsOfFavorite( StyleEntity type ) const
{
if ( !mCurrentDB )
Expand Down
20 changes: 20 additions & 0 deletions src/core/symbology/qgsstyle.h
Expand Up @@ -28,6 +28,7 @@
#include "qgssymbollayerutils.h" // QgsStringMap
#include "qgstextrenderer.h"
#include "qgspallabeling.h"
#include "layertree/qgslegendpatchshape.h"

class QgsSymbol;
class QgsSymbolLayer;
Expand Down Expand Up @@ -584,6 +585,22 @@ class CORE_EXPORT QgsStyle : public QObject
*/
bool renameLabelSettings( const QString &oldName, const QString &newName );

/**
* Returns the default legend patch shape for the given symbol \a type.
*
* \see defaultPatchAsQPolygonF()
* \since QGIS 3.14
*/
QgsLegendPatchShape defaultPatch( QgsSymbol::SymbolType type, QSizeF size ) const;

/**
* Returns the default patch geometry for the given symbol \a type and \a size as a set of QPolygonF objects (parts and rings).
*
* \see defaultPatch()
* \since QGIS 3.14
*/
QList< QList< QPolygonF > > defaultPatchAsQPolygonF( QgsSymbol::SymbolType type, QSizeF size ) const;

/**
* Creates an on-disk database
*
Expand Down Expand Up @@ -911,6 +928,9 @@ class CORE_EXPORT QgsStyle : public QObject

sqlite3_database_unique_ptr mCurrentDB;

mutable QHash< QgsSymbol::SymbolType, QHash< QSizeF, QgsLegendPatchShape > > mDefaultPatchCache;
mutable QHash< QgsSymbol::SymbolType, QHash< QSizeF, QList< QList< QPolygonF > > > > mDefaultPatchQPolygonFCache;

static QgsStyle *sDefaultStyle;

//! Convenience function to open the DB and return a sqlite3 object
Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology/qgssymbol.cpp
Expand Up @@ -549,7 +549,7 @@ void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext
const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );

const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( QgsSymbol::Fill, targetSize )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Fill, targetSize );
: QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( QgsSymbol::Fill, targetSize );

lsl->startRender( symbolContext );
QgsPaintEffect *effect = lsl->paintEffect();
Expand Down
7 changes: 4 additions & 3 deletions src/core/symbology/qgssymbollayer.cpp
Expand Up @@ -29,6 +29,7 @@
#include "qgsapplication.h"
#include "qgsmultipoint.h"
#include "qgslegendpatchshape.h"
#include "qgsstyle.h"

#include <QSize>
#include <QPainter>
Expand Down Expand Up @@ -453,7 +454,7 @@ void QgsMarkerSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSi
QgsPaintEffect *effect = paintEffect();

QPolygonF points = context.patchShape() ? context.patchShape()->toQPolygonF( QgsSymbol::Marker, size ).value( 0 ).value( 0 )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Marker, size ).value( 0 ).value( 0 );
: QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( QgsSymbol::Marker, size ).value( 0 ).value( 0 );

std::unique_ptr< QgsEffectPainter > effectPainter;
if ( effect && effect->enabled() )
Expand Down Expand Up @@ -640,7 +641,7 @@ QgsMapUnitScale QgsLineSymbolLayer::mapUnitScale() const
void QgsLineSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
{
const QList< QList< QPolygonF > > points = context.patchShape() ? context.patchShape()->toQPolygonF( QgsSymbol::Line, size )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Line, size );
: QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( QgsSymbol::Line, size );
startRender( context );
QgsPaintEffect *effect = paintEffect();

Expand Down Expand Up @@ -700,7 +701,7 @@ double QgsLineSymbolLayer::dxfWidth( const QgsDxfExport &e, QgsSymbolRenderConte
void QgsFillSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
{
const QList< QList< QPolygonF > > polys = context.patchShape() ? context.patchShape()->toQPolygonF( QgsSymbol::Fill, size )
: QgsLegendPatchShape::defaultPatch( QgsSymbol::Fill, size );
: QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( QgsSymbol::Fill, size );

startRender( context );
QgsPaintEffect *effect = paintEffect();
Expand Down
25 changes: 13 additions & 12 deletions tests/src/python/test_qgslegendpatchshape.py
Expand Up @@ -28,7 +28,8 @@
QgsMarkerSymbol,
QgsRenderChecker,
QgsReadWriteContext,
QgsRenderContext
QgsRenderContext,
QgsStyle
)
from qgis.PyQt.QtXml import QDomDocument, QDomElement

Expand Down Expand Up @@ -83,28 +84,28 @@ def testNull(self):
self.assertTrue(shape.isNull())

def testDefault(self):
self.assertEqual(QgsLegendPatchShape.defaultPatch(QgsSymbol.Hybrid, QSizeF(1, 1)), [])
self.assertEqual(QgsLegendPatchShape.defaultPatch(QgsSymbol.Hybrid, QSizeF(10, 10)), [])
self.assertEqual(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Hybrid, QSizeF(1, 1)), [])
self.assertEqual(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Hybrid, QSizeF(10, 10)), [])

# markers
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Marker, QSizeF(1, 1))), [[[[0.0, 0.0]]]])
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Marker, QSizeF(2, 2))),
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Marker, QSizeF(1, 1))), [[[[0.0, 0.0]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Marker, QSizeF(2, 2))),
[[[[1.0, 1.0]]]])
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Marker, QSizeF(10, 2))), [[[[5.0, 1.0]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Marker, QSizeF(10, 2))), [[[[5.0, 1.0]]]])

# lines
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Line, QSizeF(1, 1))), [[[[0.0, 0.5], [1.0, 0.5]]]])
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Line, QSizeF(10, 2))), [[[[0.0, 1.5], [10.0, 1.5]]]])
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Line, QSizeF(9, 3))), [[[[0.0, 1.5], [9.0, 1.5]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Line, QSizeF(1, 1))), [[[[0.0, 0.5], [1.0, 0.5]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Line, QSizeF(10, 2))), [[[[0.0, 1.5], [10.0, 1.5]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Line, QSizeF(9, 3))), [[[[0.0, 1.5], [9.0, 1.5]]]])

# fills
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Fill, QSizeF(1, 1))), [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]]])
self.assertEqual(self.polys_to_list(QgsLegendPatchShape.defaultPatch(QgsSymbol.Fill, QSizeF(10, 2))), [[[[0.0, 0.0], [10.0, 0.0], [10.0, 2.0], [0.0, 2.0], [0.0, 0.0]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Fill, QSizeF(1, 1))), [[[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0]]]])
self.assertEqual(self.polys_to_list(QgsStyle.defaultStyle().defaultPatchAsQPolygonF(QgsSymbol.Fill, QSizeF(10, 2))), [[[[0.0, 0.0], [10.0, 0.0], [10.0, 2.0], [0.0, 2.0], [0.0, 0.0]]]])

def testMarkers(self):
# shouldn't matter what a point geometry is, it will always be rendered in center of symbol patch
shape = QgsLegendPatchShape(QgsSymbol.Marker, QgsGeometry.fromWkt('Point( 5 5 )'), False)
self.assertEqual(self.polys_to_list(shape.toQPolygonF(QgsSymbol.Marker, QSizeF(1, 1))), [[[[0.5, 0.5]]]])
self.assertEqual(self.polys_to_list(shape.toQPolygonF(QgsSymbol.Marker, QSizeF(1, 1))), [[[[0, 0]]]])
self.assertEqual(self.polys_to_list(shape.toQPolygonF(QgsSymbol.Marker, QSizeF(10, 2))), [[[[5.0, 1.0]]]])

# requesting different symbol type, should return default
Expand Down

0 comments on commit ae8e5cf

Please sign in to comment.