Skip to content
Permalink
Browse files

QgsLayoutItemShape can provide clip paths

  • Loading branch information
nyalldawson committed Jul 28, 2020
1 parent aa0b36b commit 77badc009747dd9d3e9e77658d3da05737e54105
@@ -49,6 +49,8 @@ The caller takes responsibility for deleting the returned object.

virtual QString displayName() const;

virtual QgsLayoutItem::Flags itemFlags() const;


QgsLayoutItemShape::Shape shapeType() const;
%Docstring
@@ -93,6 +95,9 @@ Returns the corner radius for rounded rectangle corners.
.. seealso:: :py:func:`setCornerRadius`
%End

virtual QgsGeometry clipPath() const;


virtual QRectF boundingRect() const;


@@ -43,6 +43,7 @@ QgsLayoutItemShape::QgsLayoutItemShape( QgsLayout *layout )
{
updateBoundingRect();
update();
emit clipPathChanged();
} );
}

@@ -91,6 +92,13 @@ QString QgsLayoutItemShape::displayName() const
return tr( "<Shape>" );
}

QgsLayoutItem::Flags QgsLayoutItemShape::itemFlags() const
{
QgsLayoutItem::Flags flags = QgsLayoutItem::itemFlags();
flags |= QgsLayoutItem::FlagProvidesClipPath;
return flags;
}

void QgsLayoutItemShape::setShapeType( QgsLayoutItemShape::Shape type )
{
if ( type == mShape )
@@ -105,6 +113,8 @@ void QgsLayoutItemShape::setShapeType( QgsLayoutItemShape::Shape type )
//notify the model that the display name has changed
mLayout->itemsModel()->updateItemDisplayName( this );
}

emit clipPathChanged();
}

void QgsLayoutItemShape::refreshSymbol()
@@ -141,6 +151,21 @@ void QgsLayoutItemShape::setSymbol( QgsFillSymbol *symbol )
refreshSymbol();
}

void QgsLayoutItemShape::setCornerRadius( QgsLayoutMeasurement radius )
{
mCornerRadius = radius;
emit clipPathChanged();
}

QgsGeometry QgsLayoutItemShape::clipPath() const
{
QPolygonF shapePolygon = mapToScene( calculatePolygon( 1.0 ) );
// ensure polygon is closed
if ( shapePolygon.at( 0 ) != shapePolygon.constLast() )
shapePolygon << shapePolygon.at( 0 );
return QgsGeometry::fromQPolygonF( shapePolygon );
}

QRectF QgsLayoutItemShape::boundingRect() const
{
return mCurrentRectangle;
@@ -169,8 +194,17 @@ void QgsLayoutItemShape::draw( QgsLayoutItemRenderContext &context )
painter->setPen( Qt::NoPen );
painter->setBrush( Qt::NoBrush );

double scale = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
const double scale = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );

QVector<QPolygonF> rings; //empty list

symbol()->startRender( context.renderContext() );
symbol()->renderPolygon( calculatePolygon( scale ), &rings, nullptr, context.renderContext() );
symbol()->stopRender( context.renderContext() );
}

QPolygonF QgsLayoutItemShape::calculatePolygon( double scale ) const
{
QPolygonF shapePolygon;

//shapes with curves must be enlarged before conversion to QPolygonF, or
@@ -216,12 +250,7 @@ void QgsLayoutItemShape::draw( QgsLayoutItemRenderContext &context )
break;
}
}

QVector<QPolygonF> rings; //empty list

symbol()->startRender( context.renderContext() );
symbol()->renderPolygon( shapePolygon, &rings, nullptr, context.renderContext() );
symbol()->stopRender( context.renderContext() );
return shapePolygon;
}

bool QgsLayoutItemShape::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
@@ -62,6 +62,7 @@ class CORE_EXPORT QgsLayoutItemShape : public QgsLayoutItem

//Overridden to return shape type
QString displayName() const override;
QgsLayoutItem::Flags itemFlags() const override;

/**
* Returns the type of shape (e.g. rectangle, ellipse, etc).
@@ -92,14 +93,16 @@ class CORE_EXPORT QgsLayoutItemShape : public QgsLayoutItem
* Sets the corner \a radius for rounded rectangle corners.
* \see cornerRadius()
*/
void setCornerRadius( QgsLayoutMeasurement radius ) { mCornerRadius = radius; }
void setCornerRadius( QgsLayoutMeasurement radius );

/**
* Returns the corner radius for rounded rectangle corners.
* \see setCornerRadius()
*/
QgsLayoutMeasurement cornerRadius() const { return mCornerRadius; }

QgsGeometry clipPath() const override;

// Depending on the symbol style, the bounding rectangle can be larger than the shape
QRectF boundingRect() const override;

@@ -138,6 +141,8 @@ class CORE_EXPORT QgsLayoutItemShape : public QgsLayoutItem
QRectF mCurrentRectangle;

QgsLayoutMeasurement mCornerRadius;

QPolygonF calculatePolygon( double scale ) const;
};


@@ -13,7 +13,16 @@
import qgis # NOQA

from qgis.testing import start_app, unittest
from qgis.core import QgsLayoutItemShape
from qgis.core import (
QgsLayoutItemShape,
QgsProject,
QgsLayout,
QgsLayoutItem,
QgsLayoutMeasurement,
QgsUnitTypes
)
from qgis.PyQt.QtCore import QRectF
from qgis.PyQt.QtTest import QSignalSpy

from test_qgslayoutitem import LayoutItemTestCase

@@ -26,6 +35,35 @@ class TestQgsLayoutShape(unittest.TestCase, LayoutItemTestCase):
def setUpClass(cls):
cls.item_class = QgsLayoutItemShape

def testClipPath(self):
pr = QgsProject()
l = QgsLayout(pr)
shape = QgsLayoutItemShape(l)

shape.setShapeType(QgsLayoutItemShape.Rectangle)
shape.attemptSetSceneRect(QRectF(30, 10, 100, 200))

# must be a closed polygon, in scene coordinates!
self.assertEqual(shape.clipPath().asWkt(), 'Polygon ((30 10, 130 10, 130 210, 30 210, 30 10))')
self.assertTrue(int(shape.itemFlags() & QgsLayoutItem.FlagProvidesClipPath))

spy = QSignalSpy(shape.clipPathChanged)
shape.setCornerRadius(QgsLayoutMeasurement(10, QgsUnitTypes.LayoutMillimeters))
self.assertTrue(shape.clipPath().asWkt(0).startswith('Polygon ((30 20, 30 20, 30 19, 30 19, 30 19, 30 19'))
self.assertEqual(len(spy), 1)

shape.setShapeType(QgsLayoutItemShape.Ellipse)
self.assertEqual(len(spy), 2)
self.assertTrue(shape.clipPath().asWkt(0).startswith('Polygon ((130 110, 130 111, 130 113, 130 114'))

shape.setShapeType(QgsLayoutItemShape.Triangle)
self.assertEqual(len(spy), 3)
self.assertEqual(shape.clipPath().asWkt(), 'Polygon ((30 210, 130 210, 80 10, 30 210))')

shape.attemptSetSceneRect(QRectF(50, 20, 80, 120))
self.assertEqual(len(spy), 4)
self.assertEqual(shape.clipPath().asWkt(), 'Polygon ((50 140, 130 140, 90 20, 50 140))')


if __name__ == '__main__':
unittest.main()

0 comments on commit 77badc0

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