QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
24 #include <QPainter>
25 
27  : QgsLayoutItem( layout )
28  , mCornerRadius( 0 )
29 {
30  setBackgroundEnabled( false );
31  setFrameEnabled( false );
32  QgsStringMap properties;
33  properties.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
34  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
35  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
36  properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
37  properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
38  properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
39  mShapeStyleSymbol.reset( QgsFillSymbol::createSimple( properties ) );
40  refreshSymbol();
41 
42  connect( this, &QgsLayoutItemShape::sizePositionChanged, this, [ = ]
43  {
44  updateBoundingRect();
45  update();
46  emit clipPathChanged();
47  } );
48 }
49 
51 {
52  return new QgsLayoutItemShape( layout );
53 }
54 
56 {
58 }
59 
61 {
62  switch ( mShape )
63  {
64  case Ellipse:
65  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeEllipse.svg" ) );
66  case Rectangle:
67  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeRectangle.svg" ) );
68  case Triangle:
69  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeTriangle.svg" ) );
70  }
71 
72  return QIcon();
73 }
74 
76 {
77  if ( !id().isEmpty() )
78  {
79  return id();
80  }
81 
82  switch ( mShape )
83  {
84  case Ellipse:
85  return tr( "<Ellipse>" );
86  case Rectangle:
87  return tr( "<Rectangle>" );
88  case Triangle:
89  return tr( "<Triangle>" );
90  }
91 
92  return tr( "<Shape>" );
93 }
94 
95 QgsLayoutItem::Flags QgsLayoutItemShape::itemFlags() const
96 {
97  QgsLayoutItem::Flags flags = QgsLayoutItem::itemFlags();
99  return flags;
100 }
101 
103 {
104  if ( type == mShape )
105  {
106  return;
107  }
108 
109  mShape = type;
110 
111  if ( mLayout && id().isEmpty() )
112  {
113  //notify the model that the display name has changed
114  mLayout->itemsModel()->updateItemDisplayName( this );
115  }
116 
117  emit clipPathChanged();
118 }
119 
120 void QgsLayoutItemShape::refreshSymbol()
121 {
122  if ( auto *lLayout = layout() )
123  {
124  QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( lLayout, nullptr, lLayout->renderContext().dpi() );
125  mMaxSymbolBleed = ( 25.4 / lLayout->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol.get(), rc );
126  }
127 
128  updateBoundingRect();
129 
130  update();
131  emit frameChanged();
132 }
133 
134 void QgsLayoutItemShape::updateBoundingRect()
135 {
136  QRectF rectangle = rect();
137  rectangle.adjust( -mMaxSymbolBleed, -mMaxSymbolBleed, mMaxSymbolBleed, mMaxSymbolBleed );
138  if ( rectangle != mCurrentRectangle )
139  {
140  prepareGeometryChange();
141  mCurrentRectangle = rectangle;
142  }
143 }
144 
146 {
147  if ( !symbol )
148  return;
149 
150  mShapeStyleSymbol.reset( symbol->clone() );
151  refreshSymbol();
152 }
153 
155 {
156  mCornerRadius = radius;
157  emit clipPathChanged();
158 }
159 
161 {
162  QPolygonF shapePolygon = mapToScene( calculatePolygon( 1.0 ) );
163  // ensure polygon is closed
164  if ( shapePolygon.at( 0 ) != shapePolygon.constLast() )
165  shapePolygon << shapePolygon.at( 0 );
166  return QgsGeometry::fromQPolygonF( shapePolygon );
167 }
168 
170 {
171  return mCurrentRectangle;
172 }
173 
175 {
176  return mMaxSymbolBleed;
177 }
178 
180 {
181  if ( mShapeStyleSymbol )
182  {
183  QgsStyleSymbolEntity entity( mShapeStyleSymbol.get() );
184  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
185  return false;
186  }
187 
188  return true;
189 }
190 
192 {
193  QPainter *painter = context.renderContext().painter();
194  painter->setPen( Qt::NoPen );
195  painter->setBrush( Qt::NoBrush );
196 
197  const double scale = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
198 
199  QVector<QPolygonF> rings; //empty list
200 
201  symbol()->startRender( context.renderContext() );
202  symbol()->renderPolygon( calculatePolygon( scale ), &rings, nullptr, context.renderContext() );
203  symbol()->stopRender( context.renderContext() );
204 }
205 
206 QPolygonF QgsLayoutItemShape::calculatePolygon( double scale ) const
207 {
208  QPolygonF shapePolygon;
209 
210  //shapes with curves must be enlarged before conversion to QPolygonF, or
211  //the curves are approximated too much and appear jaggy
212  QTransform t = QTransform::fromScale( 100, 100 );
213  //inverse transform used to scale created polygons back to expected size
214  QTransform ti = t.inverted();
215 
216  switch ( mShape )
217  {
218  case Ellipse:
219  {
220  //create an ellipse
221  QPainterPath ellipsePath;
222  ellipsePath.addEllipse( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
223  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
224  shapePolygon = ti.map( ellipsePoly );
225  break;
226  }
227  case Rectangle:
228  {
229  //if corner radius set, then draw a rounded rectangle
230  if ( mCornerRadius.length() > 0 )
231  {
232  QPainterPath roundedRectPath;
233  double radius = mLayout->convertToLayoutUnits( mCornerRadius ) * scale;
234  roundedRectPath.addRoundedRect( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ), radius, radius );
235  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
236  shapePolygon = ti.map( roundedPoly );
237  }
238  else
239  {
240  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
241  }
242  break;
243  }
244  case Triangle:
245  {
246  shapePolygon << QPointF( 0, rect().height() * scale );
247  shapePolygon << QPointF( rect().width() * scale, rect().height() * scale );
248  shapePolygon << QPointF( rect().width() / 2.0 * scale, 0 );
249  shapePolygon << QPointF( 0, rect().height() * scale );
250  break;
251  }
252  }
253  return shapePolygon;
254 }
255 
256 bool QgsLayoutItemShape::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
257 {
258  element.setAttribute( QStringLiteral( "shapeType" ), mShape );
259  element.setAttribute( QStringLiteral( "cornerRadiusMeasure" ), mCornerRadius.encodeMeasurement() );
260 
261  QDomElement shapeStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mShapeStyleSymbol.get(), document, context );
262  element.appendChild( shapeStyleElem );
263 
264  return true;
265 }
266 
267 bool QgsLayoutItemShape::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
268 {
269  mShape = static_cast< Shape >( element.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() );
270  if ( element.hasAttribute( QStringLiteral( "cornerRadiusMeasure" ) ) )
271  mCornerRadius = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "cornerRadiusMeasure" ), QStringLiteral( "0" ) ) );
272  else
273  mCornerRadius = QgsLayoutMeasurement( element.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() );
274 
275  QDomElement shapeStyleSymbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
276  if ( !shapeStyleSymbolElem.isNull() )
277  {
278  mShapeStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( shapeStyleSymbolElem, context ) );
279  }
280 
281  return true;
282 }
QgsLayoutItem::id
QString id() const
Returns the item's ID name.
Definition: qgslayoutitem.h:357
QgsLayoutObject::layout
const QgsLayout * layout() const
Returns the layout the object is attached to.
Definition: qgslayoutobject.cpp:126
QgsLayoutItemShape
Layout item for basic filled shapes (e.g.
Definition: qgslayoutitemshape.h:33
QgsRenderContext::convertToPainterUnits
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Definition: qgsrendercontext.cpp:318
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
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:833
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsLayoutItemShape::itemFlags
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
Definition: qgslayoutitemshape.cpp:95
QgsStyleSymbolEntity
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1201
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:179
qgssymbollayerutils.h
QgsLayoutItemRenderContext
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:45
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsLayoutItemShape::icon
QIcon icon() const override
Returns the item's icon.
Definition: qgslayoutitemshape.cpp:60
QgsLayoutItemShape::setCornerRadius
void setCornerRadius(QgsLayoutMeasurement radius)
Sets the corner radius for rounded rectangle corners.
Definition: qgslayoutitemshape.cpp:154
QgsStyleEntityVisitorInterface
An interface for classes which can visit style entity (e.g.
Definition: qgsstyleentityvisitor.h:34
QgsUnitTypes::RenderMillimeters
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
QgsLayoutItemShape::writePropertiesToElement
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
Definition: qgslayoutitemshape.cpp:256
QgsLayoutItemShape::QgsLayoutItemShape
QgsLayoutItemShape(QgsLayout *layout)
Constructor for QgsLayoutItemShape, with the specified parent layout.
Definition: qgslayoutitemshape.cpp:26
QgsLayoutItemShape::readPropertiesFromElement
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
Definition: qgslayoutitemshape.cpp:267
QgsStyleEntityVisitorInterface::StyleLeaf
Contains information relating to the style entity currently being visited.
Definition: qgsstyleentityvisitor.h:61
QgsLayoutItemShape::Ellipse
@ Ellipse
Ellipse shape.
Definition: qgslayoutitemshape.h:41
QgsLayoutItem::FlagProvidesClipPath
@ FlagProvidesClipPath
Item can act as a clipping path provider (see clipPath())
Definition: qgslayoutitem.h:304
QgsLayoutMeasurement::encodeMeasurement
QString encodeMeasurement() const
Encodes the layout measurement to a string.
Definition: qgslayoutmeasurement.cpp:28
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:138
QgsSymbol::stopRender
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:479
QgsLayoutItemShape::displayName
QString displayName() const override
Gets item display name.
Definition: qgslayoutitemshape.cpp:75
QgsLayoutItemShape::create
static QgsLayoutItemShape * create(QgsLayout *layout)
Returns a new shape item for the specified layout.
Definition: qgslayoutitemshape.cpp:50
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:886
QgsSymbol::startRender
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:454
QgsLayoutItem
Base class for graphical items within a QgsLayout.
Definition: qgslayoutitem.h:113
QgsLayoutItemShape::estimatedFrameBleed
double estimatedFrameBleed() const override
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
Definition: qgslayoutitemshape.cpp:174
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: qgssymbol.cpp:2215
qgslayout.h
QgsLayoutItemShape::Rectangle
@ Rectangle
Rectangle shape.
Definition: qgslayoutitemshape.h:42
QgsLayoutItemShape::type
int type() const override
Definition: qgslayoutitemshape.cpp:55
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:343
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:758
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:343
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsLayoutItemShape::boundingRect
QRectF boundingRect() const override
Definition: qgslayoutitemshape.cpp:169
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsLayoutItemShape::setShapeType
void setShapeType(QgsLayoutItemShape::Shape type)
Sets the type of shape (e.g.
Definition: qgslayoutitemshape.cpp:102
QgsFillSymbol::clone
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2317
QgsFillSymbol
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgssymbol.h:1234
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:90
QgsLayoutItemShape::Shape
Shape
Shape type.
Definition: qgslayoutitemshape.h:40
QgsGeometry::fromQPolygonF
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Definition: qgsgeometry.cpp:3072
QgsFillSymbol::createSimple
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Definition: qgssymbol.cpp:1481
QgsSymbolLayerUtils::estimateMaxSymbolBleed
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
Definition: qgssymbollayerutils.cpp:822
QgsLayoutItemShape::Triangle
@ Triangle
Triangle shape.
Definition: qgslayoutitemshape.h:43
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:179
QgsLayoutItemRegistry::LayoutShape
@ LayoutShape
Shape item.
Definition: qgslayoutitemregistry.h:321
qgslayoutmodel.h
QgsLayoutItemShape::clipPath
QgsGeometry clipPath() const override
Returns the clipping path generated by this item, in layout coordinates.
Definition: qgslayoutitemshape.cpp:160
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:1182
QgsLayoutMeasurement
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
Definition: qgslayoutmeasurement.h:34
QgsLayoutItemShape::setSymbol
void setSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to draw the shape.
Definition: qgslayoutitemshape.cpp:145
qgsstyleentityvisitor.h
QgsLayoutItemShape::draw
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
Definition: qgslayoutitemshape.cpp:191
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.