Skip to content

Commit 860e23a

Browse files
committed
[FEATURE][composer] Add styling support for composer shapes
1 parent 0c6d9ef commit 860e23a

File tree

11 files changed

+330
-43
lines changed

11 files changed

+330
-43
lines changed

python/core/composer/qgscomposershape.sip

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,22 @@ class QgsComposerShape: QgsComposerItem
3939
QgsComposerShape::Shape shapeType() const;
4040
void setShapeType( QgsComposerShape::Shape s );
4141

42-
/**Sets this items bound in scene coordinates such that 1 item size units
43-
corresponds to 1 scene size unit. Also, the shape is scaled*/
44-
void setSceneRect( const QRectF& rectangle );
45-
4642
/**Sets radius for rounded rectangle corners. Added in v2.1 */
4743
void setCornerRadius( double radius );
4844
/**Returns the radius for rounded rectangle corners*/
4945
double cornerRadius() const;
46+
47+
/**Sets the QgsFillSymbolV2 used to draw the shape. Must also call setUseSymbolV2( true ) to
48+
* enable drawing with a symbol.
49+
* Note: added in version 2.1*/
50+
void setShapeStyleSymbol( QgsFillSymbolV2* symbol );
51+
/**Returns the QgsFillSymbolV2 used to draw the shape.
52+
* Note: added in version 2.1*/
53+
QgsFillSymbolV2* shapeStyleSymbol();
5054

51-
public slots:
52-
/**Sets item rotation and resizes item bounds such that the shape always has the same size*/
53-
virtual void setItemRotation( double r );
55+
/**Controls whether the shape should be drawn using a QgsFillSymbolV2.
56+
* Note: Added in v2.1 */
57+
void setUseSymbolV2( bool useSymbolV2 );
5458

5559
protected:
5660
/* reimplement drawFrame, since it's not a rect, but a custom shape */

src/app/composer/qgscomposershapewidget.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
#include "qgscomposershapewidget.h"
1919
#include "qgscomposershape.h"
2020
#include "qgscomposeritemwidget.h"
21+
#include "qgscomposition.h"
22+
#include "qgsstylev2.h"
23+
#include "qgssymbolv2selectordialog.h"
24+
#include "qgssymbollayerv2utils.h"
2125
#include <QColorDialog>
2226

2327
QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape* composerShape ): QWidget( 0 ), mComposerShape( composerShape )
@@ -54,6 +58,7 @@ void QgsComposerShapeWidget::blockAllSignals( bool block )
5458
{
5559
mShapeComboBox->blockSignals( block );
5660
mCornerRadiusSpinBox->blockSignals( block );
61+
mShapeStyleButton->blockSignals( block );
5762
}
5863

5964
void QgsComposerShapeWidget::setGuiElementValues()
@@ -65,6 +70,8 @@ void QgsComposerShapeWidget::setGuiElementValues()
6570

6671
blockAllSignals( true );
6772

73+
updateShapeStyle();
74+
6875
mCornerRadiusSpinBox->setValue( mComposerShape->cornerRadius() );
6976
if ( mComposerShape->shapeType() == QgsComposerShape::Ellipse )
7077
{
@@ -85,6 +92,37 @@ void QgsComposerShapeWidget::setGuiElementValues()
8592
blockAllSignals( false );
8693
}
8794

95+
void QgsComposerShapeWidget::on_mShapeStyleButton_clicked()
96+
{
97+
if ( !mComposerShape )
98+
{
99+
return;
100+
}
101+
102+
QgsVectorLayer* coverageLayer = 0;
103+
// use the atlas coverage layer, if any
104+
if ( mComposerShape->composition()->atlasComposition().enabled() )
105+
{
106+
coverageLayer = mComposerShape->composition()->atlasComposition().coverageLayer();
107+
}
108+
109+
QgsSymbolV2SelectorDialog d( mComposerShape->shapeStyleSymbol(), QgsStyleV2::defaultStyle(), coverageLayer );
110+
111+
if ( d.exec() == QDialog::Accepted )
112+
{
113+
updateShapeStyle();
114+
}
115+
}
116+
117+
void QgsComposerShapeWidget::updateShapeStyle()
118+
{
119+
if ( mComposerShape )
120+
{
121+
QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mComposerShape->shapeStyleSymbol(), mShapeStyleButton->iconSize() );
122+
mShapeStyleButton->setIcon( icon );
123+
}
124+
}
125+
88126
void QgsComposerShapeWidget::on_mCornerRadiusSpinBox_valueChanged( double val )
89127
{
90128
if ( mComposerShape )

src/app/composer/qgscomposershapewidget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,13 @@ class QgsComposerShapeWidget: public QWidget, private Ui::QgsComposerShapeWidget
3939
private slots:
4040
void on_mShapeComboBox_currentIndexChanged( const QString& text );
4141
void on_mCornerRadiusSpinBox_valueChanged( double val );
42+
void on_mShapeStyleButton_clicked();
4243

4344
/**Sets the GUI elements to the currentValues of mComposerShape*/
4445
void setGuiElementValues();
4546

47+
void updateShapeStyle();
48+
4649
/**Enables or disables the rounded radius spin box based on shape type*/
4750
void toggleRadiusSpin( const QString& shapeText );
4851
};

src/core/composer/qgsatlascomposition.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "qgsexpression.h"
2525
#include "qgsgeometry.h"
2626
#include "qgscomposerlabel.h"
27+
#include "qgscomposershape.h"
2728
#include "qgspaperitem.h"
2829
#include "qgsmaplayerregistry.h"
2930

@@ -375,6 +376,14 @@ void QgsAtlasComposition::prepareForFeature( int featureI )
375376
( *lit )->setExpressionContext( &mCurrentFeature, mCoverageLayer );
376377
}
377378

379+
// update shapes (in case they use data defined symbology with atlas properties)
380+
QList<QgsComposerShape*> shapes;
381+
mComposition->composerItems( shapes );
382+
for ( QList<QgsComposerShape*>::iterator lit = shapes.begin(); lit != shapes.end(); ++lit )
383+
{
384+
( *lit )->update();
385+
}
386+
378387
// update page background (in case it uses data defined symbology with atlas properties)
379388
QList<QgsPaperItem*> pages;
380389
mComposition->composerItems( pages );

src/core/composer/qgscomposershape.cpp

Lines changed: 173 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,61 @@
1616
***************************************************************************/
1717

1818
#include "qgscomposershape.h"
19+
#include "qgscomposition.h"
20+
#include "qgssymbolv2.h"
21+
#include "qgssymbollayerv2utils.h"
1922
#include <QPainter>
2023

21-
QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ), mShape( Ellipse ), mCornerRadius( 0 )
24+
QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ),
25+
mShape( Ellipse ),
26+
mCornerRadius( 0 ),
27+
mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
28+
mShapeStyleSymbol( 0 )
2229
{
2330
setFrameEnabled( true );
31+
createDefaultShapeStyleSymbol();
2432
}
2533

26-
QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ),
34+
QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ):
35+
QgsComposerItem( x, y, width, height, composition ),
2736
mShape( Ellipse ),
28-
mCornerRadius( 0 )
37+
mCornerRadius( 0 ),
38+
mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
39+
mShapeStyleSymbol( 0 )
2940
{
3041
setSceneRect( QRectF( x, y, width, height ) );
3142
setFrameEnabled( true );
43+
createDefaultShapeStyleSymbol();
3244
}
3345

3446
QgsComposerShape::~QgsComposerShape()
3547
{
48+
delete mShapeStyleSymbol;
49+
}
50+
51+
void QgsComposerShape::setUseSymbolV2( bool useSymbolV2 )
52+
{
53+
mUseSymbolV2 = useSymbolV2;
54+
setFrameEnabled( !useSymbolV2 );
55+
}
3656

57+
void QgsComposerShape::setShapeStyleSymbol( QgsFillSymbolV2* symbol )
58+
{
59+
delete mShapeStyleSymbol;
60+
mShapeStyleSymbol = symbol;
61+
update();
62+
}
63+
64+
void QgsComposerShape::createDefaultShapeStyleSymbol()
65+
{
66+
delete mShapeStyleSymbol;
67+
QgsStringMap properties;
68+
properties.insert( "color", "white" );
69+
properties.insert( "style", "solid" );
70+
properties.insert( "style_border", "solid" );
71+
properties.insert( "color_border", "black" );
72+
properties.insert( "width_border", "0.3" );
73+
mShapeStyleSymbol = QgsFillSymbolV2::createSimple( properties );
3774
}
3875

3976
void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
@@ -56,7 +93,13 @@ void QgsComposerShape::paint( QPainter* painter, const QStyleOptionGraphicsItem*
5693

5794
void QgsComposerShape::drawShape( QPainter* p )
5895
{
96+
if ( mUseSymbolV2 )
97+
{
98+
drawShapeUsingSymbol( p );
99+
return;
100+
}
59101

102+
//draw using QPainter brush and pen to keep 2.0 api compatibility
60103
p->save();
61104
p->setRenderHint( QPainter::Antialiasing );
62105

@@ -85,13 +128,101 @@ void QgsComposerShape::drawShape( QPainter* p )
85128
break;
86129
}
87130
p->restore();
131+
}
132+
133+
void QgsComposerShape::drawShapeUsingSymbol( QPainter* p )
134+
{
135+
p->save();
136+
p->setRenderHint( QPainter::Antialiasing );
137+
138+
QgsRenderContext context;
139+
context.setPainter( p );
140+
context.setScaleFactor( 1.0 );
141+
if ( mComposition->plotStyle() == QgsComposition::Preview )
142+
{
143+
context.setRasterScaleFactor( horizontalViewScaleFactor() );
144+
}
145+
else
146+
{
147+
context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
148+
}
149+
150+
//generate polygon to draw
151+
QList<QPolygonF> rings; //empty list
152+
QPolygonF shapePolygon;
88153

154+
//shapes with curves must be enlarged before conversion to QPolygonF, or
155+
//the curves are approximated too much and appear jaggy
156+
QTransform t = QTransform::fromScale( 100, 100 );
157+
//inverse transform used to scale created polygons back to expected size
158+
QTransform ti = t.inverted();
159+
160+
switch ( mShape )
161+
{
162+
case Ellipse:
163+
{
164+
//create an ellipse
165+
QPainterPath ellipsePath;
166+
ellipsePath.addEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
167+
QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
168+
shapePolygon = ti.map( ellipsePoly );
169+
break;
170+
}
171+
case Rectangle:
172+
{
173+
//if corner radius set, then draw a rounded rectangle
174+
if ( mCornerRadius > 0 )
175+
{
176+
QPainterPath roundedRectPath;
177+
roundedRectPath.addRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
178+
QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
179+
shapePolygon = ti.map( roundedPoly );
180+
}
181+
else
182+
{
183+
shapePolygon = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
184+
}
185+
break;
186+
}
187+
case Triangle:
188+
{
189+
shapePolygon << QPointF( 0, rect().height() );
190+
shapePolygon << QPointF( rect().width() , rect().height() );
191+
shapePolygon << QPointF( rect().width() / 2.0, 0 );
192+
shapePolygon << QPointF( 0, rect().height() );
193+
break;
194+
}
195+
}
196+
197+
mShapeStyleSymbol->startRender( context );
198+
199+
double maxBleed = QgsSymbolLayerV2Utils::estimateMaxSymbolBleed( mShapeStyleSymbol );
200+
201+
//even though we aren't going to use it to draw the shape, set the pen width as 2 * symbol bleed
202+
//so that the item is fully rendered within it's scene rect
203+
//(QGraphicsRectItem considers the pen width when calculating an item's scene rect)
204+
setPen( QPen( QBrush( Qt::NoBrush ), maxBleed * 2.0 ) );
205+
206+
//need to render using atlas feature properties?
207+
if ( mComposition->atlasComposition().enabled() && mComposition->atlasMode() != QgsComposition::AtlasOff )
208+
{
209+
//using an atlas, so render using current atlas feature
210+
//since there may be data defined symbols using atlas feature properties
211+
mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, mComposition->atlasComposition().currentFeature(), context );
212+
}
213+
else
214+
{
215+
mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
216+
}
217+
218+
mShapeStyleSymbol->stopRender( context );
219+
p->restore();
89220
}
90221

91222

92223
void QgsComposerShape::drawFrame( QPainter* p )
93224
{
94-
if ( mFrame && p )
225+
if ( mFrame && p && !mUseSymbolV2 )
95226
{
96227
p->setPen( pen() );
97228
p->setBrush( Qt::NoBrush );
@@ -102,7 +233,7 @@ void QgsComposerShape::drawFrame( QPainter* p )
102233

103234
void QgsComposerShape::drawBackground( QPainter* p )
104235
{
105-
if ( mBackground && p )
236+
if ( p && ( mBackground || mUseSymbolV2 ) )
106237
{
107238
p->setBrush( brush() );//this causes a problem in atlas generation
108239
p->setPen( Qt::NoPen );
@@ -117,6 +248,10 @@ bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
117248
QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
118249
composerShapeElem.setAttribute( "shapeType", mShape );
119250
composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
251+
252+
QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
253+
composerShapeElem.appendChild( shapeStyleElem );
254+
120255
elem.appendChild( composerShapeElem );
121256
return _writeXML( composerShapeElem, doc );
122257
}
@@ -141,6 +276,39 @@ bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument&
141276

142277
_readXML( composerItemElem, doc );
143278
}
279+
280+
QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
281+
if ( !shapeStyleSymbolElem.isNull() )
282+
{
283+
delete mShapeStyleSymbol;
284+
mShapeStyleSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( shapeStyleSymbolElem ) );
285+
}
286+
else
287+
{
288+
//upgrade project file from 2.0 to use symbolV2 styling
289+
delete mShapeStyleSymbol;
290+
QgsStringMap properties;
291+
properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
292+
if ( hasBackground() )
293+
{
294+
properties.insert( "style", "solid" );
295+
}
296+
else
297+
{
298+
properties.insert( "style", "no" );
299+
}
300+
if ( hasFrame() )
301+
{
302+
properties.insert( "style_border", "solid" );
303+
}
304+
else
305+
{
306+
properties.insert( "style_border", "no" );
307+
}
308+
properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
309+
properties.insert( "width_border", QString::number( pen().widthF() ) );
310+
mShapeStyleSymbol = QgsFillSymbolV2::createSimple( properties );
311+
}
144312
emit itemChanged();
145313
return true;
146314
}

0 commit comments

Comments
 (0)