Move default legend patch shape handling to QgsStyle

This commit is contained in:
Nyall Dawson 2020-04-18 12:55:21 +10:00
parent e5be0de36a
commit ae8e5cfeaa
9 changed files with 117 additions and 51 deletions

View File

@ -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 );

View File

@ -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 );

View File

@ -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 )
@ -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
@ -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 );
}
@ -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" ) ) );

View File

@ -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()

View File

@ -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>
@ -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 )

View File

@ -28,6 +28,7 @@
#include "qgssymbollayerutils.h" // QgsStringMap
#include "qgstextrenderer.h"
#include "qgspallabeling.h"
#include "layertree/qgslegendpatchshape.h"
class QgsSymbol;
class QgsSymbolLayer;
@ -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
*
@ -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

View File

@ -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();

View File

@ -29,6 +29,7 @@
#include "qgsapplication.h"
#include "qgsmultipoint.h"
#include "qgslegendpatchshape.h"
#include "qgsstyle.h"
#include <QSize>
#include <QPainter>
@ -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() )
@ -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();
@ -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();

View File

@ -28,7 +28,8 @@ from qgis.core import (QgsLegendPatchShape,
QgsMarkerSymbol,
QgsRenderChecker,
QgsReadWriteContext,
QgsRenderContext
QgsRenderContext,
QgsStyle
)
from qgis.PyQt.QtXml import QDomDocument, QDomElement
@ -83,28 +84,28 @@ class TestQgsLegendPatchShape(unittest.TestCase):
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