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 "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 );
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 );
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 {
85 
86  update();
87  emit frameChanged();
88 }
89 
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" );
101 
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  drawBackground( painter );
117  drawFrame( painter );
118 
119  if ( isSelected() )
120  {
121  drawSelectionBoxes( painter );
122  }
123 }
124 
125 
126 void QgsComposerShape::drawShape( QPainter* p )
127 {
128  if ( mUseSymbolV2 )
129  {
131  return;
132  }
133 
134  //draw using QPainter brush and pen to keep 2.0 api compatibility
135  p->save();
136  p->setRenderHint( QPainter::Antialiasing );
137 
138  switch ( mShape )
139  {
140  case Ellipse:
141  p->drawEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
142  break;
143  case Rectangle:
144  //if corner radius set, then draw a rounded rectangle
145  if ( mCornerRadius > 0 )
146  {
147  p->drawRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
148  }
149  else
150  {
151  p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
152  }
153  break;
154  case Triangle:
155  QPolygonF triangle;
156  triangle << QPointF( 0, rect().height() );
157  triangle << QPointF( rect().width() , rect().height() );
158  triangle << QPointF( rect().width() / 2.0, 0 );
159  p->drawPolygon( triangle );
160  break;
161  }
162  p->restore();
163 }
164 
166 {
167  p->save();
168  p->setRenderHint( QPainter::Antialiasing );
169 
170  //setup painter scaling to dots so that raster symbology is drawn to scale
171  double dotsPerMM = p->device()->logicalDpiX() / 25.4;
172 
173  //setup render context
175  //context units should be in dots
176  ms.setOutputDpi( p->device()->logicalDpiX() );
178  context.setPainter( p );
179  context.setForceVectorOutput( true );
180  p->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
181 
182  //generate polygon to draw
183  QList<QPolygonF> rings; //empty list
184  QPolygonF shapePolygon;
185 
186  //shapes with curves must be enlarged before conversion to QPolygonF, or
187  //the curves are approximated too much and appear jaggy
188  QTransform t = QTransform::fromScale( 100, 100 );
189  //inverse transform used to scale created polygons back to expected size
190  QTransform ti = t.inverted();
191 
192  switch ( mShape )
193  {
194  case Ellipse:
195  {
196  //create an ellipse
197  QPainterPath ellipsePath;
198  ellipsePath.addEllipse( QRectF( 0, 0 , rect().width() * dotsPerMM, rect().height() * dotsPerMM ) );
199  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
200  shapePolygon = ti.map( ellipsePoly );
201  break;
202  }
203  case Rectangle:
204  {
205  //if corner radius set, then draw a rounded rectangle
206  if ( mCornerRadius > 0 )
207  {
208  QPainterPath roundedRectPath;
209  roundedRectPath.addRoundedRect( QRectF( 0, 0 , rect().width() * dotsPerMM, rect().height() * dotsPerMM ), mCornerRadius * dotsPerMM, mCornerRadius * dotsPerMM );
210  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
211  shapePolygon = ti.map( roundedPoly );
212  }
213  else
214  {
215  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width() * dotsPerMM, rect().height() * dotsPerMM ) );
216  }
217  break;
218  }
219  case Triangle:
220  {
221  shapePolygon << QPointF( 0, rect().height() * dotsPerMM );
222  shapePolygon << QPointF( rect().width() * dotsPerMM, rect().height() * dotsPerMM );
223  shapePolygon << QPointF( rect().width() / 2.0 * dotsPerMM, 0 );
224  shapePolygon << QPointF( 0, rect().height() * dotsPerMM );
225  break;
226  }
227  }
228 
229  mShapeStyleSymbol->startRender( context );
230 
231  //need to render using atlas feature properties?
233  {
234  //using an atlas, so render using current atlas feature
235  //since there may be data defined symbols using atlas feature properties
236  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, mComposition->atlasComposition().currentFeature(), context );
237  }
238  else
239  {
240  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
241  }
242 
243  mShapeStyleSymbol->stopRender( context );
244  p->restore();
245 }
246 
247 
248 void QgsComposerShape::drawFrame( QPainter* p )
249 {
250  if ( mFrame && p && !mUseSymbolV2 )
251  {
252  p->setPen( pen() );
253  p->setBrush( Qt::NoBrush );
254  p->setRenderHint( QPainter::Antialiasing, true );
255  drawShape( p );
256  }
257 }
258 
260 {
261  if ( p && ( mBackground || mUseSymbolV2 ) )
262  {
263  p->setBrush( brush() );//this causes a problem in atlas generation
264  p->setPen( Qt::NoPen );
265  p->setRenderHint( QPainter::Antialiasing, true );
266  drawShape( p );
267  }
268 }
269 
271 {
272  return mMaxSymbolBleed;
273 }
274 
275 bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
276 {
277  QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
278  composerShapeElem.setAttribute( "shapeType", mShape );
279  composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
280 
281  QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
282  composerShapeElem.appendChild( shapeStyleElem );
283 
284  elem.appendChild( composerShapeElem );
285  return _writeXML( composerShapeElem, doc );
286 }
287 
288 bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc )
289 {
290  mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() );
291  mCornerRadius = itemElem.attribute( "cornerRadius", "0" ).toDouble();
292 
293  //restore general composer item properties
294  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
295  if ( composerItemList.size() > 0 )
296  {
297  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
298 
299  //rotation
300  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
301  {
302  //check for old (pre 2.1) rotation attribute
303  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
304  }
305 
306  _readXML( composerItemElem, doc );
307  }
308 
309  QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
310  if ( !shapeStyleSymbolElem.isNull() )
311  {
312  delete mShapeStyleSymbol;
313  mShapeStyleSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( shapeStyleSymbolElem ) );
314  }
315  else
316  {
317  //upgrade project file from 2.0 to use symbolV2 styling
318  delete mShapeStyleSymbol;
319  QgsStringMap properties;
320  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
321  if ( hasBackground() )
322  {
323  properties.insert( "style", "solid" );
324  }
325  else
326  {
327  properties.insert( "style", "no" );
328  }
329  if ( hasFrame() )
330  {
331  properties.insert( "style_border", "solid" );
332  }
333  else
334  {
335  properties.insert( "style_border", "no" );
336  }
337  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
338  properties.insert( "width_border", QString::number( pen().widthF() ) );
339 
340  //for pre 2.0 projects, shape color and outline were specified in a different element...
341  QDomNodeList outlineColorList = itemElem.elementsByTagName( "OutlineColor" );
342  if ( outlineColorList.size() > 0 )
343  {
344  QDomElement frameColorElem = outlineColorList.at( 0 ).toElement();
345  bool redOk, greenOk, blueOk, alphaOk, widthOk;
346  int penRed, penGreen, penBlue, penAlpha;
347  double penWidth;
348 
349  penWidth = itemElem.attribute( "outlineWidth" ).toDouble( &widthOk );
350  penRed = frameColorElem.attribute( "red" ).toDouble( &redOk );
351  penGreen = frameColorElem.attribute( "green" ).toDouble( &greenOk );
352  penBlue = frameColorElem.attribute( "blue" ).toDouble( &blueOk );
353  penAlpha = frameColorElem.attribute( "alpha" ).toDouble( &alphaOk );
354 
355  if ( redOk && greenOk && blueOk && alphaOk && widthOk )
356  {
357  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( QColor( penRed, penGreen, penBlue, penAlpha ) ) );
358  properties.insert( "width_border", QString::number( penWidth ) );
359  }
360  }
361  QDomNodeList fillColorList = itemElem.elementsByTagName( "FillColor" );
362  if ( fillColorList.size() > 0 )
363  {
364  QDomElement fillColorElem = fillColorList.at( 0 ).toElement();
365  bool redOk, greenOk, blueOk, alphaOk;
366  int fillRed, fillGreen, fillBlue, fillAlpha;
367 
368  fillRed = fillColorElem.attribute( "red" ).toDouble( &redOk );
369  fillGreen = fillColorElem.attribute( "green" ).toDouble( &greenOk );
370  fillBlue = fillColorElem.attribute( "blue" ).toDouble( &blueOk );
371  fillAlpha = fillColorElem.attribute( "alpha" ).toDouble( &alphaOk );
372 
373  if ( redOk && greenOk && blueOk && alphaOk )
374  {
375  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( QColor( fillRed, fillGreen, fillBlue, fillAlpha ) ) );
376  properties.insert( "style", "solid" );
377  }
378  }
379  if ( itemElem.hasAttribute( "transparentFill" ) )
380  {
381  //old style (pre 2.0) of specifying that shapes had no fill
382  bool hasOldTransparentFill = itemElem.attribute( "transparentFill", "0" ).toInt();
383  if ( hasOldTransparentFill )
384  {
385  properties.insert( "style", "no" );
386  }
387  }
388 
390  }
391  emit itemChanged();
392  return true;
393 }
394 
396 {
397  if ( s == mShape )
398  {
399  return;
400  }
401 
402  mShape = s;
403 
404  if ( mComposition && id().isEmpty() )
405  {
406  //notify the model that the display name has changed
408  }
409 }
410 
412 {
413  mCornerRadius = radius;
414 }
415 
417 {
418  return mCurrentRectangle;
419 }
420 
422 {
423  QRectF rectangle = rect();
425  if ( rectangle != mCurrentRectangle )
426  {
427  prepareGeometryChange();
428  mCurrentRectangle = rectangle;
429  }
430 }
431 
432 void QgsComposerShape::setSceneRect( const QRectF& rectangle )
433 {
434  // Reimplemented from QgsComposerItem as we need to call updateBoundingRect after the shape's size changes
435 
436  //update rect for data defined size and position
437  QRectF evaluatedRect = evalItemRect( rectangle );
438  QgsComposerItem::setSceneRect( evaluatedRect );
439 
441  update();
442 }
443 
445 {
446  if ( !id().isEmpty() )
447  {
448  return id();
449  }
450 
451  switch ( mShape )
452  {
453  case Ellipse:
454  return tr( "<ellipse>" );
455  break;
456  case Rectangle:
457  return tr( "<rectangle>" );
458  break;
459  case Triangle:
460  return tr( "<triangle>" );
461  break;
462  }
463 
464  return tr( "<shape>" );
465 }
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.
void setShapeType(QgsComposerShape::Shape s)
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.
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.
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
void updateItemDisplayName(QgsComposerItem *item)
Must be called when an item's display name is modified.
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)
Draws additional graphics on selected items.
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)