QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 <QPainter>
23 
25  mShape( Ellipse ),
26  mCornerRadius( 0 ),
27  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
28  mShapeStyleSymbol( 0 ),
29  mMaxSymbolBleed( 0 )
30 {
31  setFrameEnabled( true );
33 
34  if ( mComposition )
35  {
36  //connect to atlas feature changes
37  //to update symbol style (in case of data-defined symbology)
38  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( repaint() ) );
39  }
40 }
41 
42 QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ):
43  QgsComposerItem( x, y, width, height, composition ),
44  mShape( Ellipse ),
45  mCornerRadius( 0 ),
46  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
47  mShapeStyleSymbol( 0 ),
48  mMaxSymbolBleed( 0 )
49 {
50  setSceneRect( QRectF( x, y, width, height ) );
51  setFrameEnabled( true );
53 
54  if ( mComposition )
55  {
56  //connect to atlas feature changes
57  //to update symbol style (in case of data-defined symbology)
58  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( repaint() ) );
59  }
60 }
61 
63 {
64  delete mShapeStyleSymbol;
65 }
66 
67 void QgsComposerShape::setUseSymbolV2( bool useSymbolV2 )
68 {
69  mUseSymbolV2 = useSymbolV2;
70  setFrameEnabled( !useSymbolV2 );
71 }
72 
74 {
75  delete mShapeStyleSymbol;
76  mShapeStyleSymbol = symbol;
77  refreshSymbol();
78 }
79 
81 {
84 
85  update();
86  emit frameChanged();
87 }
88 
90 {
91  delete mShapeStyleSymbol;
92  QgsStringMap properties;
93  properties.insert( "color", "white" );
94  properties.insert( "style", "solid" );
95  properties.insert( "style_border", "solid" );
96  properties.insert( "color_border", "black" );
97  properties.insert( "width_border", "0.3" );
98  properties.insert( "joinstyle", "miter" );
100 
103 
104  emit frameChanged();
105 }
106 
107 void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
108 {
109  Q_UNUSED( itemStyle );
110  Q_UNUSED( pWidget );
111  if ( !painter )
112  {
113  return;
114  }
115  drawBackground( painter );
116  drawFrame( painter );
117 
118  if ( isSelected() )
119  {
120  drawSelectionBoxes( painter );
121  }
122 }
123 
124 
125 void QgsComposerShape::drawShape( QPainter* p )
126 {
127  if ( mUseSymbolV2 )
128  {
130  return;
131  }
132 
133  //draw using QPainter brush and pen to keep 2.0 api compatibility
134  p->save();
135  p->setRenderHint( QPainter::Antialiasing );
136 
137  switch ( mShape )
138  {
139  case Ellipse:
140  p->drawEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
141  break;
142  case Rectangle:
143  //if corner radius set, then draw a rounded rectangle
144  if ( mCornerRadius > 0 )
145  {
146  p->drawRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
147  }
148  else
149  {
150  p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
151  }
152  break;
153  case Triangle:
154  QPolygonF triangle;
155  triangle << QPointF( 0, rect().height() );
156  triangle << QPointF( rect().width() , rect().height() );
157  triangle << QPointF( rect().width() / 2.0, 0 );
158  p->drawPolygon( triangle );
159  break;
160  }
161  p->restore();
162 }
163 
165 {
166  p->save();
167  p->setRenderHint( QPainter::Antialiasing );
168 
169  //setup painter scaling to dots so that raster symbology is drawn to scale
170  double dotsPerMM = p->device()->logicalDpiX() / 25.4;
171 
172  //setup render context
174  //context units should be in dots
175  ms.setOutputDpi( p->device()->logicalDpiX() );
177  context.setPainter( p );
178  context.setForceVectorOutput( true );
179  p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
180 
181  //generate polygon to draw
182  QList<QPolygonF> rings; //empty list
183  QPolygonF shapePolygon;
184 
185  //shapes with curves must be enlarged before conversion to QPolygonF, or
186  //the curves are approximated too much and appear jaggy
187  QTransform t = QTransform::fromScale( 100, 100 );
188  //inverse transform used to scale created polygons back to expected size
189  QTransform ti = t.inverted();
190 
191  switch ( mShape )
192  {
193  case Ellipse:
194  {
195  //create an ellipse
196  QPainterPath ellipsePath;
197  ellipsePath.addEllipse( QRectF( 0, 0 , rect().width() * dotsPerMM, rect().height() * dotsPerMM ) );
198  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
199  shapePolygon = ti.map( ellipsePoly );
200  break;
201  }
202  case Rectangle:
203  {
204  //if corner radius set, then draw a rounded rectangle
205  if ( mCornerRadius > 0 )
206  {
207  QPainterPath roundedRectPath;
208  roundedRectPath.addRoundedRect( QRectF( 0, 0 , rect().width() * dotsPerMM, rect().height() * dotsPerMM ), mCornerRadius * dotsPerMM, mCornerRadius * dotsPerMM );
209  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
210  shapePolygon = ti.map( roundedPoly );
211  }
212  else
213  {
214  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * dotsPerMM, rect().height() * dotsPerMM ) );
215  }
216  break;
217  }
218  case Triangle:
219  {
220  shapePolygon << QPointF( 0, rect().height() * dotsPerMM );
221  shapePolygon << QPointF( rect().width() * dotsPerMM, rect().height() * dotsPerMM );
222  shapePolygon << QPointF( rect().width() / 2.0 * dotsPerMM, 0 );
223  shapePolygon << QPointF( 0, rect().height() * dotsPerMM );
224  break;
225  }
226  }
227 
228  mShapeStyleSymbol->startRender( context );
229 
230  //need to render using atlas feature properties?
232  {
233  //using an atlas, so render using current atlas feature
234  //since there may be data defined symbols using atlas feature properties
235  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, mComposition->atlasComposition().currentFeature(), context );
236  }
237  else
238  {
239  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
240  }
241 
242  mShapeStyleSymbol->stopRender( context );
243  p->restore();
244 }
245 
246 
247 void QgsComposerShape::drawFrame( QPainter* p )
248 {
249  if ( mFrame && p && !mUseSymbolV2 )
250  {
251  p->setPen( pen() );
252  p->setBrush( Qt::NoBrush );
253  p->setRenderHint( QPainter::Antialiasing, true );
254  drawShape( p );
255  }
256 }
257 
259 {
260  if ( p && ( mBackground || mUseSymbolV2 ) )
261  {
262  p->setBrush( brush() );//this causes a problem in atlas generation
263  p->setPen( Qt::NoPen );
264  p->setRenderHint( QPainter::Antialiasing, true );
265  drawShape( p );
266  }
267 }
268 
270 {
271  return mMaxSymbolBleed;
272 }
273 
274 bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
275 {
276  QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
277  composerShapeElem.setAttribute( "shapeType", mShape );
278  composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
279 
280  QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
281  composerShapeElem.appendChild( shapeStyleElem );
282 
283  elem.appendChild( composerShapeElem );
284  return _writeXML( composerShapeElem, doc );
285 }
286 
287 bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc )
288 {
289  mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() );
290  mCornerRadius = itemElem.attribute( "cornerRadius", "0" ).toDouble();
291 
292  //restore general composer item properties
293  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
294  if ( composerItemList.size() > 0 )
295  {
296  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
297 
298  //rotation
299  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
300  {
301  //check for old (pre 2.1) rotation attribute
302  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
303  }
304 
305  _readXML( composerItemElem, doc );
306  }
307 
308  QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
309  if ( !shapeStyleSymbolElem.isNull() )
310  {
311  delete mShapeStyleSymbol;
312  mShapeStyleSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( shapeStyleSymbolElem ) );
313  }
314  else
315  {
316  //upgrade project file from 2.0 to use symbolV2 styling
317  delete mShapeStyleSymbol;
318  QgsStringMap properties;
319  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
320  if ( hasBackground() )
321  {
322  properties.insert( "style", "solid" );
323  }
324  else
325  {
326  properties.insert( "style", "no" );
327  }
328  if ( hasFrame() )
329  {
330  properties.insert( "style_border", "solid" );
331  }
332  else
333  {
334  properties.insert( "style_border", "no" );
335  }
336  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
337  properties.insert( "width_border", QString::number( pen().widthF() ) );
338 
339  //for pre 2.0 projects, shape color and outline were specified in a different element...
340  QDomNodeList outlineColorList = itemElem.elementsByTagName( "OutlineColor" );
341  if ( outlineColorList.size() > 0 )
342  {
343  QDomElement frameColorElem = outlineColorList.at( 0 ).toElement();
344  bool redOk, greenOk, blueOk, alphaOk, widthOk;
345  int penRed, penGreen, penBlue, penAlpha;
346  double penWidth;
347 
348  penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
349  penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
350  penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
351  penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
352  penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
353 
354  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
355  {
356  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( QColor( penRed, penGreen, penBlue, penAlpha ) ) );
357  properties.insert( "width_border", QString::number( penWidth ) );
358  }
359  }
360  QDomNodeList fillColorList = itemElem.elementsByTagName( "FillColor" );
361  if ( fillColorList.size() > 0 )
362  {
363  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
364  bool redOk, greenOk, blueOk, alphaOk;
365  int fillRed, fillGreen, fillBlue, fillAlpha;
366 
367  fillRed = fillColorElem.attribute( "red" ).toDouble( &redOk );
368  fillGreen = fillColorElem.attribute( "green" ).toDouble( &greenOk );
369  fillBlue = fillColorElem.attribute( "blue" ).toDouble( &blueOk );
370  fillAlpha = fillColorElem.attribute( "alpha" ).toDouble( &alphaOk );
371 
372  if ( redOk && greenOk && blueOk && alphaOk )
373  {
374  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) ) );
375  properties.insert( "style", "solid" );
376  }
377  }
378  if ( itemElem.hasAttribute( "transparentFill" ) )
379  {
380  //old style (pre 2.0) of specifying that shapes had no fill
381  bool hasOldTransparentFill = itemElem.attribute( "transparentFill", "0" ).toInt();
382  if ( hasOldTransparentFill )
383  {
384  properties.insert( "style", "no" );
385  }
386  }
387 
389  }
390  emit itemChanged();
391  return true;
392 }
393 
395 {
396  mCornerRadius = radius;
397 }
398 
400 {
401  return mCurrentRectangle;
402 }
403 
405 {
406  QRectF rectangle = rect();
408  if ( rectangle != mCurrentRectangle )
409  {
410  prepareGeometryChange();
411  mCurrentRectangle = rectangle;
412  }
413 }
414 
415 void QgsComposerShape::setSceneRect( const QRectF& rectangle )
416 {
417  // Reimplemented from QgsComposerItem as we need to call updateBoundingRect after the shape's size changes
418 
419  //update rect for data defined size and position
420  QRectF evaluatedRect = evalItemRect( rectangle );
421  QgsComposerItem::setSceneRect( evaluatedRect );
422 
424  update();
425 }
426 
428 {
429  if ( !id().isEmpty() )
430  {
431  return id();
432  }
433 
434  switch ( mShape )
435  {
436  case Ellipse:
437  return tr( "<ellipse>" );
438  break;
439  case Rectangle:
440  return tr( "<rectangle>" );
441  break;
442  case Triangle:
443  return tr( "<triangle>" );
444  break;
445  }
446 
447  return tr( "<shape>" );
448 }
QRectF mCurrentRectangle
Current bounding rectangle of shape.
void setShapeStyleSymbol(QgsFillSymbolV2 *symbol)
Sets the QgsFillSymbolV2 used to draw the shape.
void setForceVectorOutput(bool force)
Added in QGIS v1.5.
QgsComposition::AtlasMode atlasMode() const
Returns the current atlas mode of the composition.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
void drawShapeUsingSymbol(QPainter *p)
void updateBoundingRect()
Updates the bounding rect of this item.
Shape mShape
Ellipse, rectangle or triangle.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
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.
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
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 createDefaultShapeStyleSymbol()
bool enabled() const
Returns whether the atlas generation is enabled.
void setFrameEnabled(const bool drawFrame)
Set whether this item has a frame drawn around it or not.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
bool writeXML(QDomElement &elem, QDomDocument &doc) const
stores state in Dom element
QMap< QString, QString > QgsStringMap
Definition: qgis.h:416
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.
static QDomElement saveSymbol(QString symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
void frameChanged()
Emitted if the item's frame style changes.
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget)
Reimplementation of QCanvasItem::paint - draw on canvas.
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
virtual void drawSelectionBoxes(QPainter *p)
Draw selection boxes around item.
QgsFillSymbolV2 * mShapeStyleSymbol
void setCornerRadius(double radius)
Sets radius for rounded rectangle corners.
void setUseSymbolV2(bool useSymbolV2)
Controls whether the shape should be drawn using a QgsFillSymbolV2.
bool mFrame
True if item fram needs to be painted.
void drawShape(QPainter *p)
void setPainter(QPainter *p)
static double estimateMaxSymbolBleed(QgsSymbolV2 *symbol)
Returns the maximum estimated bleed for the symbol.
virtual double estimatedFrameBleed() const
reimplement estimatedFrameBleed, since frames on shapes are drawn using symbology rather than the ite...
QRectF evalItemRect(const QRectF &newRect)
Update an item rect to consider data defined position and size of item.
virtual QString displayName() const
Get item display name.
Graphics scene for map printing.
QgsFeature * currentFeature()
Returns the current atlas feature.
void refreshSymbol()
Should be called after the shape's symbol is changed.
QgsComposition * mComposition
Contains information about the context of a rendering operation.
void stopRender(QgsRenderContext &context)
QRectF boundingRect() const
Depending on the symbol style, the bounding rectangle can be larger than the shape.
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
virtual void drawBackground(QPainter *p)
Draw background.
bool readXML(const QDomElement &itemElem, const QDomDocument &doc)
sets state from Dom 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.
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.
QgsAtlasComposition & atlasComposition()
static QgsSymbolV2 * loadSymbol(QDomElement &element)
bool mBackground
True if item background needs to be painted.
QgsComposerShape(QgsComposition *composition)
void setSceneRect(const QRectF &rectangle)
Sets new scene rectangle bounds and recalculates hight and extent.
#define tr(sourceText)
QString id() const
Get item's id (which is not necessarly unique)