Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] Setting for corner radius for composer rectangle shapes, al…
…lows creation of rounded rectangles in composers

Add tests for composer shapes
  • Loading branch information
nyalldawson authored and mhugent committed Sep 17, 2013
1 parent e5c40d7 commit 58d0483
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 29 deletions.
5 changes: 5 additions & 0 deletions python/core/composer/qgscomposershape.sip
Expand Up @@ -42,6 +42,11 @@ class QgsComposerShape: QgsComposerItem
/**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*/
void setCornerRadius( double radius );
/**Returns the radius for rounded rectangle corners*/
double cornerRadius() const;

public slots:
/**Sets item rotation and resizes item bounds such that the shape always has the same size*/
Expand Down
29 changes: 29 additions & 0 deletions src/app/composer/qgscomposershapewidget.cpp
Expand Up @@ -54,6 +54,7 @@ void QgsComposerShapeWidget::blockAllSignals( bool block )
{
mShapeComboBox->blockSignals( block );
mRotationSpinBox->blockSignals( block );
mCornerRadiusSpinBox->blockSignals( block );
}

void QgsComposerShapeWidget::setGuiElementValues()
Expand All @@ -66,17 +67,21 @@ void QgsComposerShapeWidget::setGuiElementValues()
blockAllSignals( true );

mRotationSpinBox->setValue( mComposerShape->rotation() );
mCornerRadiusSpinBox->setValue( mComposerShape->cornerRadius() );
if ( mComposerShape->shapeType() == QgsComposerShape::Ellipse )
{
mShapeComboBox->setCurrentIndex( mShapeComboBox->findText( tr( "Ellipse" ) ) );
mCornerRadiusSpinBox->setEnabled( false );
}
else if ( mComposerShape->shapeType() == QgsComposerShape::Rectangle )
{
mShapeComboBox->setCurrentIndex( mShapeComboBox->findText( tr( "Rectangle" ) ) );
mCornerRadiusSpinBox->setEnabled( true );
}
else if ( mComposerShape->shapeType() == QgsComposerShape::Triangle )
{
mShapeComboBox->setCurrentIndex( mShapeComboBox->findText( tr( "Triangle" ) ) );
mCornerRadiusSpinBox->setEnabled( false );
}

blockAllSignals( false );
Expand All @@ -93,6 +98,17 @@ void QgsComposerShapeWidget::on_mRotationSpinBox_valueChanged( int val )
}
}

void QgsComposerShapeWidget::on_mCornerRadiusSpinBox_valueChanged( double val )
{
if ( mComposerShape )
{
mComposerShape->beginCommand( tr( "Shape radius changed" ), QgsComposerMergeCommand::ShapeRotation );
mComposerShape->setCornerRadius( val );
mComposerShape->update();
mComposerShape->endCommand();
}
}

void QgsComposerShapeWidget::on_mShapeComboBox_currentIndexChanged( const QString& text )
{
if ( !mComposerShape )
Expand All @@ -113,9 +129,22 @@ void QgsComposerShapeWidget::on_mShapeComboBox_currentIndexChanged( const QStrin
{
mComposerShape->setShapeType( QgsComposerShape::Triangle );
}
toggleRadiusSpin( text );
mComposerShape->update();
mComposerShape->endCommand();
}

void QgsComposerShapeWidget::toggleRadiusSpin( const QString& shapeText )
{
if ( shapeText == tr( "Rectangle" ) )
{
mCornerRadiusSpinBox->setEnabled( true );
}
else
{
mCornerRadiusSpinBox->setEnabled( false );
}
}



4 changes: 4 additions & 0 deletions src/app/composer/qgscomposershapewidget.h
Expand Up @@ -39,9 +39,13 @@ class QgsComposerShapeWidget: public QWidget, private Ui::QgsComposerShapeWidget
private slots:
void on_mShapeComboBox_currentIndexChanged( const QString& text );
void on_mRotationSpinBox_valueChanged( int val );
void on_mCornerRadiusSpinBox_valueChanged( double val );

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

/**Enables or disables the rounded radius spin box based on shape type*/
void toggleRadiusSpin( const QString& shapeText );
};

#endif // QGSCOMPOSERSHAPEWIDGET_H
25 changes: 20 additions & 5 deletions src/core/composer/qgscomposershape.cpp
Expand Up @@ -18,12 +18,14 @@
#include "qgscomposershape.h"
#include <QPainter>

QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ), mShape( Ellipse )
QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ), mShape( Ellipse ), mCornerRadius( 0 )
{
setFrameEnabled( true );
}

QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ), mShape( Ellipse )
QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ),
mShape( Ellipse ),
mCornerRadius( 0 )
{
setSceneRect( QRectF( x, y, width, height ) );
setFrameEnabled( true );
Expand Down Expand Up @@ -68,7 +70,15 @@ void QgsComposerShape::drawShape( QPainter* p )
p->drawEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
break;
case Rectangle:
p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
//if corner radius set, then draw a rounded rectangle
if ( mCornerRadius > 0 )
{
p->drawRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
}
else
{
p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
}
break;
case Triangle:
QPolygonF triangle;
Expand Down Expand Up @@ -110,13 +120,15 @@ bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
{
QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
composerShapeElem.setAttribute( "shapeType", mShape );
composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
elem.appendChild( composerShapeElem );
return _writeXML( composerShapeElem, doc );
}

bool QgsComposerShape::readXML( const QDomElement& itemElem, const QDomDocument& doc )
{
mShape = QgsComposerShape::Shape( itemElem.attribute( "shapeType", "0" ).toInt() );
mCornerRadius = itemElem.attribute( "cornerRadius", "0" ).toDouble();

//restore general composer item properties
QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
Expand Down Expand Up @@ -145,10 +157,13 @@ void QgsComposerShape::setRotation( double r )
QgsComposerItem::setRotation( r );
}

void QgsComposerShape::setSceneRect( const QRectF& rectangle )
void QgsComposerShape::setCornerRadius( double radius )
{
mCornerRadius = radius;
}


void QgsComposerShape::setSceneRect( const QRectF& rectangle )
{
//consider to change size of the shape if the rectangle changes width and/or height
if ( rectangle.width() != rect().width() || rectangle.height() != rect().height() )
{
Expand Down
7 changes: 6 additions & 1 deletion src/core/composer/qgscomposershape.h
Expand Up @@ -65,11 +65,14 @@ class CORE_EXPORT QgsComposerShape: public QgsComposerItem
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 );
double cornerRadius() const { return mCornerRadius; };

public slots:
/**Sets item rotation and resizes item bounds such that the shape always has the same size*/
virtual void setRotation( double r );


protected:
/* reimplement drawFrame, since it's not a rect, but a custom shape */
virtual void drawFrame( QPainter* p );
Expand All @@ -81,6 +84,8 @@ class CORE_EXPORT QgsComposerShape: public QgsComposerItem
/**Ellipse, rectangle or triangle*/
Shape mShape;

double mCornerRadius;

/* draws the custom shape */
void drawShape( QPainter* p );

Expand Down
67 changes: 44 additions & 23 deletions src/ui/qgscomposershapewidgetbase.ui
Expand Up @@ -47,7 +47,7 @@
<x>0</x>
<y>0</y>
<width>283</width>
<height>405</height>
<height>404</height>
</rect>
</property>
<layout class="QVBoxLayout" name="mainLayout">
Expand All @@ -69,28 +69,49 @@
<property name="labelAlignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="mShapeComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Rotation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="mRotationSpinBox">
<property name="suffix">
<string> °</string>
</property>
<property name="prefix">
<string comment="Rotation" extracomment="Rotation"/>
</property>
<property name="maximum">
<number>359</number>
</property>
</widget>
<item row="2" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Rotation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="mRotationSpinBox">
<property name="suffix">
<string> °</string>
</property>
<property name="prefix">
<string comment="Rotation" extracomment="Rotation"/>
</property>
<property name="maximum">
<number>359</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Corner radius</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="mCornerRadiusSpinBox">
<property name="suffix">
<string> mm</string>
</property>
<property name="maximum">
<double>999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QComboBox" name="mShapeComboBox"/>
</item>
</layout>
</item>
</layout>
</widget>
Expand Down
1 change: 1 addition & 0 deletions tests/src/core/CMakeLists.txt
Expand Up @@ -103,6 +103,7 @@ ADD_QGIS_TEST(ziplayertest testziplayer.cpp)
ADD_QGIS_TEST(dataitemtest testqgsdataitem.cpp)
ADD_QGIS_TEST(composermaptest testqgscomposermap.cpp)
ADD_QGIS_TEST(composereffectstest testqgscomposereffects.cpp)
ADD_QGIS_TEST(composershapestest testqgscomposershapes.cpp)
ADD_QGIS_TEST(atlascompositiontest testqgsatlascomposition.cpp)
ADD_QGIS_TEST(composerlabeltest testqgscomposerlabel.cpp)
ADD_QGIS_TEST(stylev2test testqgsstylev2.cpp)
Expand Down
112 changes: 112 additions & 0 deletions tests/src/core/testqgscomposershapes.cpp
@@ -0,0 +1,112 @@
/***************************************************************************
testqgscomposershapes.cpp
----------------------
begin : April 2013
copyright : (C) 2013 by Marco Hugentobler, Nyall Dawson
email : nyall dot dawson at gmail.com
***************************************************************************/

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsapplication.h"
#include "qgscomposition.h"
#include "qgscompositionchecker.h"
#include "qgscomposershape.h"
#include <QObject>
#include <QtTest>
#include <QColor>
#include <QPainter>

class TestQgsComposerShapes: public QObject
{
Q_OBJECT;
private slots:
void initTestCase();// will be called before the first testfunction is executed.
void cleanupTestCase();// will be called after the last testfunction was executed.
void init();// will be called before each testfunction is executed.
void cleanup();// will be called after every testfunction.
void rectangle(); //test if rectangle shape is functioning
void triangle(); //test if triange shape is functioning
void ellipse(); //test if ellipse shape is functioning
void roundedRectangle(); //test if rounded rectangle shape is functioning

private:
QgsComposition* mComposition;
QgsComposerShape* mComposerShape;
};

void TestQgsComposerShapes::initTestCase()
{
QgsApplication::init();
QgsApplication::initQgis();

//create composition with two rectangles
mComposition = new QgsComposition( 0 );
mComposition->setPaperSize( 297, 210 ); //A4 landscape
mComposerShape = new QgsComposerShape( 20, 20, 150, 100, mComposition );
mComposerShape->setBackgroundColor( QColor::fromRgb( 255, 150, 0 ) );
mComposition->addComposerShape( mComposerShape );
}

void TestQgsComposerShapes::cleanupTestCase()
{
delete mComposition;
}

void TestQgsComposerShapes::init()
{

}

void TestQgsComposerShapes::cleanup()
{

}

void TestQgsComposerShapes::rectangle()
{
mComposerShape->setShapeType( QgsComposerShape::Rectangle );

QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_rectangle.png" ) );
QVERIFY( checker.testComposition() );
}

void TestQgsComposerShapes::triangle()
{
mComposerShape->setShapeType( QgsComposerShape::Triangle );

QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_triangle.png" ) );
QVERIFY( checker.testComposition() );
}

void TestQgsComposerShapes::ellipse()
{
mComposerShape->setShapeType( QgsComposerShape::Ellipse );

QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_ellipse.png" ) );
QVERIFY( checker.testComposition() );
}

void TestQgsComposerShapes::roundedRectangle()
{
mComposerShape->setShapeType( QgsComposerShape::Rectangle );
mComposerShape->setCornerRadius( 30 );

QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_roundedrectangle.png" ) );
QVERIFY( checker.testComposition() );
mComposerShape->setCornerRadius( 0 );
}

QTEST_MAIN( TestQgsComposerShapes )
#include "moc_testqgscomposershapes.cxx"
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -17,6 +17,7 @@ ADD_PYTHON_TEST(PyQgsComposition test_qgscomposition.py)
ADD_PYTHON_TEST(PyQgsAnalysis test_qgsanalysis.py)
ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py)
ADD_PYTHON_TEST(PyQgsComposerEffects test_qgscomposereffects.py)
ADD_PYTHON_TEST(PyQgsComposerShapes test_qgscomposershapes.py)
ADD_PYTHON_TEST(PyQgsSymbolLayerV2 test_qgssymbollayerv2.py)
ADD_PYTHON_TEST(PyQgsPoint test_qgspoint.py)
ADD_PYTHON_TEST(PyQgsAtlasComposition test_qgsatlascomposition.py)
Expand Down

0 comments on commit 58d0483

Please sign in to comment.