QGIS API Documentation  2.3.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 
35 QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ):
36  QgsComposerItem( x, y, width, height, composition ),
37  mShape( Ellipse ),
38  mCornerRadius( 0 ),
39  mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
40  mShapeStyleSymbol( 0 ),
41  mMaxSymbolBleed( 0 )
42 {
43  setSceneRect( QRectF( x, y, width, height ) );
44  setFrameEnabled( true );
46 }
47 
49 {
50  delete mShapeStyleSymbol;
51 }
52 
53 void QgsComposerShape::setUseSymbolV2( bool useSymbolV2 )
54 {
55  mUseSymbolV2 = useSymbolV2;
56  setFrameEnabled( !useSymbolV2 );
57 }
58 
60 {
61  delete mShapeStyleSymbol;
62  mShapeStyleSymbol = symbol;
63  refreshSymbol();
64 }
65 
67 {
70 
71  update();
72  emit frameChanged();
73 }
74 
76 {
77  delete mShapeStyleSymbol;
78  QgsStringMap properties;
79  properties.insert( "color", "white" );
80  properties.insert( "style", "solid" );
81  properties.insert( "style_border", "solid" );
82  properties.insert( "color_border", "black" );
83  properties.insert( "width_border", "0.3" );
84  properties.insert( "joinstyle", "miter" );
86 
89 
90  emit frameChanged();
91 }
92 
93 void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
94 {
95  Q_UNUSED( itemStyle );
96  Q_UNUSED( pWidget );
97  if ( !painter )
98  {
99  return;
100  }
101  drawBackground( painter );
102  drawFrame( painter );
103 
104  if ( isSelected() )
105  {
106  drawSelectionBoxes( painter );
107  }
108 }
109 
110 
111 void QgsComposerShape::drawShape( QPainter* p )
112 {
113  if ( mUseSymbolV2 )
114  {
116  return;
117  }
118 
119  //draw using QPainter brush and pen to keep 2.0 api compatibility
120  p->save();
121  p->setRenderHint( QPainter::Antialiasing );
122 
123  switch ( mShape )
124  {
125  case Ellipse:
126  p->drawEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
127  break;
128  case Rectangle:
129  //if corner radius set, then draw a rounded rectangle
130  if ( mCornerRadius > 0 )
131  {
132  p->drawRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
133  }
134  else
135  {
136  p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
137  }
138  break;
139  case Triangle:
140  QPolygonF triangle;
141  triangle << QPointF( 0, rect().height() );
142  triangle << QPointF( rect().width() , rect().height() );
143  triangle << QPointF( rect().width() / 2.0, 0 );
144  p->drawPolygon( triangle );
145  break;
146  }
147  p->restore();
148 }
149 
151 {
152  p->save();
153  p->setRenderHint( QPainter::Antialiasing );
154 
155  QgsRenderContext context;
156  context.setPainter( p );
157  context.setScaleFactor( 1.0 );
159  {
160  //Limit resolution of symbol fill if composition is not being exported
161  //otherwise zooming into composition slows down renders
162  context.setRasterScaleFactor( qMin( horizontalViewScaleFactor(), 3.0 ) );
163  }
164  else
165  {
167  }
168 
169  //generate polygon to draw
170  QList<QPolygonF> rings; //empty list
171  QPolygonF shapePolygon;
172 
173  //shapes with curves must be enlarged before conversion to QPolygonF, or
174  //the curves are approximated too much and appear jaggy
175  QTransform t = QTransform::fromScale( 100, 100 );
176  //inverse transform used to scale created polygons back to expected size
177  QTransform ti = t.inverted();
178 
179  switch ( mShape )
180  {
181  case Ellipse:
182  {
183  //create an ellipse
184  QPainterPath ellipsePath;
185  ellipsePath.addEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
186  QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
187  shapePolygon = ti.map( ellipsePoly );
188  break;
189  }
190  case Rectangle:
191  {
192  //if corner radius set, then draw a rounded rectangle
193  if ( mCornerRadius > 0 )
194  {
195  QPainterPath roundedRectPath;
196  roundedRectPath.addRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
197  QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
198  shapePolygon = ti.map( roundedPoly );
199  }
200  else
201  {
202  shapePolygon = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
203  }
204  break;
205  }
206  case Triangle:
207  {
208  shapePolygon << QPointF( 0, rect().height() );
209  shapePolygon << QPointF( rect().width() , rect().height() );
210  shapePolygon << QPointF( rect().width() / 2.0, 0 );
211  shapePolygon << QPointF( 0, rect().height() );
212  break;
213  }
214  }
215 
216  mShapeStyleSymbol->startRender( context );
217 
218  //need to render using atlas feature properties?
220  {
221  //using an atlas, so render using current atlas feature
222  //since there may be data defined symbols using atlas feature properties
223  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, mComposition->atlasComposition().currentFeature(), context );
224  }
225  else
226  {
227  mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
228  }
229 
230  mShapeStyleSymbol->stopRender( context );
231  p->restore();
232 }
233 
234 
235 void QgsComposerShape::drawFrame( QPainter* p )
236 {
237  if ( mFrame && p && !mUseSymbolV2 )
238  {
239  p->setPen( pen() );
240  p->setBrush( Qt::NoBrush );
241  p->setRenderHint( QPainter::Antialiasing, true );
242  drawShape( p );
243  }
244 }
245 
247 {
248  if ( p && ( mBackground || mUseSymbolV2 ) )
249  {
250  p->setBrush( brush() );//this causes a problem in atlas generation
251  p->setPen( Qt::NoPen );
252  p->setRenderHint( QPainter::Antialiasing, true );
253  drawShape( p );
254  }
255 }
256 
258 {
259  return mMaxSymbolBleed;
260 }
261 
262 bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
263 {
264  QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
265  composerShapeElem.setAttribute( "shapeType", mShape );
266  composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
267 
268  QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
269  composerShapeElem.appendChild( shapeStyleElem );
270 
271  elem.appendChild( composerShapeElem );
272  return _writeXML( composerShapeElem, doc );
273 }
274 
275 bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc )
276 {
277  mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() );
278  mCornerRadius = itemElem.attribute( "cornerRadius", "0" ).toDouble();
279 
280  //restore general composer item properties
281  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
282  if ( composerItemList.size() > 0 )
283  {
284  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
285 
286  //rotation
287  if ( composerItemElem.attribute( "rotation", "0" ).toDouble() != 0 )
288  {
289  //check for old (pre 2.1) rotation attribute
290  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
291  }
292 
293  _readXML( composerItemElem, doc );
294  }
295 
296  QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
297  if ( !shapeStyleSymbolElem.isNull() )
298  {
299  delete mShapeStyleSymbol;
300  mShapeStyleSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( shapeStyleSymbolElem ) );
301  }
302  else
303  {
304  //upgrade project file from 2.0 to use symbolV2 styling
305  delete mShapeStyleSymbol;
306  QgsStringMap properties;
307  properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
308  if ( hasBackground() )
309  {
310  properties.insert( "style", "solid" );
311  }
312  else
313  {
314  properties.insert( "style", "no" );
315  }
316  if ( hasFrame() )
317  {
318  properties.insert( "style_border", "solid" );
319  }
320  else
321  {
322  properties.insert( "style_border", "no" );
323  }
324  properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
325  properties.insert( "width_border", QString::number( pen().widthF() ) );
327  }
328  emit itemChanged();
329  return true;
330 }
331 
333 {
334  mCornerRadius = radius;
335 }
336 
338 {
339  return mCurrentRectangle;
340 }
341 
343 {
344  QRectF rectangle = rect();
346  if ( rectangle != mCurrentRectangle )
347  {
348  prepareGeometryChange();
349  mCurrentRectangle = rectangle;
350  }
351 }
352 
353 void QgsComposerShape::setSceneRect( const QRectF& rectangle )
354 {
355  // Reimplemented from QgsComposerItem as we need to call updateBoundingRect after the shape's size changes
356  QgsComposerItem::setSceneRect( rectangle );
358  update();
359 }
QRectF mCurrentRectangle
Current bounding rectangle of shape.
void setShapeStyleSymbol(QgsFillSymbolV2 *symbol)
Sets the QgsFillSymbolV2 used to draw the shape.
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.
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
Is the atlas generation enabled ?
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)
void itemChanged()
Used e.g.
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.
double horizontalViewScaleFactor() const
Returns the zoom factor of the graphics view.
void setScaleFactor(double factor)
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.
int printResolution() const
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...
QgsComposition * mComposition
Graphics scene for map printing.
QgsFeature * currentFeature()
Returns the current atlas feature.
virtual void setItemRotation(double r, bool adjustPosition=false)
Sets the item rotation.
void refreshSymbol()
Should be called after the shape's symbol is changed.
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
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)
void setRasterScaleFactor(double factor)
QgsComposition::PlotStyle plotStyle() const
void setFrameEnabled(bool drawFrame)
Set whether this item has a frame drawn around it or not.
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.