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