QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgslayoutitemshape.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemshape.cpp
3 -----------------------
4 begin : July 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgslayoutitemshape.h"
18#include "qgslayout.h"
19#include "qgslayoututils.h"
20#include "qgssymbollayerutils.h"
21#include "qgslayoutmodel.h"
23#include "qgsfillsymbol.h"
24
25#include <QPainter>
26
28 : QgsLayoutItem( layout )
29 , mCornerRadius( 0 )
30{
31 setBackgroundEnabled( false );
32 setFrameEnabled( false );
33 QVariantMap properties;
34 properties.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
35 properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
36 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
37 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
38 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
39 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
40 mShapeStyleSymbol.reset( QgsFillSymbol::createSimple( properties ) );
41 refreshSymbol( false );
42
43 connect( this, &QgsLayoutItemShape::sizePositionChanged, this, [ = ]
44 {
45 updateBoundingRect();
46 update();
47 emit clipPathChanged();
48 } );
49}
50
52
54{
55 return new QgsLayoutItemShape( layout );
56}
57
59{
61}
62
64{
65 switch ( mShape )
66 {
67 case Ellipse:
68 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeEllipse.svg" ) );
69 case Rectangle:
70 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeRectangle.svg" ) );
71 case Triangle:
72 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeTriangle.svg" ) );
73 }
74
75 return QIcon();
76}
77
79{
80 if ( !id().isEmpty() )
81 {
82 return id();
83 }
84
85 switch ( mShape )
86 {
87 case Ellipse:
88 return tr( "<Ellipse>" );
89 case Rectangle:
90 return tr( "<Rectangle>" );
91 case Triangle:
92 return tr( "<Triangle>" );
93 }
94
95 return tr( "<Shape>" );
96}
97
98QgsLayoutItem::Flags QgsLayoutItemShape::itemFlags() const
99{
100 QgsLayoutItem::Flags flags = QgsLayoutItem::itemFlags();
102 return flags;
103}
104
106{
107 if ( type == mShape )
108 {
109 return;
110 }
111
112 mShape = type;
113
114 if ( mLayout && id().isEmpty() )
115 {
116 //notify the model that the display name has changed
117 mLayout->itemsModel()->updateItemDisplayName( this );
118 }
119
120 emit clipPathChanged();
121}
122
123void QgsLayoutItemShape::refreshSymbol( bool redraw )
124{
125 if ( auto *lLayout = layout() )
126 {
127 const QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( lLayout, nullptr, lLayout->renderContext().dpi() );
128 mMaxSymbolBleed = ( 25.4 / lLayout->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol.get(), rc );
129 }
130
131 updateBoundingRect();
132
133 if ( redraw )
134 update();
135
136 emit frameChanged();
137}
138
139void QgsLayoutItemShape::updateBoundingRect()
140{
141 QRectF rectangle = rect();
142 rectangle.adjust( -mMaxSymbolBleed, -mMaxSymbolBleed, mMaxSymbolBleed, mMaxSymbolBleed );
143 if ( rectangle != mCurrentRectangle )
144 {
145 prepareGeometryChange();
146 mCurrentRectangle = rectangle;
147 }
148}
149
151{
152 if ( !symbol )
153 return;
154
155 mShapeStyleSymbol.reset( symbol->clone() );
156 refreshSymbol( true );
157}
158
160{
161 mCornerRadius = radius;
162 emit clipPathChanged();
163}
164
166{
167 QPolygonF shapePolygon = mapToScene( calculatePolygon( 1.0 ) );
168 // ensure polygon is closed
169 if ( shapePolygon.at( 0 ) != shapePolygon.constLast() )
170 shapePolygon << shapePolygon.at( 0 );
171 return QgsGeometry::fromQPolygonF( shapePolygon );
172}
173
175{
176 return mCurrentRectangle;
177}
178
180{
181 return mMaxSymbolBleed;
182}
183
185{
186 if ( mShapeStyleSymbol )
187 {
188 QgsStyleSymbolEntity entity( mShapeStyleSymbol.get() );
189 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
190 return false;
191 }
192
193 return true;
194}
195
197{
198 QPainter *painter = context.renderContext().painter();
199 painter->setPen( Qt::NoPen );
200 painter->setBrush( Qt::NoBrush );
201
202 const double scale = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
203
204 const QVector<QPolygonF> rings; //empty list
205
206 symbol()->startRender( context.renderContext() );
207 symbol()->renderPolygon( calculatePolygon( scale ), &rings, nullptr, context.renderContext() );
208 symbol()->stopRender( context.renderContext() );
209}
210
211QPolygonF QgsLayoutItemShape::calculatePolygon( double scale ) const
212{
213 QPolygonF shapePolygon;
214
215 //shapes with curves must be enlarged before conversion to QPolygonF, or
216 //the curves are approximated too much and appear jaggy
217 const QTransform t = QTransform::fromScale( 100, 100 );
218 //inverse transform used to scale created polygons back to expected size
219 const QTransform ti = t.inverted();
220
221 switch ( mShape )
222 {
223 case Ellipse:
224 {
225 //create an ellipse
226 QPainterPath ellipsePath;
227 ellipsePath.addEllipse( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
228 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
229 shapePolygon = ti.map( ellipsePoly );
230 break;
231 }
232 case Rectangle:
233 {
234 //if corner radius set, then draw a rounded rectangle
235 if ( mCornerRadius.length() > 0 )
236 {
237 QPainterPath roundedRectPath;
238 const double radius = mLayout->convertToLayoutUnits( mCornerRadius ) * scale;
239 roundedRectPath.addRoundedRect( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ), radius, radius );
240 const QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
241 shapePolygon = ti.map( roundedPoly );
242 }
243 else
244 {
245 shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
246 }
247 break;
248 }
249 case Triangle:
250 {
251 shapePolygon << QPointF( 0, rect().height() * scale );
252 shapePolygon << QPointF( rect().width() * scale, rect().height() * scale );
253 shapePolygon << QPointF( rect().width() / 2.0 * scale, 0 );
254 shapePolygon << QPointF( 0, rect().height() * scale );
255 break;
256 }
257 }
258 return shapePolygon;
259}
260
261bool QgsLayoutItemShape::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
262{
263 element.setAttribute( QStringLiteral( "shapeType" ), mShape );
264 element.setAttribute( QStringLiteral( "cornerRadiusMeasure" ), mCornerRadius.encodeMeasurement() );
265
266 const QDomElement shapeStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mShapeStyleSymbol.get(), document, context );
267 element.appendChild( shapeStyleElem );
268
269 return true;
270}
271
272bool QgsLayoutItemShape::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
273{
274 mShape = static_cast< Shape >( element.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() );
275 if ( element.hasAttribute( QStringLiteral( "cornerRadiusMeasure" ) ) )
276 mCornerRadius = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "cornerRadiusMeasure" ), QStringLiteral( "0" ) ) );
277 else
278 mCornerRadius = QgsLayoutMeasurement( element.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() );
279
280 const QDomElement shapeStyleSymbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
281 if ( !shapeStyleSymbolElem.isNull() )
282 {
283 mShapeStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( shapeStyleSymbolElem, context ) );
284 refreshSymbol( false );
285 }
286
287 return true;
288}
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
void renderPolygon(const QPolygonF &points, const QVector< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol using the given render context.
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:45
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
Layout item for basic filled shapes (e.g.
QRectF boundingRect() const override
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
static QgsLayoutItemShape * create(QgsLayout *layout)
Returns a new shape item for the specified layout.
double estimatedFrameBleed() const override
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
QgsFillSymbol * symbol()
Returns the fill symbol used to draw the shape.
~QgsLayoutItemShape() override
void setShapeType(QgsLayoutItemShape::Shape type)
Sets the type of shape (e.g.
QString displayName() const override
Gets item display name.
QgsLayoutItemShape(QgsLayout *layout)
Constructor for QgsLayoutItemShape, with the specified parent layout.
QgsGeometry clipPath() const override
Returns the clipping path generated by this item, in layout coordinates.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void setSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to draw the shape.
QIcon icon() const override
Returns the item's icon.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
int type() const override
@ Ellipse
Ellipse shape.
@ Rectangle
Rectangle shape.
@ Triangle
Triangle shape.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
void setCornerRadius(QgsLayoutMeasurement radius)
Sets the corner radius for rounded rectangle corners.
Base class for graphical items within a QgsLayout.
virtual void redraw()
Triggers a redraw (update) of the item.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
@ FlagProvidesClipPath
Item can act as a clipping path provider (see clipPath())
void sizePositionChanged()
Emitted when the item's size or position changes.
virtual QString uuid() const
Returns the item identification string.
QString id() const
Returns the item's ID name.
void frameChanged()
Emitted if the item's frame style changes.
virtual Flags itemFlags() const
Returns the item's flags, which indicate how the item behaves.
void clipPathChanged()
Emitted when the item's clipping path has changed.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
QString encodeMeasurement() const
Encodes the layout measurement to a string.
double length() const
Returns the length of the measurement.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QPointer< QgsLayout > mLayout
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1342
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:873
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:825
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
Contains information relating to the style entity currently being visited.