QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  } );
47 }
48 
50 {
51  return new QgsLayoutItemShape( layout );
52 }
53 
55 {
57 }
58 
60 {
61  switch ( mShape )
62  {
63  case Ellipse:
64  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeEllipse.svg" ) );
65  case Rectangle:
66  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeRectangle.svg" ) );
67  case Triangle:
68  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeTriangle.svg" ) );
69  }
70 
71  return QIcon();
72 }
73 
75 {
76  if ( !id().isEmpty() )
77  {
78  return id();
79  }
80 
81  switch ( mShape )
82  {
83  case Ellipse:
84  return tr( "<Ellipse>" );
85  case Rectangle:
86  return tr( "<Rectangle>" );
87  case Triangle:
88  return tr( "<Triangle>" );
89  }
90 
91  return tr( "<Shape>" );
92 }
93 
95 {
96  if ( type == mShape )
97  {
98  return;
99  }
100 
101  mShape = type;
102 
103  if ( mLayout && id().isEmpty() )
104  {
105  //notify the model that the display name has changed
106  mLayout->itemsModel()->updateItemDisplayName( this );
107  }
108 }
109 
110 void QgsLayoutItemShape::refreshSymbol()
111 {
112  if ( layout() )
113  {
114  QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( layout(), nullptr, layout()->renderContext().dpi() );
115  mMaxSymbolBleed = ( 25.4 / layout()->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol.get(), rc );
116  }
117 
118  updateBoundingRect();
119 
120  update();
121  emit frameChanged();
122 }
123 
124 void QgsLayoutItemShape::updateBoundingRect()
125 {
126  QRectF rectangle = rect();
127  rectangle.adjust( -mMaxSymbolBleed, -mMaxSymbolBleed, mMaxSymbolBleed, mMaxSymbolBleed );
128  if ( rectangle != mCurrentRectangle )
129  {
130  prepareGeometryChange();
131  mCurrentRectangle = rectangle;
132  }
133 }
134 
136 {
137  if ( !symbol )
138  return;
139 
140  mShapeStyleSymbol.reset( symbol->clone() );
141  refreshSymbol();
142 }
143 
145 {
146  return mCurrentRectangle;
147 }
148 
150 {
151  return mMaxSymbolBleed;
152 }
153 
155 {
156  if ( mShapeStyleSymbol )
157  {
158  QgsStyleSymbolEntity entity( mShapeStyleSymbol.get() );
159  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, uuid(), displayName() ) ) )
160  return false;
161  }
162 
163  return true;
164 }
165 
167 {
168  QPainter *painter = context.renderContext().painter();
169  painter->setPen( Qt::NoPen );
170  painter->setBrush( Qt::NoBrush );
171 
173 
174  QPolygonF shapePolygon;
175 
176  //shapes with curves must be enlarged before conversion to QPolygonF, or
177  //the curves are approximated too much and appear jaggy
178  QTransform t = QTransform::fromScale( 100, 100 );
179  //inverse transform used to scale created polygons back to expected size
180  QTransform ti = t.inverted();
181 
182  switch ( mShape )
183  {
184  case Ellipse:
185  {
186  //create an ellipse
187  QPainterPath ellipsePath;
188  ellipsePath.addEllipse( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
189  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
190  shapePolygon = ti.map( ellipsePoly );
191  break;
192  }
193  case Rectangle:
194  {
195  //if corner radius set, then draw a rounded rectangle
196  if ( mCornerRadius.length() > 0 )
197  {
198  QPainterPath roundedRectPath;
199  double radius = mLayout->convertToLayoutUnits( mCornerRadius ) * scale;
200  roundedRectPath.addRoundedRect( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ), radius, radius );
201  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
202  shapePolygon = ti.map( roundedPoly );
203  }
204  else
205  {
206  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
207  }
208  break;
209  }
210  case Triangle:
211  {
212  shapePolygon << QPointF( 0, rect().height() * scale );
213  shapePolygon << QPointF( rect().width() * scale, rect().height() * scale );
214  shapePolygon << QPointF( rect().width() / 2.0 * scale, 0 );
215  shapePolygon << QPointF( 0, rect().height() * scale );
216  break;
217  }
218  }
219 
220  QVector<QPolygonF> rings; //empty list
221 
222  symbol()->startRender( context.renderContext() );
223  symbol()->renderPolygon( shapePolygon, &rings, nullptr, context.renderContext() );
224  symbol()->stopRender( context.renderContext() );
225 }
226 
227 bool QgsLayoutItemShape::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
228 {
229  element.setAttribute( QStringLiteral( "shapeType" ), mShape );
230  element.setAttribute( QStringLiteral( "cornerRadiusMeasure" ), mCornerRadius.encodeMeasurement() );
231 
232  QDomElement shapeStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mShapeStyleSymbol.get(), document, context );
233  element.appendChild( shapeStyleElem );
234 
235  return true;
236 }
237 
238 bool QgsLayoutItemShape::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
239 {
240  mShape = static_cast< Shape >( element.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() );
241  if ( element.hasAttribute( QStringLiteral( "cornerRadiusMeasure" ) ) )
242  mCornerRadius = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "cornerRadiusMeasure" ), QStringLiteral( "0" ) ) );
243  else
244  mCornerRadius = QgsLayoutMeasurement( element.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() );
245 
246  QDomElement shapeStyleSymbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
247  if ( !shapeStyleSymbolElem.isNull() )
248  {
249  mShapeStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( shapeStyleSymbolElem, context ) );
250  }
251 
252  return true;
253 }
QgsLayoutItem::id
QString id() const
Returns the item's ID name.
Definition: qgslayoutitem.h:354
QgsLayoutObject::layout
const QgsLayout * layout() const
Returns the layout the object is attached to.
Definition: qgslayoutobject.cpp:118
QgsLayoutItemShape
Layout item for basic filled shapes (e.g. rectangles, ellipses).
Definition: qgslayoutitemshape.h:32
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:287
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:605
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
Definition: qgsreadwritecontext.h:34
QgsStyleSymbolEntity
Definition: qgsstyle.h:1134
QgsLayout::renderContext
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Definition: qgslayout.cpp:358
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:154
qgssymbollayerutils.h
QgsLayoutItemRenderContext
Definition: qgslayoutitem.h:44
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsLayoutItemShape::icon
QIcon icon() const override
Returns the item's icon.
Definition: qgslayoutitemshape.cpp:59
QgsStyleEntityVisitorInterface
Definition: qgsstyleentityvisitor.h:33
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:227
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:238
QgsStyleEntityVisitorInterface::StyleLeaf
Contains information relating to the style entity currently being visited.
Definition: qgsstyleentityvisitor.h:60
QgsLayoutItemShape::Ellipse
@ Ellipse
Ellipse shape.
Definition: qgslayoutitemshape.h:41
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:460
QgsLayoutItemShape::displayName
QString displayName() const override
Gets item display name.
Definition: qgslayoutitemshape.cpp:74
QgsLayoutItemShape::create
static QgsLayoutItemShape * create(QgsLayout *layout)
Returns a new shape item for the specified layout.
Definition: qgslayoutitemshape.cpp:49
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:435
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:149
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:2161
qgslayout.h
QgsLayoutItemShape::Rectangle
@ Rectangle
Rectangle shape.
Definition: qgslayoutitemshape.h:42
QgsLayoutItemShape::type
int type() const override
Definition: qgslayoutitemshape.cpp:54
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:340
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:714
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:335
QgsLayoutItemShape::boundingRect
QRectF boundingRect() const override
Definition: qgslayoutitemshape.cpp:144
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
QgsLayoutItemShape::setShapeType
void setShapeType(QgsLayoutItemShape::Shape type)
Sets the type of shape (e.g.
Definition: qgslayoutitemshape.cpp:94
QgsFillSymbol::clone
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2263
QgsFillSymbol
Definition: qgssymbol.h:1212
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:89
QgsLayoutRenderContext::dpi
double dpi() const
Returns the dpi for outputting the layout.
Definition: qgslayoutrendercontext.cpp:84
QgsLayoutItemShape::Shape
Shape
Shape type.
Definition: qgslayoutitemshape.h:39
QgsFillSymbol::createSimple
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Definition: qgssymbol.cpp:1440
QgsSymbolLayerUtils::estimateMaxSymbolBleed
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
Definition: qgssymbollayerutils.cpp:820
QgsLayoutItemShape::Triangle
@ Triangle
Triangle shape.
Definition: qgslayoutitemshape.h:43
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:174
QgsLayoutItemRegistry::LayoutShape
@ LayoutShape
Shape item.
Definition: qgslayoutitemregistry.h:321
qgslayoutmodel.h
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:1180
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:135
qgsstyleentityvisitor.h
QgsLayoutItemShape::draw
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
Definition: qgslayoutitemshape.cpp:166
QgsLayoutMeasurement::decodeMeasurement
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
Definition: qgslayoutmeasurement.cpp:33