QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
22 #include "qgsstyleentityvisitor.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 
98 QgsLayoutItem::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 
123 void 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 
139 void 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 
211 QPolygonF 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 
261 bool 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 
272 bool 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 }
QgsLayoutItem::id
QString id() const
Returns the item's ID name.
Definition: qgslayoutitem.h:359
QgsLayoutObject::layout
const QgsLayout * layout() const
Returns the layout the object is attached to.
Definition: qgslayoutobject.cpp:216
QgsLayoutItemShape
Layout item for basic filled shapes (e.g. rectangles, ellipses).
Definition: qgslayoutitemshape.h:31
QgsLayoutItem::sizePositionChanged
void sizePositionChanged()
Emitted when the item's size or position changes.
QgsLayoutItem::setFrameEnabled
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
Definition: qgslayoutitem.cpp:834
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
QgsLayoutItemShape::itemFlags
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
Definition: qgslayoutitemshape.cpp:98
QgsStyleSymbolEntity
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1341
QgsLayoutMeasurement::length
double length() const
Returns the length of the measurement.
Definition: qgslayoutmeasurement.h:48
QgsLayoutItemShape::accept
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
Definition: qgslayoutitemshape.cpp:184
qgssymbollayerutils.h
QgsLayoutItemRenderContext
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsLayoutItemShape::icon
QIcon icon() const override
Returns the item's icon.
Definition: qgslayoutitemshape.cpp:63
QgsLayoutItemShape::setCornerRadius
void setCornerRadius(QgsLayoutMeasurement radius)
Sets the corner radius for rounded rectangle corners.
Definition: qgslayoutitemshape.cpp:159
QgsStyleEntityVisitorInterface
An interface for classes which can visit style entity (e.g. symbol) nodes (using the visitor pattern)...
Definition: qgsstyleentityvisitor.h:33
QgsUnitTypes::RenderMillimeters
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
QgsLayoutItemShape::writePropertiesToElement
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
Definition: qgslayoutitemshape.cpp:261
QgsLayoutItemShape::QgsLayoutItemShape
QgsLayoutItemShape(QgsLayout *layout)
Constructor for QgsLayoutItemShape, with the specified parent layout.
Definition: qgslayoutitemshape.cpp:27
QgsLayoutItemShape::readPropertiesFromElement
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
Definition: qgslayoutitemshape.cpp:272
QgsStyleEntityVisitorInterface::StyleLeaf
Contains information relating to the style entity currently being visited.
Definition: qgsstyleentityvisitor.h:60
QgsLayoutItemShape::Ellipse
@ Ellipse
Ellipse shape.
Definition: qgslayoutitemshape.h:40
QgsLayoutItem::FlagProvidesClipPath
@ FlagProvidesClipPath
Item can act as a clipping path provider (see clipPath())
Definition: qgslayoutitem.h:306
QgsLayoutMeasurement::encodeMeasurement
QString encodeMeasurement() const
Encodes the layout measurement to a string.
Definition: qgslayoutmeasurement.cpp:28
QgsLayoutItem::redraw
virtual void redraw()
Triggers a redraw (update) of the item.
Definition: qgslayoutitem.cpp:1195
qgslayoututils.h
QgsLayoutUtils::createRenderContextForLayout
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
Definition: qgslayoututils.cpp:141
QgsSymbol::stopRender
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:842
QgsLayoutItemShape::displayName
QString displayName() const override
Gets item display name.
Definition: qgslayoutitemshape.cpp:78
QgsLayoutItemShape::create
static QgsLayoutItemShape * create(QgsLayout *layout)
Returns a new shape item for the specified layout.
Definition: qgslayoutitemshape.cpp:53
QgsLayoutItem::frameChanged
void frameChanged()
Emitted if the item's frame style changes.
QgsLayoutItem::setBackgroundEnabled
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
Definition: qgslayoutitem.cpp:887
QgsSymbol::startRender
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:794
QgsLayoutItem
Base class for graphical items within a QgsLayout.
Definition: qgslayoutitem.h:112
QgsLayoutItemShape::estimatedFrameBleed
double estimatedFrameBleed() const override
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
Definition: qgslayoutitemshape.cpp:179
QgsFillSymbol::renderPolygon
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.
Definition: qgsfillsymbol.cpp:39
QgsFillSymbol::createSimple
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Definition: qgsfillsymbol.cpp:20
qgslayout.h
QgsLayoutItemShape::Rectangle
@ Rectangle
Rectangle shape.
Definition: qgslayoutitemshape.h:41
QgsRenderContext::convertToPainterUnits
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).
Definition: qgsrendercontext.cpp:367
QgsLayoutItemShape::type
int type() const override
Definition: qgslayoutitemshape.cpp:58
QgsLayoutItem::itemFlags
virtual Flags itemFlags() const
Returns the item's flags, which indicate how the item behaves.
Definition: qgslayoutitem.cpp:129
QgsLayoutItemRenderContext::renderContext
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
QgsLayoutItem::uuid
virtual QString uuid() const
Returns the item identification string.
Definition: qgslayoutitem.h:345
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:363
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsLayoutItemShape::boundingRect
QRectF boundingRect() const override
Definition: qgslayoutitemshape.cpp:174
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsLayoutItemShape::~QgsLayoutItemShape
~QgsLayoutItemShape() override
QgsLayoutItemShape::setShapeType
void setShapeType(QgsLayoutItemShape::Shape type)
Sets the type of shape (e.g.
Definition: qgslayoutitemshape.cpp:105
QgsFillSymbol::clone
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgsfillsymbol.cpp:144
QgsFillSymbol
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:29
QgsStyleEntityVisitorInterface::visit
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
Definition: qgsstyleentityvisitor.h:153
qgslayoutitemshape.h
QgsLayoutItemShape::symbol
QgsFillSymbol * symbol()
Returns the fill symbol used to draw the shape.
Definition: qgslayoutitemshape.h:91
QgsLayoutItemShape::Shape
Shape
Shape type.
Definition: qgslayoutitemshape.h:38
QgsGeometry::fromQPolygonF
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Definition: qgsgeometry.cpp:3408
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
QgsSymbolLayerUtils::estimateMaxSymbolBleed
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
Definition: qgssymbollayerutils.cpp:934
QgsLayoutItemShape::Triangle
@ Triangle
Triangle shape.
Definition: qgslayoutitemshape.h:42
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:112
QgsLayoutItemRegistry::LayoutShape
@ LayoutShape
Shape item.
Definition: qgslayoutitemregistry.h:351
qgsfillsymbol.h
qgslayoutmodel.h
QgsLayoutItemShape::clipPath
QgsGeometry clipPath() const override
Returns the clipping path generated by this item, in layout coordinates.
Definition: qgslayoutitemshape.cpp:165
QgsSymbolLayerUtils::saveSymbol
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
Definition: qgssymbollayerutils.cpp:1397
QgsLayoutMeasurement
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
Definition: qgslayoutmeasurement.h:33
QgsLayoutItemShape::setSymbol
void setSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to draw the shape.
Definition: qgslayoutitemshape.cpp:150
qgsstyleentityvisitor.h
QgsLayoutItemShape::draw
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
Definition: qgslayoutitemshape.cpp:196
QgsLayoutMeasurement::decodeMeasurement
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
Definition: qgslayoutmeasurement.cpp:33
QgsLayoutItem::clipPathChanged
void clipPathChanged()
Emitted when the item's clipping path has changed.