QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgscomposershape.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposershape.cpp
3  ----------------------
4  begin : November 2009
5  copyright : (C) 2009 by Marco Hugentobler
6  email : marco@hugis.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgscomposershape.h"
19 #include "qgscomposition.h"
20 #include "qgssymbolv2.h"
21 #include "qgssymbollayerv2utils.h"
22 #include "qgscomposermodel.h"
23 #include <QPainter>
24 
26  mShape( Ellipse ),
27  mCornerRadius( 0 ),
28  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
29  mShapeStyleSymbol( 0 ),
30  mMaxSymbolBleed( 0 )
31 {
32  setFrameEnabled( true );
33  createDefaultShapeStyleSymbol();
34 
35  if ( mComposition )
36  {
37  //connect to atlas feature changes
38  //to update symbol style (in case of data-defined symbology)
39  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( repaint() ) );
40  }
41 }
42 
43 QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ):
44  QgsComposerItem( x, y, width, height, composition ),
45  mShape( Ellipse ),
46  mCornerRadius( 0 ),
47  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
48  mShapeStyleSymbol( 0 ),
49  mMaxSymbolBleed( 0 )
50 {
51  setSceneRect( QRectF( x, y, width, height ) );
52  setFrameEnabled( true );
53  createDefaultShapeStyleSymbol();
54 
55  if ( mComposition )
56  {
57  //connect to atlas feature changes
58  //to update symbol style (in case of data-defined symbology)
59  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( repaint() ) );
60  }
61 }
62 
64 {
65  delete mShapeStyleSymbol;
66 }
67 
68 void QgsComposerShape::setUseSymbolV2( bool useSymbolV2 )
69 {
70  mUseSymbolV2 = useSymbolV2;
71  setFrameEnabled( !useSymbolV2 );
72 }
73 
75 {
76  delete mShapeStyleSymbol;
77  mShapeStyleSymbol = symbol;
78  refreshSymbol();
79 }
80 
82 {
83  mMaxSymbolBleed = QgsSymbolLayerV2Utils::estimateMaxSymbolBleed( mShapeStyleSymbol );
84  updateBoundingRect();
85 
86  update();
87  emit frameChanged();
88 }
89 
90 void QgsComposerShape::createDefaultShapeStyleSymbol()
91 {
92  delete mShapeStyleSymbol;
93  QgsStringMap properties;
94  properties.insert( "color", "white" );
95  properties.insert( "style", "solid" );
96  properties.insert( "style_border", "solid" );
97  properties.insert( "color_border", "black" );
98  properties.insert( "width_border", "0.3" );
99  properties.insert( "joinstyle", "miter" );
100  mShapeStyleSymbol = QgsFillSymbolV2::createSimple( properties );
101 
102  mMaxSymbolBleed = QgsSymbolLayerV2Utils::estimateMaxSymbolBleed( mShapeStyleSymbol );
103  updateBoundingRect();
104 
105  emit frameChanged();
106 }
107 
108 void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
109 {
110  Q_UNUSED( itemStyle );
111  Q_UNUSED( pWidget );
112  if ( !painter )
113  {
114  return;
115  }
116  if ( !shouldDrawItem() )
117  {
118  return;
119  }
120 
121  drawBackground( painter );
122  drawFrame( painter );
123 
124  if ( isSelected() )
125  {
126  drawSelectionBoxes( painter );
127  }
128 }
129 
130 
131 void QgsComposerShape::drawShape( QPainter* p )
132 {
133  if ( mUseSymbolV2 )
134  {
135  drawShapeUsingSymbol( p );
136  return;
137  }
138 
139  //draw using QPainter brush and pen to keep 2.0 api compatibility
140  p->save();
141  p->setRenderHint( QPainter::Antialiasing );
142 
143  switch ( mShape )
144  {
145  case Ellipse:
146  p->drawEllipse( QRectF( 0, 0, rect().width(), rect().height() ) );
147  break;
148  case Rectangle:
149  //if corner radius set, then draw a rounded rectangle
150  if ( mCornerRadius > 0 )
151  {
152  p->drawRoundedRect( QRectF( 0, 0, rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
153  }
154  else
155  {
156  p->drawRect( QRectF( 0, 0, rect().width(), rect().height() ) );
157  }
158  break;
159  case Triangle:
160  QPolygonF triangle;
161  triangle << QPointF( 0, rect().height() );
162  triangle << QPointF( rect().width(), rect().height() );
163  triangle << QPointF( rect().width() / 2.0, 0 );
164  p->drawPolygon( triangle );
165  break;
166  }
167  p->restore();
168 }
169 
170 void QgsComposerShape::drawShapeUsingSymbol( QPainter* p )
171 {
172  p->save();
173  p->setRenderHint( QPainter::Antialiasing );
174 
175  //setup painter scaling to dots so that raster symbology is drawn to scale
176  double dotsPerMM = p->device()->logicalDpiX() / 25.4;
177 
178  //setup render context
180  //context units should be in dots
181  ms.setOutputDpi( p->device()->logicalDpiX() );
183  context.setPainter( p );
184  context.setForceVectorOutput( true );
185  p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
186 
187  //generate polygon to draw
188  QList<QPolygonF> rings; //empty list
189  QPolygonF shapePolygon;
190 
191  //shapes with curves must be enlarged before conversion to QPolygonF, or
192  //the curves are approximated too much and appear jaggy
193  QTransform t = QTransform::fromScale( 100, 100 );
194  //inverse transform used to scale created polygons back to expected size
195  QTransform ti = t.inverted();
196 
197  switch ( mShape )
198  {
199  case Ellipse:
200  {
201  //create an ellipse
202  QPainterPath ellipsePath;
203  ellipsePath.addEllipse( QRectF( 0, 0, rect().width() * dotsPerMM, rect().height() * dotsPerMM ) );
204  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
205  shapePolygon = ti.map( ellipsePoly );
206  break;
207  }
208  case Rectangle:
209  {
210  //if corner radius set, then draw a rounded rectangle
211  if ( mCornerRadius > 0 )
212  {
213  QPainterPath roundedRectPath;
214  roundedRectPath.addRoundedRect( QRectF( 0, 0, rect().width() * dotsPerMM, rect().height() * dotsPerMM ), mCornerRadius * dotsPerMM, mCornerRadius * dotsPerMM );
215  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
216  shapePolygon = ti.map( roundedPoly );
217  }
218  else
219  {
220  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * dotsPerMM, rect().height() * dotsPerMM ) );
221  }
222  break;
223  }
224  case Triangle:
225  {
226  shapePolygon << QPointF( 0, rect().height() * dotsPerMM );
227  shapePolygon << QPointF( rect().width() * dotsPerMM, rect().height() * dotsPerMM );
228  shapePolygon << QPointF( rect().width() / 2.0 * dotsPerMM, 0 );
229  shapePolygon << QPointF( 0, rect().height() * dotsPerMM );
230  break;
231  }
232  }
233 
234  mShapeStyleSymbol->startRender( context );
235 
236  //need to render using atlas feature properties?
238  {
239  //using an atlas, so render using current atlas feature
240  //since there may be data defined symbols using atlas feature properties
241  QgsFeature atlasFeature = mComposition->atlasComposition().feature();
242  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, &atlasFeature, context );
243  }
244  else
245  {
246  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
247  }
248 
249  mShapeStyleSymbol->stopRender( context );
250  p->restore();
251 }
252 
253 
255 {
256  if ( mFrame && p && !mUseSymbolV2 )
257  {
258  p->setPen( pen() );
259  p->setBrush( Qt::NoBrush );
260  p->setRenderHint( QPainter::Antialiasing, true );
261  drawShape( p );
262  }
263 }
264 
266 {
267  if ( p && ( mBackground || mUseSymbolV2 ) )
268  {
269  p->setBrush( brush() );//this causes a problem in atlas generation
270  p->setPen( Qt::NoPen );
271  p->setRenderHint( QPainter::Antialiasing, true );
272  drawShape( p );
273  }
274 }
275 
277 {
278  return mMaxSymbolBleed;
279 }
280 
282 {
283  QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
284  composerShapeElem.setAttribute( "shapeType", mShape );
285  composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
286 
287  QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
288  composerShapeElem.appendChild( shapeStyleElem );
289 
290  elem.appendChild( composerShapeElem );
291  return _writeXML( composerShapeElem, doc );
292 }
293 
294 bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc )
295 {
296  mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() );
297  mCornerRadius = itemElem.attribute( "cornerRadius", "0" ).toDouble();
298 
299  //restore general composer item properties
300  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
301  if ( composerItemList.size() > 0 )
302  {
303  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
304 
305  //rotation
306  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
307  {
308  //check for old (pre 2.1) rotation attribute
309  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
310  }
311 
312  _readXML( composerItemElem, doc );
313  }
314 
315  QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
316  if ( !shapeStyleSymbolElem.isNull() )
317  {
318  delete mShapeStyleSymbol;
319  mShapeStyleSymbol = QgsSymbolLayerV2Utils::loadSymbol<QgsFillSymbolV2>( shapeStyleSymbolElem );
320  }
321  else
322  {
323  //upgrade project file from 2.0 to use symbolV2 styling
324  delete mShapeStyleSymbol;
325  QgsStringMap properties;
326  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
327  if ( hasBackground() )
328  {
329  properties.insert( "style", "solid" );
330  }
331  else
332  {
333  properties.insert( "style", "no" );
334  }
335  if ( hasFrame() )
336  {
337  properties.insert( "style_border", "solid" );
338  }
339  else
340  {
341  properties.insert( "style_border", "no" );
342  }
343  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
344  properties.insert( "width_border", QString::number( pen().widthF() ) );
345 
346  //for pre 2.0 projects, shape color and outline were specified in a different element...
347  QDomNodeList outlineColorList = itemElem.elementsByTagName( "OutlineColor" );
348  if ( outlineColorList.size() > 0 )
349  {
350  QDomElement frameColorElem = outlineColorList.at( 0 ).toElement();
351  bool redOk, greenOk, blueOk, alphaOk, widthOk;
352  int penRed, penGreen, penBlue, penAlpha;
353  double penWidth;
354 
355  penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
356  penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
357  penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
358  penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
359  penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
360 
361  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
362  {
363  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( QColor( penRed, penGreen, penBlue, penAlpha ) ) );
364  properties.insert( "width_border", QString::number( penWidth ) );
365  }
366  }
367  QDomNodeList fillColorList = itemElem.elementsByTagName( "FillColor" );
368  if ( fillColorList.size() > 0 )
369  {
370  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
371  bool redOk, greenOk, blueOk, alphaOk;
372  int fillRed, fillGreen, fillBlue, fillAlpha;
373 
374  fillRed = fillColorElem.attribute( "red" ).toDouble( &redOk );
375  fillGreen = fillColorElem.attribute( "green" ).toDouble( &greenOk );
376  fillBlue = fillColorElem.attribute( "blue" ).toDouble( &blueOk );
377  fillAlpha = fillColorElem.attribute( "alpha" ).toDouble( &alphaOk );
378 
379  if ( redOk && greenOk && blueOk && alphaOk )
380  {
381  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) ) );
382  properties.insert( "style", "solid" );
383  }
384  }
385  if ( itemElem.hasAttribute( "transparentFill" ) )
386  {
387  //old style (pre 2.0) of specifying that shapes had no fill
388  bool hasOldTransparentFill = itemElem.attribute( "transparentFill", "0" ).toInt();
389  if ( hasOldTransparentFill )
390  {
391  properties.insert( "style", "no" );
392  }
393  }
394 
395  mShapeStyleSymbol = QgsFillSymbolV2::createSimple( properties );
396  }
397  emit itemChanged();
398  return true;
399 }
400 
402 {
403  if ( s == mShape )
404  {
405  return;
406  }
407 
408  mShape = s;
409 
410  if ( mComposition && id().isEmpty() )
411  {
412  //notify the model that the display name has changed
414  }
415 }
416 
418 {
419  mCornerRadius = radius;
420 }
421 
423 {
424  return mCurrentRectangle;
425 }
426 
427 void QgsComposerShape::updateBoundingRect()
428 {
429  QRectF rectangle = rect();
430  rectangle.adjust( -mMaxSymbolBleed, -mMaxSymbolBleed, mMaxSymbolBleed, mMaxSymbolBleed );
431  if ( rectangle != mCurrentRectangle )
432  {
434  mCurrentRectangle = rectangle;
435  }
436 }
437 
438 void QgsComposerShape::setSceneRect( const QRectF& rectangle )
439 {
440  // Reimplemented from QgsComposerItem as we need to call updateBoundingRect after the shape's size changes
441 
442  //update rect for data defined size and position
443  QRectF evaluatedRect = evalItemRect( rectangle );
444  QgsComposerItem::setSceneRect( evaluatedRect );
445 
446  updateBoundingRect();
447  update();
448 }
449 
451 {
452  if ( !id().isEmpty() )
453  {
454  return id();
455  }
456 
457  switch ( mShape )
458  {
459  case Ellipse:
460  return tr( "<ellipse>" );
461  break;
462  case Rectangle:
463  return tr( "<rectangle>" );
464  break;
465  case Triangle:
466  return tr( "<triangle>" );
467  break;
468  }
469 
470  return tr( "<shape>" );
471 }
void addEllipse(const QRectF &boundingRectangle)
void setShapeStyleSymbol(QgsFillSymbolV2 *symbol)
Sets the QgsFillSymbolV2 used to draw the shape.
void setForceVectorOutput(bool force)
void setShapeType(QgsComposerShape::Shape s)
QgsComposition::AtlasMode atlasMode() const
Returns the current atlas mode of the composition.
QDomNodeList elementsByTagName(const QString &tagname) const
void setSceneRect(const QRectF &rectangle) override
Sets new scene rectangle bounds and recalculates hight and extent.
QTransform fromScale(qreal sx, qreal sy)
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
virtual QString displayName() const override
Get item display name.
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
QString attribute(const QString &name, const QString &defValue) const
QgsComposerModel * itemsModel()
Returns the items model attached to the composition.
void setOutputDpi(int dpi)
Set DPI used for conversion between real world units (e.g. mm) and pixels.
void itemChanged()
Emitted when the item changes.
QPoint map(const QPoint &point) const
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
void scale(qreal sx, qreal sy)
static QgsFillSymbolV2 * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
A item that forms part of a map composition.
void addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
void save()
bool enabled() const
Returns whether the atlas generation is enabled.
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QgsFeature feature() const
Returns the current atlas feature.
virtual void setFrameEnabled(const bool drawFrame)
Set whether this item has a frame drawn around it or not.
QPolygonF toFillPolygon(const QMatrix &matrix) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:162
double toDouble(bool *ok) const
QString tr(const char *sourceText, const char *disambiguation, int n)
void adjust(qreal dx1, qreal dy1, qreal dx2, qreal dy2)
virtual void drawBackground(QPainter *p) override
Draw background.
void update(const QRectF &rect)
void updateItemDisplayName(QgsComposerItem *item)
Must be called when an item's display name is modified.
QTransform inverted(bool *invertible) const
static QString encodeColor(QColor color)
The QgsMapSettings class contains configuration for rendering of the map.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
QDomElement toElement() const
static QDomElement saveSymbol(QString symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
void drawRect(const QRectF &rectangle)
void frameChanged()
Emitted if the item's frame style changes.
QString number(int n, int base)
bool hasAttribute(const QString &name) const
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
virtual void drawSelectionBoxes(QPainter *p)
Draws additional graphics on selected items.
void setCornerRadius(double radius)
Sets radius for rounded rectangle corners.
void setUseSymbolV2(bool useSymbolV2)
Controls whether the shape should be drawn using a QgsFillSymbolV2.
void setPen(const QColor &color)
bool mFrame
True if item fram needs to be painted.
void drawEllipse(const QRectF &rectangle)
void setAttribute(const QString &name, const QString &value)
bool isSelected() const
void drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
int toInt(bool *ok, int base) const
virtual void drawFrame(QPainter *p) override
Draw black frame around item.
QRectF evalItemRect(const QRectF &newRect, const bool resizeOnly=false, const QgsExpressionContext *context=0)
Evaluates an item's bounding rect to consider data defined position and size of item and reference po...
QPaintDevice * device() const
void setBrush(const QBrush &brush)
void setPainter(QPainter *p)
void repaint() override
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
static double estimateMaxSymbolBleed(QgsSymbolV2 *symbol)
Returns the maximum estimated bleed for the symbol.
void prepareGeometryChange()
Graphics scene for map printing.
int logicalDpiX() const
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void refreshSymbol()
Should be called after the shape's symbol is changed.
bool isNull() const
void restore()
bool writeXML(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom element.
QgsComposition * mComposition
Contains information about the context of a rendering operation.
void stopRender(QgsRenderContext &context)
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
virtual void setItemRotation(const double r, const bool adjustPosition=false)
Sets the item rotation.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
bool hasFrame() const
Whether this item has a frame or not.
QDomElement firstChildElement(const QString &tagName) const
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
bool hasBackground() const
Whether this item has a Background or not.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint - draw on canvas.
QgsAtlasComposition & atlasComposition()
iterator insert(const Key &key, const T &value)
int size() const
QDomElement createElement(const QString &tagName)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool mBackground
True if item background needs to be painted.
virtual double estimatedFrameBleed() const override
Reimplement estimatedFrameBleed, since frames on shapes are drawn using symbology rather than the ite...
QRectF boundingRect() const override
Depending on the symbol style, the bounding rectangle can be larger than the shape.
QgsComposerShape(QgsComposition *composition)
QDomNode at(int index) const
QRectF rect() const
QString id() const
Get item's id (which is not necessarly unique)