QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
Loading...
Searching...
No Matches
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 "moc_qgslayoutitemshape.cpp"
19#include "qgslayout.h"
20#include "qgslayoututils.h"
21#include "qgssymbollayerutils.h"
22#include "qgslayoutmodel.h"
24#include "qgsfillsymbol.h"
26
27#include <QPainter>
28
30 : QgsLayoutItem( layout )
31 , mCornerRadius( 0 )
32{
33 setBackgroundEnabled( false );
34 setFrameEnabled( false );
35 QVariantMap properties;
36 properties.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
37 properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
38 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
39 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
40 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
41 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
42 mShapeStyleSymbol.reset( QgsFillSymbol::createSimple( properties ) );
43 refreshSymbol( false );
44
45 connect( this, &QgsLayoutItemShape::sizePositionChanged, this, [this]
46 {
47 updateBoundingRect();
48 update();
49 emit clipPathChanged();
50 } );
51}
52
54
59
64
66{
67 switch ( mShape )
68 {
69 case Ellipse:
70 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeEllipse.svg" ) );
71 case Rectangle:
72 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeRectangle.svg" ) );
73 case Triangle:
74 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeTriangle.svg" ) );
75 }
76
77 return QIcon();
78}
79
81{
82 if ( !id().isEmpty() )
83 {
84 return id();
85 }
86
87 switch ( mShape )
88 {
89 case Ellipse:
90 return tr( "<Ellipse>" );
91 case Rectangle:
92 return tr( "<Rectangle>" );
93 case Triangle:
94 return tr( "<Triangle>" );
95 }
96
97 return tr( "<Shape>" );
98}
99
106
108{
109 if ( type == mShape )
110 {
111 return;
112 }
113
114 mShape = type;
115
116 if ( mLayout && id().isEmpty() )
117 {
118 //notify the model that the display name has changed
119 mLayout->itemsModel()->updateItemDisplayName( this );
120 }
121
122 emit clipPathChanged();
123}
124
125void QgsLayoutItemShape::refreshSymbol( bool redraw )
126{
127 if ( auto *lLayout = layout() )
128 {
129 const QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( lLayout, nullptr, lLayout->renderContext().dpi() );
130 mMaxSymbolBleed = ( 25.4 / lLayout->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol.get(), rc );
131 }
132
133 updateBoundingRect();
134
135 if ( redraw )
136 update();
137
138 emit frameChanged();
139}
140
141void QgsLayoutItemShape::updateBoundingRect()
142{
143 QRectF rectangle = rect();
144 rectangle.adjust( -mMaxSymbolBleed, -mMaxSymbolBleed, mMaxSymbolBleed, mMaxSymbolBleed );
145 if ( rectangle != mCurrentRectangle )
146 {
147 prepareGeometryChange();
148 mCurrentRectangle = rectangle;
149 }
150}
151
153{
154 if ( !symbol )
155 return;
156
157 mShapeStyleSymbol.reset( symbol->clone() );
158 refreshSymbol( true );
159}
160
162{
163 mCornerRadius = radius;
164 emit clipPathChanged();
165}
166
168{
169 QPolygonF shapePolygon = mapToScene( calculatePolygon( 1.0 ) );
170 // ensure polygon is closed
171 if ( shapePolygon.at( 0 ) != shapePolygon.constLast() )
172 shapePolygon << shapePolygon.at( 0 );
173 return QgsGeometry::fromQPolygonF( shapePolygon );
174}
175
177{
178 return mCurrentRectangle;
179}
180
182{
183 return mMaxSymbolBleed;
184}
185
187{
188 if ( mShapeStyleSymbol )
189 {
190 QgsStyleSymbolEntity entity( mShapeStyleSymbol.get() );
191 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
192 return false;
193 }
194
195 return true;
196}
197
199{
200 QgsRenderContext renderContext = context.renderContext();
201 // symbol clipping messes with geometry generators used in the symbol for this item, and has no
202 // valid use here. See https://github.com/qgis/QGIS/issues/58909
204
205 QPainter *painter = renderContext.painter();
206 painter->setPen( Qt::NoPen );
207 painter->setBrush( Qt::NoBrush );
208
209 const double scale = renderContext.convertToPainterUnits( 1, Qgis::RenderUnit::Millimeters );
210
211 const QVector<QPolygonF> rings; //empty list
212
213 symbol()->startRender( renderContext );
214 symbol()->renderPolygon( calculatePolygon( scale ), &rings, nullptr, renderContext );
215 symbol()->stopRender( renderContext );
216}
217
218QPolygonF QgsLayoutItemShape::calculatePolygon( double scale ) const
219{
220 QPolygonF shapePolygon;
221
222 //shapes with curves must be enlarged before conversion to QPolygonF, or
223 //the curves are approximated too much and appear jaggy
224 const QTransform t = QTransform::fromScale( 100, 100 );
225 //inverse transform used to scale created polygons back to expected size
226 const QTransform ti = t.inverted();
227
228 switch ( mShape )
229 {
230 case Ellipse:
231 {
232 //create an ellipse
233 QPainterPath ellipsePath;
234 ellipsePath.addEllipse( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
235 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
236 shapePolygon = ti.map( ellipsePoly );
237 break;
238 }
239 case Rectangle:
240 {
241 //if corner radius set, then draw a rounded rectangle
242 if ( mCornerRadius.length() > 0 )
243 {
244 QPainterPath roundedRectPath;
245 const double radius = mLayout->convertToLayoutUnits( mCornerRadius ) * scale;
246 roundedRectPath.addRoundedRect( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ), radius, radius );
247 const QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
248 shapePolygon = ti.map( roundedPoly );
249 }
250 else
251 {
252 shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
253 }
254 break;
255 }
256 case Triangle:
257 {
258 shapePolygon << QPointF( 0, rect().height() * scale );
259 shapePolygon << QPointF( rect().width() * scale, rect().height() * scale );
260 shapePolygon << QPointF( rect().width() / 2.0 * scale, 0 );
261 shapePolygon << QPointF( 0, rect().height() * scale );
262 break;
263 }
264 }
265 return shapePolygon;
266}
267
268bool QgsLayoutItemShape::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
269{
270 element.setAttribute( QStringLiteral( "shapeType" ), mShape );
271 element.setAttribute( QStringLiteral( "cornerRadiusMeasure" ), mCornerRadius.encodeMeasurement() );
272
273 const QDomElement shapeStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mShapeStyleSymbol.get(), document, context );
274 element.appendChild( shapeStyleElem );
275
276 return true;
277}
278
279bool QgsLayoutItemShape::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
280{
281 mShape = static_cast< Shape >( element.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() );
282 if ( element.hasAttribute( QStringLiteral( "cornerRadiusMeasure" ) ) )
283 mCornerRadius = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "cornerRadiusMeasure" ), QStringLiteral( "0" ) ) );
284 else
285 mCornerRadius = QgsLayoutMeasurement( element.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() );
286
287 const QDomElement shapeStyleSymbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
288 if ( !shapeStyleSymbolElem.isNull() )
289 {
290 mShapeStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( shapeStyleSymbolElem, context ) );
291 refreshSymbol( false );
292 }
293
294 return true;
295}
@ Millimeters
Millimeters.
@ DisableSymbolClippingToExtent
Force symbol clipping to map extent to be disabled in all situations. This will result in slower rend...
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.
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.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
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.
QFlags< Flag > Flags
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:49
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.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
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:1396
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.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Contains information relating to the style entity currently being visited.