Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][composer] Add styling support for composer shapes
  • Loading branch information
nyalldawson committed Jan 12, 2013
1 parent 0c6d9ef commit 860e23a
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 43 deletions.
18 changes: 11 additions & 7 deletions python/core/composer/qgscomposershape.sip
Expand Up @@ -39,18 +39,22 @@ class QgsComposerShape: QgsComposerItem
QgsComposerShape::Shape shapeType() const;
void setShapeType( QgsComposerShape::Shape s );

/**Sets this items bound in scene coordinates such that 1 item size units
corresponds to 1 scene size unit. Also, the shape is scaled*/
void setSceneRect( const QRectF& rectangle );

/**Sets radius for rounded rectangle corners. Added in v2.1 */
void setCornerRadius( double radius );
/**Returns the radius for rounded rectangle corners*/
double cornerRadius() const;

/**Sets the QgsFillSymbolV2 used to draw the shape. Must also call setUseSymbolV2( true ) to
* enable drawing with a symbol.
* Note: added in version 2.1*/
void setShapeStyleSymbol( QgsFillSymbolV2* symbol );
/**Returns the QgsFillSymbolV2 used to draw the shape.
* Note: added in version 2.1*/
QgsFillSymbolV2* shapeStyleSymbol();

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

protected:
/* reimplement drawFrame, since it's not a rect, but a custom shape */
Expand Down
38 changes: 38 additions & 0 deletions src/app/composer/qgscomposershapewidget.cpp
Expand Up @@ -18,6 +18,10 @@
#include "qgscomposershapewidget.h"
#include "qgscomposershape.h"
#include "qgscomposeritemwidget.h"
#include "qgscomposition.h"
#include "qgsstylev2.h"
#include "qgssymbolv2selectordialog.h"
#include "qgssymbollayerv2utils.h"
#include <QColorDialog>

QgsComposerShapeWidget::QgsComposerShapeWidget( QgsComposerShape* composerShape ): QWidget( 0 ), mComposerShape( composerShape )
Expand Down Expand Up @@ -54,6 +58,7 @@ void QgsComposerShapeWidget::blockAllSignals( bool block )
{
mShapeComboBox->blockSignals( block );
mCornerRadiusSpinBox->blockSignals( block );
mShapeStyleButton->blockSignals( block );
}

void QgsComposerShapeWidget::setGuiElementValues()
Expand All @@ -65,6 +70,8 @@ void QgsComposerShapeWidget::setGuiElementValues()

blockAllSignals( true );

updateShapeStyle();

mCornerRadiusSpinBox->setValue( mComposerShape->cornerRadius() );
if ( mComposerShape->shapeType() == QgsComposerShape::Ellipse )
{
Expand All @@ -85,6 +92,37 @@ void QgsComposerShapeWidget::setGuiElementValues()
blockAllSignals( false );
}

void QgsComposerShapeWidget::on_mShapeStyleButton_clicked()
{
if ( !mComposerShape )
{
return;
}

QgsVectorLayer* coverageLayer = 0;
// use the atlas coverage layer, if any
if ( mComposerShape->composition()->atlasComposition().enabled() )
{
coverageLayer = mComposerShape->composition()->atlasComposition().coverageLayer();
}

QgsSymbolV2SelectorDialog d( mComposerShape->shapeStyleSymbol(), QgsStyleV2::defaultStyle(), coverageLayer );

if ( d.exec() == QDialog::Accepted )
{
updateShapeStyle();
}
}

void QgsComposerShapeWidget::updateShapeStyle()
{
if ( mComposerShape )
{
QIcon icon = QgsSymbolLayerV2Utils::symbolPreviewIcon( mComposerShape->shapeStyleSymbol(), mShapeStyleButton->iconSize() );
mShapeStyleButton->setIcon( icon );
}
}

void QgsComposerShapeWidget::on_mCornerRadiusSpinBox_valueChanged( double val )
{
if ( mComposerShape )
Expand Down
3 changes: 3 additions & 0 deletions src/app/composer/qgscomposershapewidget.h
Expand Up @@ -39,10 +39,13 @@ class QgsComposerShapeWidget: public QWidget, private Ui::QgsComposerShapeWidget
private slots:
void on_mShapeComboBox_currentIndexChanged( const QString& text );
void on_mCornerRadiusSpinBox_valueChanged( double val );
void on_mShapeStyleButton_clicked();

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

void updateShapeStyle();

/**Enables or disables the rounded radius spin box based on shape type*/
void toggleRadiusSpin( const QString& shapeText );
};
Expand Down
9 changes: 9 additions & 0 deletions src/core/composer/qgsatlascomposition.cpp
Expand Up @@ -24,6 +24,7 @@
#include "qgsexpression.h"
#include "qgsgeometry.h"
#include "qgscomposerlabel.h"
#include "qgscomposershape.h"
#include "qgspaperitem.h"
#include "qgsmaplayerregistry.h"

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

// update shapes (in case they use data defined symbology with atlas properties)
QList<QgsComposerShape*> shapes;
mComposition->composerItems( shapes );
for ( QList<QgsComposerShape*>::iterator lit = shapes.begin(); lit != shapes.end(); ++lit )
{
( *lit )->update();
}

// update page background (in case it uses data defined symbology with atlas properties)
QList<QgsPaperItem*> pages;
mComposition->composerItems( pages );
Expand Down
178 changes: 173 additions & 5 deletions src/core/composer/qgscomposershape.cpp
Expand Up @@ -16,24 +16,61 @@
***************************************************************************/

#include "qgscomposershape.h"
#include "qgscomposition.h"
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include <QPainter>

QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ), mShape( Ellipse ), mCornerRadius( 0 )
QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ),
mShape( Ellipse ),
mCornerRadius( 0 ),
mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
mShapeStyleSymbol( 0 )
{
setFrameEnabled( true );
createDefaultShapeStyleSymbol();
}

QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ),
QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ):
QgsComposerItem( x, y, width, height, composition ),
mShape( Ellipse ),
mCornerRadius( 0 )
mCornerRadius( 0 ),
mUseSymbolV2( false ), //default to not using SymbolV2 for shapes, to preserve 2.0 api
mShapeStyleSymbol( 0 )
{
setSceneRect( QRectF( x, y, width, height ) );
setFrameEnabled( true );
createDefaultShapeStyleSymbol();
}

QgsComposerShape::~QgsComposerShape()
{
delete mShapeStyleSymbol;
}

void QgsComposerShape::setUseSymbolV2( bool useSymbolV2 )
{
mUseSymbolV2 = useSymbolV2;
setFrameEnabled( !useSymbolV2 );
}

void QgsComposerShape::setShapeStyleSymbol( QgsFillSymbolV2* symbol )
{
delete mShapeStyleSymbol;
mShapeStyleSymbol = symbol;
update();
}

void QgsComposerShape::createDefaultShapeStyleSymbol()
{
delete mShapeStyleSymbol;
QgsStringMap properties;
properties.insert( "color", "white" );
properties.insert( "style", "solid" );
properties.insert( "style_border", "solid" );
properties.insert( "color_border", "black" );
properties.insert( "width_border", "0.3" );
mShapeStyleSymbol = QgsFillSymbolV2::createSimple( properties );
}

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

void QgsComposerShape::drawShape( QPainter* p )
{
if ( mUseSymbolV2 )
{
drawShapeUsingSymbol( p );
return;
}

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

Expand Down Expand Up @@ -85,13 +128,101 @@ void QgsComposerShape::drawShape( QPainter* p )
break;
}
p->restore();
}

void QgsComposerShape::drawShapeUsingSymbol( QPainter* p )
{
p->save();
p->setRenderHint( QPainter::Antialiasing );

QgsRenderContext context;
context.setPainter( p );
context.setScaleFactor( 1.0 );
if ( mComposition->plotStyle() == QgsComposition::Preview )
{
context.setRasterScaleFactor( horizontalViewScaleFactor() );
}
else
{
context.setRasterScaleFactor( mComposition->printResolution() / 25.4 );
}

//generate polygon to draw
QList<QPolygonF> rings; //empty list
QPolygonF shapePolygon;

//shapes with curves must be enlarged before conversion to QPolygonF, or
//the curves are approximated too much and appear jaggy
QTransform t = QTransform::fromScale( 100, 100 );
//inverse transform used to scale created polygons back to expected size
QTransform ti = t.inverted();

switch ( mShape )
{
case Ellipse:
{
//create an ellipse
QPainterPath ellipsePath;
ellipsePath.addEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
shapePolygon = ti.map( ellipsePoly );
break;
}
case Rectangle:
{
//if corner radius set, then draw a rounded rectangle
if ( mCornerRadius > 0 )
{
QPainterPath roundedRectPath;
roundedRectPath.addRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
QPolygonF roundedPoly = roundedRectPath.toFillPolygon( t );
shapePolygon = ti.map( roundedPoly );
}
else
{
shapePolygon = QPolygonF( QRectF( 0, 0, rect().width(), rect().height() ) );
}
break;
}
case Triangle:
{
shapePolygon << QPointF( 0, rect().height() );
shapePolygon << QPointF( rect().width() , rect().height() );
shapePolygon << QPointF( rect().width() / 2.0, 0 );
shapePolygon << QPointF( 0, rect().height() );
break;
}
}

mShapeStyleSymbol->startRender( context );

double maxBleed = QgsSymbolLayerV2Utils::estimateMaxSymbolBleed( mShapeStyleSymbol );

//even though we aren't going to use it to draw the shape, set the pen width as 2 * symbol bleed
//so that the item is fully rendered within it's scene rect
//(QGraphicsRectItem considers the pen width when calculating an item's scene rect)
setPen( QPen( QBrush( Qt::NoBrush ), maxBleed * 2.0 ) );

//need to render using atlas feature properties?
if ( mComposition->atlasComposition().enabled() && mComposition->atlasMode() != QgsComposition::AtlasOff )
{
//using an atlas, so render using current atlas feature
//since there may be data defined symbols using atlas feature properties
mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, mComposition->atlasComposition().currentFeature(), context );
}
else
{
mShapeStyleSymbol->renderPolygon( shapePolygon, &rings, 0, context );
}

mShapeStyleSymbol->stopRender( context );
p->restore();
}


void QgsComposerShape::drawFrame( QPainter* p )
{
if ( mFrame && p )
if ( mFrame && p && !mUseSymbolV2 )
{
p->setPen( pen() );
p->setBrush( Qt::NoBrush );
Expand All @@ -102,7 +233,7 @@ void QgsComposerShape::drawFrame( QPainter* p )

void QgsComposerShape::drawBackground( QPainter* p )
{
if ( mBackground && p )
if ( p && ( mBackground || mUseSymbolV2 ) )
{
p->setBrush( brush() );//this causes a problem in atlas generation
p->setPen( Qt::NoPen );
Expand All @@ -117,6 +248,10 @@ bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
composerShapeElem.setAttribute( "shapeType", mShape );
composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );

QDomElement shapeStyleElem = QgsSymbolLayerV2Utils::saveSymbol( QString(), mShapeStyleSymbol, doc );
composerShapeElem.appendChild( shapeStyleElem );

elem.appendChild( composerShapeElem );
return _writeXML( composerShapeElem, doc );
}
Expand All @@ -141,6 +276,39 @@ bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument&

_readXML( composerItemElem, doc );
}

QDomElement shapeStyleSymbolElem = itemElem.firstChildElement( "symbol" );
if ( !shapeStyleSymbolElem.isNull() )
{
delete mShapeStyleSymbol;
mShapeStyleSymbol = dynamic_cast<QgsFillSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( shapeStyleSymbolElem ) );
}
else
{
//upgrade project file from 2.0 to use symbolV2 styling
delete mShapeStyleSymbol;
QgsStringMap properties;
properties.insert( "color", QgsSymbolLayerV2Utils::encodeColor( brush().color() ) );
if ( hasBackground() )
{
properties.insert( "style", "solid" );
}
else
{
properties.insert( "style", "no" );
}
if ( hasFrame() )
{
properties.insert( "style_border", "solid" );
}
else
{
properties.insert( "style_border", "no" );
}
properties.insert( "color_border", QgsSymbolLayerV2Utils::encodeColor( pen().color() ) );
properties.insert( "width_border", QString::number( pen().widthF() ) );
mShapeStyleSymbol = QgsFillSymbolV2::createSimple( properties );
}
emit itemChanged();
return true;
}
Expand Down

0 comments on commit 860e23a

Please sign in to comment.