Skip to content

Commit 58d0483

Browse files
nyalldawsonmhugent
authored andcommitted
[FEATURE] Setting for corner radius for composer rectangle shapes, allows creation of rounded rectangles in composers
Add tests for composer shapes
1 parent e5c40d7 commit 58d0483

14 files changed

+337
-29
lines changed

python/core/composer/qgscomposershape.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ class QgsComposerShape: QgsComposerItem
4242
/**Sets this items bound in scene coordinates such that 1 item size units
4343
corresponds to 1 scene size unit. Also, the shape is scaled*/
4444
void setSceneRect( const QRectF& rectangle );
45+
46+
/**Sets radius for rounded rectangle corners*/
47+
void setCornerRadius( double radius );
48+
/**Returns the radius for rounded rectangle corners*/
49+
double cornerRadius() const;
4550

4651
public slots:
4752
/**Sets item rotation and resizes item bounds such that the shape always has the same size*/

src/app/composer/qgscomposershapewidget.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ void QgsComposerShapeWidget::blockAllSignals( bool block )
5454
{
5555
mShapeComboBox->blockSignals( block );
5656
mRotationSpinBox->blockSignals( block );
57+
mCornerRadiusSpinBox->blockSignals( block );
5758
}
5859

5960
void QgsComposerShapeWidget::setGuiElementValues()
@@ -66,17 +67,21 @@ void QgsComposerShapeWidget::setGuiElementValues()
6667
blockAllSignals( true );
6768

6869
mRotationSpinBox->setValue( mComposerShape->rotation() );
70+
mCornerRadiusSpinBox->setValue( mComposerShape->cornerRadius() );
6971
if ( mComposerShape->shapeType() == QgsComposerShape::Ellipse )
7072
{
7173
mShapeComboBox->setCurrentIndex( mShapeComboBox->findText( tr( "Ellipse" ) ) );
74+
mCornerRadiusSpinBox->setEnabled( false );
7275
}
7376
else if ( mComposerShape->shapeType() == QgsComposerShape::Rectangle )
7477
{
7578
mShapeComboBox->setCurrentIndex( mShapeComboBox->findText( tr( "Rectangle" ) ) );
79+
mCornerRadiusSpinBox->setEnabled( true );
7680
}
7781
else if ( mComposerShape->shapeType() == QgsComposerShape::Triangle )
7882
{
7983
mShapeComboBox->setCurrentIndex( mShapeComboBox->findText( tr( "Triangle" ) ) );
84+
mCornerRadiusSpinBox->setEnabled( false );
8085
}
8186

8287
blockAllSignals( false );
@@ -93,6 +98,17 @@ void QgsComposerShapeWidget::on_mRotationSpinBox_valueChanged( int val )
9398
}
9499
}
95100

101+
void QgsComposerShapeWidget::on_mCornerRadiusSpinBox_valueChanged( double val )
102+
{
103+
if ( mComposerShape )
104+
{
105+
mComposerShape->beginCommand( tr( "Shape radius changed" ), QgsComposerMergeCommand::ShapeRotation );
106+
mComposerShape->setCornerRadius( val );
107+
mComposerShape->update();
108+
mComposerShape->endCommand();
109+
}
110+
}
111+
96112
void QgsComposerShapeWidget::on_mShapeComboBox_currentIndexChanged( const QString& text )
97113
{
98114
if ( !mComposerShape )
@@ -113,9 +129,22 @@ void QgsComposerShapeWidget::on_mShapeComboBox_currentIndexChanged( const QStrin
113129
{
114130
mComposerShape->setShapeType( QgsComposerShape::Triangle );
115131
}
132+
toggleRadiusSpin( text );
116133
mComposerShape->update();
117134
mComposerShape->endCommand();
118135
}
119136

137+
void QgsComposerShapeWidget::toggleRadiusSpin( const QString& shapeText )
138+
{
139+
if ( shapeText == tr( "Rectangle" ) )
140+
{
141+
mCornerRadiusSpinBox->setEnabled( true );
142+
}
143+
else
144+
{
145+
mCornerRadiusSpinBox->setEnabled( false );
146+
}
147+
}
148+
120149

121150

src/app/composer/qgscomposershapewidget.h

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

4344
/**Sets the GUI elements to the currentValues of mComposerShape*/
4445
void setGuiElementValues();
46+
47+
/**Enables or disables the rounded radius spin box based on shape type*/
48+
void toggleRadiusSpin( const QString& shapeText );
4549
};
4650

4751
#endif // QGSCOMPOSERSHAPEWIDGET_H

src/core/composer/qgscomposershape.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
#include "qgscomposershape.h"
1919
#include <QPainter>
2020

21-
QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ), mShape( Ellipse )
21+
QgsComposerShape::QgsComposerShape( QgsComposition* composition ): QgsComposerItem( composition ), mShape( Ellipse ), mCornerRadius( 0 )
2222
{
2323
setFrameEnabled( true );
2424
}
2525

26-
QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ), mShape( Ellipse )
26+
QgsComposerShape::QgsComposerShape( qreal x, qreal y, qreal width, qreal height, QgsComposition* composition ): QgsComposerItem( x, y, width, height, composition ),
27+
mShape( Ellipse ),
28+
mCornerRadius( 0 )
2729
{
2830
setSceneRect( QRectF( x, y, width, height ) );
2931
setFrameEnabled( true );
@@ -68,7 +70,15 @@ void QgsComposerShape::drawShape( QPainter* p )
6870
p->drawEllipse( QRectF( 0, 0 , rect().width(), rect().height() ) );
6971
break;
7072
case Rectangle:
71-
p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
73+
//if corner radius set, then draw a rounded rectangle
74+
if ( mCornerRadius > 0 )
75+
{
76+
p->drawRoundedRect( QRectF( 0, 0 , rect().width(), rect().height() ), mCornerRadius, mCornerRadius );
77+
}
78+
else
79+
{
80+
p->drawRect( QRectF( 0, 0 , rect().width(), rect().height() ) );
81+
}
7282
break;
7383
case Triangle:
7484
QPolygonF triangle;
@@ -110,13 +120,15 @@ bool QgsComposerShape::writeXML( QDomElement& elem, QDomDocument & doc ) const
110120
{
111121
QDomElement composerShapeElem = doc.createElement( "ComposerShape" );
112122
composerShapeElem.setAttribute( "shapeType", mShape );
123+
composerShapeElem.setAttribute( "cornerRadius", mCornerRadius );
113124
elem.appendChild( composerShapeElem );
114125
return _writeXML( composerShapeElem, doc );
115126
}
116127

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

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

148-
void QgsComposerShape::setSceneRect( const QRectF& rectangle )
160+
void QgsComposerShape::setCornerRadius( double radius )
149161
{
162+
mCornerRadius = radius;
163+
}
150164

151-
165+
void QgsComposerShape::setSceneRect( const QRectF& rectangle )
166+
{
152167
//consider to change size of the shape if the rectangle changes width and/or height
153168
if ( rectangle.width() != rect().width() || rectangle.height() != rect().height() )
154169
{

src/core/composer/qgscomposershape.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,14 @@ class CORE_EXPORT QgsComposerShape: public QgsComposerItem
6565
corresponds to 1 scene size unit. Also, the shape is scaled*/
6666
void setSceneRect( const QRectF& rectangle );
6767

68+
/**Sets radius for rounded rectangle corners. Added in v2.1 */
69+
void setCornerRadius( double radius );
70+
double cornerRadius() const { return mCornerRadius; };
71+
6872
public slots:
6973
/**Sets item rotation and resizes item bounds such that the shape always has the same size*/
7074
virtual void setRotation( double r );
7175

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

87+
double mCornerRadius;
88+
8489
/* draws the custom shape */
8590
void drawShape( QPainter* p );
8691

src/ui/qgscomposershapewidgetbase.ui

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
<x>0</x>
4848
<y>0</y>
4949
<width>283</width>
50-
<height>405</height>
50+
<height>404</height>
5151
</rect>
5252
</property>
5353
<layout class="QVBoxLayout" name="mainLayout">
@@ -69,28 +69,49 @@
6969
<property name="labelAlignment">
7070
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
7171
</property>
72-
<item row="0" column="0" colspan="2">
73-
<widget class="QComboBox" name="mShapeComboBox"/>
74-
</item>
75-
<item row="1" column="0">
76-
<widget class="QLabel" name="label">
77-
<property name="text">
78-
<string>Rotation</string>
79-
</property>
80-
</widget>
81-
</item>
82-
<item row="1" column="1">
83-
<widget class="QSpinBox" name="mRotationSpinBox">
84-
<property name="suffix">
85-
<string> °</string>
86-
</property>
87-
<property name="prefix">
88-
<string comment="Rotation" extracomment="Rotation"/>
89-
</property>
90-
<property name="maximum">
91-
<number>359</number>
92-
</property>
93-
</widget>
72+
<item row="2" column="0" colspan="2">
73+
<layout class="QGridLayout" name="gridLayout">
74+
<item row="1" column="0">
75+
<widget class="QLabel" name="label">
76+
<property name="text">
77+
<string>Rotation</string>
78+
</property>
79+
</widget>
80+
</item>
81+
<item row="1" column="1">
82+
<widget class="QSpinBox" name="mRotationSpinBox">
83+
<property name="suffix">
84+
<string> °</string>
85+
</property>
86+
<property name="prefix">
87+
<string comment="Rotation" extracomment="Rotation"/>
88+
</property>
89+
<property name="maximum">
90+
<number>359</number>
91+
</property>
92+
</widget>
93+
</item>
94+
<item row="2" column="0">
95+
<widget class="QLabel" name="label_3">
96+
<property name="text">
97+
<string>Corner radius</string>
98+
</property>
99+
</widget>
100+
</item>
101+
<item row="2" column="1">
102+
<widget class="QDoubleSpinBox" name="mCornerRadiusSpinBox">
103+
<property name="suffix">
104+
<string> mm</string>
105+
</property>
106+
<property name="maximum">
107+
<double>999.000000000000000</double>
108+
</property>
109+
</widget>
110+
</item>
111+
<item row="0" column="0" colspan="2">
112+
<widget class="QComboBox" name="mShapeComboBox"/>
113+
</item>
114+
</layout>
94115
</item>
95116
</layout>
96117
</widget>

tests/src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ ADD_QGIS_TEST(ziplayertest testziplayer.cpp)
103103
ADD_QGIS_TEST(dataitemtest testqgsdataitem.cpp)
104104
ADD_QGIS_TEST(composermaptest testqgscomposermap.cpp)
105105
ADD_QGIS_TEST(composereffectstest testqgscomposereffects.cpp)
106+
ADD_QGIS_TEST(composershapestest testqgscomposershapes.cpp)
106107
ADD_QGIS_TEST(atlascompositiontest testqgsatlascomposition.cpp)
107108
ADD_QGIS_TEST(composerlabeltest testqgscomposerlabel.cpp)
108109
ADD_QGIS_TEST(stylev2test testqgsstylev2.cpp)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/***************************************************************************
2+
testqgscomposershapes.cpp
3+
----------------------
4+
begin : April 2013
5+
copyright : (C) 2013 by Marco Hugentobler, Nyall Dawson
6+
email : nyall dot dawson at gmail.com
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 "qgsapplication.h"
19+
#include "qgscomposition.h"
20+
#include "qgscompositionchecker.h"
21+
#include "qgscomposershape.h"
22+
#include <QObject>
23+
#include <QtTest>
24+
#include <QColor>
25+
#include <QPainter>
26+
27+
class TestQgsComposerShapes: public QObject
28+
{
29+
Q_OBJECT;
30+
private slots:
31+
void initTestCase();// will be called before the first testfunction is executed.
32+
void cleanupTestCase();// will be called after the last testfunction was executed.
33+
void init();// will be called before each testfunction is executed.
34+
void cleanup();// will be called after every testfunction.
35+
void rectangle(); //test if rectangle shape is functioning
36+
void triangle(); //test if triange shape is functioning
37+
void ellipse(); //test if ellipse shape is functioning
38+
void roundedRectangle(); //test if rounded rectangle shape is functioning
39+
40+
private:
41+
QgsComposition* mComposition;
42+
QgsComposerShape* mComposerShape;
43+
};
44+
45+
void TestQgsComposerShapes::initTestCase()
46+
{
47+
QgsApplication::init();
48+
QgsApplication::initQgis();
49+
50+
//create composition with two rectangles
51+
mComposition = new QgsComposition( 0 );
52+
mComposition->setPaperSize( 297, 210 ); //A4 landscape
53+
mComposerShape = new QgsComposerShape( 20, 20, 150, 100, mComposition );
54+
mComposerShape->setBackgroundColor( QColor::fromRgb( 255, 150, 0 ) );
55+
mComposition->addComposerShape( mComposerShape );
56+
}
57+
58+
void TestQgsComposerShapes::cleanupTestCase()
59+
{
60+
delete mComposition;
61+
}
62+
63+
void TestQgsComposerShapes::init()
64+
{
65+
66+
}
67+
68+
void TestQgsComposerShapes::cleanup()
69+
{
70+
71+
}
72+
73+
void TestQgsComposerShapes::rectangle()
74+
{
75+
mComposerShape->setShapeType( QgsComposerShape::Rectangle );
76+
77+
QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
78+
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_rectangle.png" ) );
79+
QVERIFY( checker.testComposition() );
80+
}
81+
82+
void TestQgsComposerShapes::triangle()
83+
{
84+
mComposerShape->setShapeType( QgsComposerShape::Triangle );
85+
86+
QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
87+
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_triangle.png" ) );
88+
QVERIFY( checker.testComposition() );
89+
}
90+
91+
void TestQgsComposerShapes::ellipse()
92+
{
93+
mComposerShape->setShapeType( QgsComposerShape::Ellipse );
94+
95+
QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
96+
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_ellipse.png" ) );
97+
QVERIFY( checker.testComposition() );
98+
}
99+
100+
void TestQgsComposerShapes::roundedRectangle()
101+
{
102+
mComposerShape->setShapeType( QgsComposerShape::Rectangle );
103+
mComposerShape->setCornerRadius( 30 );
104+
105+
QgsCompositionChecker checker( "Composer shapes", mComposition, QString( QString( TEST_DATA_DIR ) + QDir::separator() +
106+
"control_images" + QDir::separator() + "expected_composershapes" + QDir::separator() + "composershape_roundedrectangle.png" ) );
107+
QVERIFY( checker.testComposition() );
108+
mComposerShape->setCornerRadius( 0 );
109+
}
110+
111+
QTEST_MAIN( TestQgsComposerShapes )
112+
#include "moc_testqgscomposershapes.cxx"

tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ADD_PYTHON_TEST(PyQgsComposition test_qgscomposition.py)
1717
ADD_PYTHON_TEST(PyQgsAnalysis test_qgsanalysis.py)
1818
ADD_PYTHON_TEST(PyQgsComposerMap test_qgscomposermap.py)
1919
ADD_PYTHON_TEST(PyQgsComposerEffects test_qgscomposereffects.py)
20+
ADD_PYTHON_TEST(PyQgsComposerShapes test_qgscomposershapes.py)
2021
ADD_PYTHON_TEST(PyQgsSymbolLayerV2 test_qgssymbollayerv2.py)
2122
ADD_PYTHON_TEST(PyQgsPoint test_qgspoint.py)
2223
ADD_PYTHON_TEST(PyQgsAtlasComposition test_qgsatlascomposition.py)

0 commit comments

Comments
 (0)