QGIS API Documentation  3.6.0-Noosa (5873452)
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 
23 #include <QPainter>
24 
26  : QgsLayoutItem( layout )
27  , mCornerRadius( 0 )
28 {
29  setBackgroundEnabled( false );
30  setFrameEnabled( false );
31  QgsStringMap properties;
32  properties.insert( QStringLiteral( "color" ), QStringLiteral( "white" ) );
33  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
34  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
35  properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "black" ) );
36  properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
37  properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
38  mShapeStyleSymbol.reset( QgsFillSymbol::createSimple( properties ) );
39  refreshSymbol();
40 
41  connect( this, &QgsLayoutItemShape::sizePositionChanged, this, [ = ]
42  {
43  updateBoundingRect();
44  update();
45  } );
46 }
47 
49 {
50  return new QgsLayoutItemShape( layout );
51 }
52 
54 {
56 }
57 
59 {
60  switch ( mShape )
61  {
62  case Ellipse:
63  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeEllipse.svg" ) );
64  case Rectangle:
65  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeRectangle.svg" ) );
66  case Triangle:
67  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemShapeTriangle.svg" ) );
68  }
69 
70  return QIcon();
71 }
72 
74 {
75  if ( !id().isEmpty() )
76  {
77  return id();
78  }
79 
80  switch ( mShape )
81  {
82  case Ellipse:
83  return tr( "<Ellipse>" );
84  case Rectangle:
85  return tr( "<Rectangle>" );
86  case Triangle:
87  return tr( "<Triangle>" );
88  }
89 
90  return tr( "<Shape>" );
91 }
92 
94 {
95  if ( type == mShape )
96  {
97  return;
98  }
99 
100  mShape = type;
101 
102  if ( mLayout && id().isEmpty() )
103  {
104  //notify the model that the display name has changed
105  mLayout->itemsModel()->updateItemDisplayName( this );
106  }
107 }
108 
109 void QgsLayoutItemShape::refreshSymbol()
110 {
111  if ( layout() )
112  {
113  QgsRenderContext rc = QgsLayoutUtils::createRenderContextForLayout( layout(), nullptr, layout()->renderContext().dpi() );
114  mMaxSymbolBleed = ( 25.4 / layout()->renderContext().dpi() ) * QgsSymbolLayerUtils::estimateMaxSymbolBleed( mShapeStyleSymbol.get(), rc );
115  }
116 
117  updateBoundingRect();
118 
119  update();
120  emit frameChanged();
121 }
122 
123 void QgsLayoutItemShape::updateBoundingRect()
124 {
125  QRectF rectangle = rect();
126  rectangle.adjust( -mMaxSymbolBleed, -mMaxSymbolBleed, mMaxSymbolBleed, mMaxSymbolBleed );
127  if ( rectangle != mCurrentRectangle )
128  {
129  prepareGeometryChange();
130  mCurrentRectangle = rectangle;
131  }
132 }
133 
135 {
136  if ( !symbol )
137  return;
138 
139  mShapeStyleSymbol.reset( symbol->clone() );
140  refreshSymbol();
141 }
142 
144 {
145  return mCurrentRectangle;
146 }
147 
149 {
150  return mMaxSymbolBleed;
151 }
152 
154 {
155  QPainter *painter = context.renderContext().painter();
156  painter->setPen( Qt::NoPen );
157  painter->setBrush( Qt::NoBrush );
158 
160 
161  QPolygonF shapePolygon;
162 
163  //shapes with curves must be enlarged before conversion to QPolygonF, or
164  //the curves are approximated too much and appear jaggy
165  QTransform t = QTransform::fromScale( 100, 100 );
166  //inverse transform used to scale created polygons back to expected size
167  QTransform ti = t.inverted();
168 
169  switch ( mShape )
170  {
171  case Ellipse:
172  {
173  //create an ellipse
174  QPainterPath ellipsePath;
175  ellipsePath.addEllipse( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
176  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
177  shapePolygon = ti.map( ellipsePoly );
178  break;
179  }
180  case Rectangle:
181  {
182  //if corner radius set, then draw a rounded rectangle
183  if ( mCornerRadius.length() > 0 )
184  {
185  QPainterPath roundedRectPath;
186  double radius = mLayout->convertToLayoutUnits( mCornerRadius ) * scale;
187  roundedRectPath.addRoundedRect( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ), radius, radius );
188  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
189  shapePolygon = ti.map( roundedPoly );
190  }
191  else
192  {
193  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * scale, rect().height() * scale ) );
194  }
195  break;
196  }
197  case Triangle:
198  {
199  shapePolygon << QPointF( 0, rect().height() * scale );
200  shapePolygon << QPointF( rect().width() * scale, rect().height() * scale );
201  shapePolygon << QPointF( rect().width() / 2.0 * scale, 0 );
202  shapePolygon << QPointF( 0, rect().height() * scale );
203  break;
204  }
205  }
206 
207  QList<QPolygonF> rings; //empty list
208 
209  symbol()->startRender( context.renderContext() );
210  symbol()->renderPolygon( shapePolygon, &rings, nullptr, context.renderContext() );
211  symbol()->stopRender( context.renderContext() );
212 }
213 
214 bool QgsLayoutItemShape::writePropertiesToElement( QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context ) const
215 {
216  element.setAttribute( QStringLiteral( "shapeType" ), mShape );
217  element.setAttribute( QStringLiteral( "cornerRadiusMeasure" ), mCornerRadius.encodeMeasurement() );
218 
219  QDomElement shapeStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mShapeStyleSymbol.get(), document, context );
220  element.appendChild( shapeStyleElem );
221 
222  return true;
223 }
224 
225 bool QgsLayoutItemShape::readPropertiesFromElement( const QDomElement &element, const QDomDocument &, const QgsReadWriteContext &context )
226 {
227  mShape = static_cast< Shape >( element.attribute( QStringLiteral( "shapeType" ), QStringLiteral( "0" ) ).toInt() );
228  if ( element.hasAttribute( QStringLiteral( "cornerRadiusMeasure" ) ) )
229  mCornerRadius = QgsLayoutMeasurement::decodeMeasurement( element.attribute( QStringLiteral( "cornerRadiusMeasure" ), QStringLiteral( "0" ) ) );
230  else
231  mCornerRadius = QgsLayoutMeasurement( element.attribute( QStringLiteral( "cornerRadius" ), QStringLiteral( "0" ) ).toDouble() );
232 
233  QDomElement shapeStyleSymbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
234  if ( !shapeStyleSymbolElem.isNull() )
235  {
236  mShapeStyleSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( shapeStyleSymbolElem, context ) );
237  }
238 
239  return true;
240 }
QIcon icon() const override
Returns the item&#39;s icon.
void setShapeType(QgsLayoutItemShape::Shape type)
Sets the type of shape (e.g.
The class is used as a container of context for various read/write operations on other objects...
Base class for graphical items within a QgsLayout.
double estimatedFrameBleed() const override
Returns the estimated amount the item&#39;s frame bleeds outside the item&#39;s actual rectangle.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
QgsLayoutItemShape(QgsLayout *layout)
Constructor for QgsLayoutItemShape, with the specified parent layout.
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1148
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout&#39;s render context, which stores information relating to the current ...
Definition: qgslayout.cpp:357
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:406
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
QString displayName() const override
Gets item display name.
void frameChanged()
Emitted if the item&#39;s frame style changes.
void sizePositionChanged()
Emitted when the item&#39;s size or position changes.
static QgsLayoutItemShape * create(QgsLayout *layout)
Returns a new shape item for the specified layout.
const QgsLayout * layout() const
Returns the layout the object is attached to.
This class provides a method of storing measurements for use in QGIS layouts using a variety of diffe...
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1842
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:1941
double dpi() const
Returns the dpi for outputting the layout.
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgslayoutitem.h:72
Layout item for basic filled shapes (e.g.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
QPointer< QgsLayout > mLayout
int type() const override
QRectF boundingRect() const override
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
QString id() const
Returns the item&#39;s ID name.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:44
static QgsLayoutMeasurement decodeMeasurement(const QString &string)
Decodes a measurement from a string.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
double length() const
Returns the length of the measurement.
void setSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to draw the shape.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsFillSymbol * symbol()
Returns the fill symbol used to draw the shape.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgssymbol.h:1003
QString encodeMeasurement() const
Encodes the layout measurement to a string.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item&#39;s contents using the specified item render context.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:428