##// END OF EJS Templates
Added a pie chart customization example and refactoring the pie interface.
Jani Honkonen -
r437:8d9e377a1065
parent child
Show More
@@ -0,0 +1,224
1 #include <QtGui/QApplication>
2 #include <QMainWindow>
3 #include <qchartglobal.h>
4 #include <qchartview.h>
5 #include <qpieseries.h>
6 #include <qpieslice.h>
7 #include <QGridLayout>
8 #include <QFormLayout>
9 #include <QComboBox>
10 #include <QSpinBox>
11 #include <QCheckBox>
12 #include <QGroupBox>
13 #include <QLabel>
14
15 QTCOMMERCIALCHART_USE_NAMESPACE
16
17 Q_DECLARE_METATYPE(QPieSeries::PiePosition)
18
19 class CustomSlice : public QPieSlice
20 {
21 Q_OBJECT
22 public:
23 CustomSlice(qreal value, QString label)
24 :QPieSlice(value, label)
25 {
26 connect(this, SIGNAL(hoverEnter()), this, SLOT(handleHoverEnter()));
27 connect(this, SIGNAL(hoverLeave()), this, SLOT(handleHoverLeave()));
28 }
29
30 public Q_SLOTS:
31
32 void handleHoverEnter()
33 {
34 QBrush brush = this->brush();
35 m_originalBrush = brush;
36 brush.setColor(brush.color().lighter());
37 setBrush(brush);
38 }
39
40 void handleHoverLeave()
41 {
42 setBrush(m_originalBrush);
43 }
44
45 private:
46 QBrush m_originalBrush;
47 };
48
49 class MainWidget : public QWidget
50 {
51 Q_OBJECT
52
53 public:
54 explicit MainWidget(QWidget* parent = 0)
55 :QWidget(parent)
56 {
57 m_chartView = new QChartView();
58 m_chartView->setChartTitle("Piechart customization");
59 //m_chartView->setRenderHint(QPainter::Antialiasing);
60
61 m_series = new QPieSeries();
62 *m_series << new CustomSlice(10.0, "Slice 1");
63 *m_series << new CustomSlice(20.0, "Slice 2");
64 *m_series << new CustomSlice(30.0, "Slice 3");
65 *m_series << new CustomSlice(40.0, "Slice 4");
66 *m_series << new CustomSlice(50.0, "Slice 5");
67 m_chartView->addSeries(m_series);
68
69 m_vPosition = new QComboBox();
70 m_vPosition->addItem("Top", QPieSeries::PiePositionTop);
71 m_vPosition->addItem("Bottom", QPieSeries::PiePositionBottom);
72 m_vPosition->addItem("Center", QPieSeries::PiePositionVCenter);
73
74 m_hPosition = new QComboBox();
75 m_hPosition->addItem("Left", QPieSeries::PiePositionLeft);
76 m_hPosition->addItem("Right", QPieSeries::PiePositionRight);
77 m_hPosition->addItem("Center", QPieSeries::PiePositionHCenter);
78
79 m_sizePolicy = new QComboBox();
80 m_sizePolicy->addItem("Maximized", QPieSeries::PieSizePolicyMaximized);
81 m_sizePolicy->addItem("Space for labels", QPieSeries::PieSizePolicyReserveSpaceForLabels);
82 m_sizePolicy->addItem("Space for exploding", QPieSeries::PieSizePolicyReserveSpaceForExploding);
83 m_sizePolicy->addItem("Space for all", QPieSeries::PieSizePolicyReserveSpaceForAll);
84
85 m_sizeFactor = new QDoubleSpinBox();
86 m_sizeFactor->setMinimum(0.0);
87 m_sizeFactor->setMaximum(1.0);
88 m_sizeFactor->setValue(m_series->sizeFactor());
89 m_sizeFactor->setSingleStep(0.1);
90
91 m_startAngle = new QDoubleSpinBox();
92 m_startAngle->setMinimum(0.0);
93 m_startAngle->setMaximum(360);
94 m_startAngle->setValue(m_series->startAngle());
95 m_startAngle->setSingleStep(1);
96
97 m_endAngle = new QDoubleSpinBox();
98 m_endAngle->setMinimum(0.0);
99 m_endAngle->setMaximum(360);
100 m_endAngle->setValue(m_series->endAngle());
101 m_endAngle->setSingleStep(1);
102
103 QFormLayout* seriesSettingsLayout = new QFormLayout();
104 seriesSettingsLayout->addRow("Vertical position", m_vPosition);
105 seriesSettingsLayout->addRow("Horizontal position", m_hPosition);
106 seriesSettingsLayout->addRow("Size policy", m_sizePolicy);
107 seriesSettingsLayout->addRow("Size factor", m_sizeFactor);
108 seriesSettingsLayout->addRow("Start angle", m_startAngle);
109 seriesSettingsLayout->addRow("End angle", m_endAngle);
110 QGroupBox* seriesSettings = new QGroupBox("Series");
111 seriesSettings->setLayout(seriesSettingsLayout);
112
113 m_sliceName = new QLabel("<click a slice>");
114 m_sliceValue = new QDoubleSpinBox();
115 m_sliceValue->setMaximum(1000);
116 m_sliceLabelVisible = new QCheckBox();
117 m_sliceExploded = new QCheckBox();
118
119 QFormLayout* sliceSettingsLayout = new QFormLayout();
120 sliceSettingsLayout->addRow("Selected", m_sliceName);
121 sliceSettingsLayout->addRow("Value", m_sliceValue);
122 sliceSettingsLayout->addRow("Label visible", m_sliceLabelVisible);
123 sliceSettingsLayout->addRow("Exploded", m_sliceExploded);
124 QGroupBox* sliceSettings = new QGroupBox("Slice");
125 sliceSettings->setLayout(sliceSettingsLayout);
126
127 QGridLayout* baseLayout = new QGridLayout();
128 baseLayout->addWidget(seriesSettings, 0, 0);
129 baseLayout->addWidget(sliceSettings, 1, 0);
130 baseLayout->addWidget(m_chartView, 0, 1, 2, 1);
131 setLayout(baseLayout);
132
133 connect(m_vPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSerieSettings()));
134 connect(m_hPosition, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSerieSettings()));
135 connect(m_sizePolicy, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSerieSettings()));
136 connect(m_sizeFactor, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
137 connect(m_startAngle, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
138 connect(m_endAngle, SIGNAL(valueChanged(double)), this, SLOT(updateSerieSettings()));
139
140 connect(m_sliceValue, SIGNAL(valueChanged(double)), this, SLOT(updateSliceSettings()));
141 connect(m_sliceLabelVisible, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
142 connect(m_sliceExploded, SIGNAL(toggled(bool)), this, SLOT(updateSliceSettings()));
143
144 connect(m_series, SIGNAL(clicked(QPieSlice*)), this, SLOT(handleSliceClicked(QPieSlice*)));
145
146 updateSerieSettings();
147 }
148
149 public Q_SLOTS:
150
151 void updateSerieSettings()
152 {
153 QPieSeries::PiePosition vPos(m_vPosition->itemData(m_vPosition->currentIndex()).toInt());
154 QPieSeries::PiePosition hPos(m_hPosition->itemData(m_hPosition->currentIndex()).toInt());
155 m_series->setPosition(vPos | hPos);
156
157 QPieSeries::PieSizePolicy policy(m_sizePolicy->itemData(m_sizePolicy->currentIndex()).toInt());
158 m_series->setSizePolicy(policy);
159
160 m_series->setSizeFactor(m_sizeFactor->value());
161
162 m_series->setStartAngle(m_startAngle->value());
163 m_series->setEndAngle(m_endAngle->value());
164 }
165
166 void updateSliceSettings()
167 {
168 m_slice->setValue(m_sliceValue->value());
169 m_slice->setLabelVisible(m_sliceLabelVisible->isChecked());
170 m_slice->setExploded(m_sliceExploded->isChecked());
171 }
172
173 void handleSliceClicked(QPieSlice* slice)
174 {
175 m_slice = slice;
176 m_sliceName->setText(slice->label());
177
178 m_sliceValue->blockSignals(true);
179 m_sliceValue->setValue(slice->value());
180 m_sliceValue->blockSignals(false);
181
182 m_sliceLabelVisible->blockSignals(true);
183 m_sliceLabelVisible->setChecked(slice->isLabelVisible());
184 m_sliceLabelVisible->blockSignals(false);
185
186 m_sliceExploded->blockSignals(true);
187 m_sliceExploded->setChecked(slice->isExploded());
188 m_sliceExploded->blockSignals(false);
189 }
190
191 private:
192 QChartView* m_chartView;
193 QPieSeries* m_series;
194 QPieSlice* m_slice;
195
196 QComboBox* m_vPosition;
197 QComboBox* m_hPosition;
198 QComboBox* m_sizePolicy;
199 QDoubleSpinBox* m_sizeFactor;
200 QDoubleSpinBox* m_startAngle;
201 QDoubleSpinBox* m_endAngle;
202
203 QLabel* m_sliceName;
204 QDoubleSpinBox* m_sliceValue;
205 QCheckBox* m_sliceLabelVisible;
206 QCheckBox* m_sliceExploded;
207 };
208
209 int main(int argc, char *argv[])
210 {
211 QApplication a(argc, argv);
212
213 QMainWindow window;
214
215 MainWidget* widget = new MainWidget();
216
217 window.setCentralWidget(widget);
218 window.resize(900, 600);
219 window.show();
220
221 return a.exec();
222 }
223
224 #include "main.moc"
@@ -0,0 +1,8
1 !include( ../example.pri ) {
2 error( "Couldn't find the example.pri file!" )
3 }
4 TARGET = piechartcustomization
5 SOURCES += main.cpp
6 HEADERS +=
7
8
@@ -7,6 +7,7 SUBDIRS += linechart \
7 percentbarchart \
7 percentbarchart \
8 scatter \
8 scatter \
9 piechart \
9 piechart \
10 piechartcustomization \
10 piechartdrilldown \
11 piechartdrilldown \
11 dynamiclinechart \
12 dynamiclinechart \
12 axischart \
13 axischart \
@@ -5,13 +5,11 SOURCES += \
5 $$PWD/qpieseries.cpp \
5 $$PWD/qpieseries.cpp \
6 $$PWD/pieslice.cpp \
6 $$PWD/pieslice.cpp \
7 $$PWD/piepresenter.cpp \
7 $$PWD/piepresenter.cpp \
8 $$PWD/pieslicelabel.cpp \
9 $$PWD/qpieslice.cpp
8 $$PWD/qpieslice.cpp
10
9
11 PRIVATE_HEADERS += \
10 PRIVATE_HEADERS += \
12 $$PWD/piepresenter_p.h \
11 $$PWD/piepresenter_p.h \
13 $$PWD/pieslice_p.h \
12 $$PWD/pieslice_p.h \
14 $$PWD/pieslicelabel_p.h
15
13
16 PUBLIC_HEADERS += \
14 PUBLIC_HEADERS += \
17 $$PWD/qpieseries.h \
15 $$PWD/qpieseries.h \
@@ -1,12 +1,9
1
2 #include "piepresenter_p.h"
1 #include "piepresenter_p.h"
3 #include "pieslice_p.h"
2 #include "pieslice_p.h"
4 #include "qpieslice.h"
3 #include "qpieslice.h"
5 #include "pieslicelabel_p.h"
6 #include "qpieseries.h"
4 #include "qpieseries.h"
7 #include <qmath.h>
8 #include <QDebug>
5 #include <QDebug>
9 #include <QFontMetrics>
6 #include <QPainter>
10
7
11
8
12 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
@@ -19,6 +16,7 PiePresenter::PiePresenter(QGraphicsItem *parent, QPieSeries *series)
19 connect(series, SIGNAL(changed(const QPieSeries::ChangeSet&)), this, SLOT(handleSeriesChanged(const QPieSeries::ChangeSet&)));
16 connect(series, SIGNAL(changed(const QPieSeries::ChangeSet&)), this, SLOT(handleSeriesChanged(const QPieSeries::ChangeSet&)));
20 connect(series, SIGNAL(sizeFactorChanged()), this, SLOT(updateGeometry()));
17 connect(series, SIGNAL(sizeFactorChanged()), this, SLOT(updateGeometry()));
21 connect(series, SIGNAL(positionChanged()), this, SLOT(updateGeometry()));
18 connect(series, SIGNAL(positionChanged()), this, SLOT(updateGeometry()));
19 connect(series, SIGNAL(sizePolicyChanged()), this, SLOT(updateGeometry()));
22
20
23 if (m_series->count()) {
21 if (m_series->count()) {
24 QPieSeries::ChangeSet changeSet;
22 QPieSeries::ChangeSet changeSet;
@@ -36,6 +34,8 void PiePresenter::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QW
36 {
34 {
37 // TODO: paint shadows for all components
35 // TODO: paint shadows for all components
38 // - get paths from items & merge & offset and draw with shadow color?
36 // - get paths from items & merge & offset and draw with shadow color?
37 //painter->setBrush(QBrush(Qt::red));
38 //painter->drawRect(m_debugRect);
39 }
39 }
40
40
41 void PiePresenter::handleSeriesChanged(const QPieSeries::ChangeSet& changeSet)
41 void PiePresenter::handleSeriesChanged(const QPieSeries::ChangeSet& changeSet)
@@ -78,84 +78,104 void PiePresenter::updateGeometry()
78 // calculate maximum rectangle for pie
78 // calculate maximum rectangle for pie
79 QRectF pieRect = m_rect;
79 QRectF pieRect = m_rect;
80 if (pieRect.width() < pieRect.height()) {
80 if (pieRect.width() < pieRect.height()) {
81 pieRect.setWidth(pieRect.width() * m_series->sizeFactor());
82 pieRect.setHeight(pieRect.width());
81 pieRect.setHeight(pieRect.width());
83 pieRect.moveCenter(m_rect.center());
84 } else {
82 } else {
85 pieRect.setHeight(pieRect.height() * m_series->sizeFactor());
86 pieRect.setWidth(pieRect.height());
83 pieRect.setWidth(pieRect.height());
87 pieRect.moveCenter(m_rect.center());
88 }
84 }
89
85
90 // position the pie rectangle
86 // position the pie rectangle
91 switch (m_series->position()) {
87 QPointF center = m_rect.center(); // default position is center
92 case QPieSeries::PiePositionTopLeft: {
88 qreal dx = pieRect.width() / 2;
93 pieRect.setHeight(pieRect.height() / 2);
89 qreal dy = pieRect.height() / 2;
94 pieRect.setWidth(pieRect.height());
90 if (m_series->position() & QPieSeries::PiePositionLeft)
95 pieRect.moveCenter(QPointF(m_rect.center().x() / 2, m_rect.center().y() / 2));
91 center.setX(m_rect.left() + dx);
96 break;
92 if (m_series->position() & QPieSeries::PiePositionRight)
97 }
93 center.setX(m_rect.right() - dx);
98 case QPieSeries::PiePositionTopRight: {
94 if (m_series->position() & QPieSeries::PiePositionHCenter)
99 pieRect.setHeight(pieRect.height() / 2);
95 center.setX(m_rect.center().x());
100 pieRect.setWidth(pieRect.height());
96 if (m_series->position() & QPieSeries::PiePositionTop)
101 pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, m_rect.center().y() / 2));
97 center.setY(m_rect.top() + dy);
102 break;
98 if (m_series->position() & QPieSeries::PiePositionBottom)
103 }
99 center.setY(m_rect.bottom() - dy);
104 case QPieSeries::PiePositionBottomLeft: {
100 if (m_series->position() & QPieSeries::PiePositionVCenter)
105 pieRect.setHeight(pieRect.height() / 2);
101 center.setY(m_rect.center().y());
106 pieRect.setWidth(pieRect.height());
102 pieRect.moveCenter(center);
107 pieRect.moveCenter(QPointF(m_rect.center().x() / 2, (m_rect.center().y() / 2) * 3));
108 break;
109 }
110 case QPieSeries::PiePositionBottomRight: {
111 pieRect.setHeight(pieRect.height() / 2);
112 pieRect.setWidth(pieRect.height());
113 pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, (m_rect.center().y() / 2) * 3));
114 break;
115 }
116 default:
117 break;
118 }
119
103
120 // calculate how much space we need around the pie rectangle (labels & exploding)
104 // calculate how much space we need around the pie rectangle (labels & exploding)
121 qreal delta = 0;
105 qreal delta = 0;
122 qreal pieRadius = pieRect.height() / 2;
106 qreal pieRadius = pieRect.height() / 2;
123 foreach (QPieSlice* s, m_series->m_slices) {
107 foreach (QPieSlice* s, m_series->m_slices) {
124
108
125 // calculate the farthest point in the slice from the pie center
109 bool exploded = s->isExploded();
126
110 if (m_series->sizePolicy() & QPieSeries::PieSizePolicyReserveSpaceForExploding)
127 // the arm
111 exploded = true;
128 qreal centerAngle = s->m_startAngle + (s->m_angleSpan / 2);
112
129 qreal len = pieRadius + PIESLICE_LABEL_GAP + s->labelArmLength() + s->explodeDistance();
113 bool labelVisible = s->isLabelVisible();
130 QPointF dp(qSin(centerAngle*(PI/180)) * len, -qCos(centerAngle*(PI/180)) * len);
114 if (m_series->sizePolicy() & QPieSeries::PieSizePolicyReserveSpaceForLabels)
131 QPointF p = pieRect.center() + dp;
115 labelVisible = true;
132
116
133 // the label text
117 qreal centerAngle;
134 QFontMetricsF fm(s->labelFont());
118 QPointF armStart;
135 QRectF labelRect = fm.boundingRect(s->label());
119 QRectF sliceRect = PieSlice::slicePath(center, pieRadius, s->m_startAngle, s->m_angleSpan, exploded, s->explodeDistance(), &centerAngle, &armStart).boundingRect();
136 if (centerAngle < 90 || centerAngle > 270)
120
137 p += QPointF(0, -labelRect.height());
121 if (labelVisible) {
138 if (centerAngle < 180)
122 QRectF textRect = PieSlice::labelTextRect(s->labelFont(), s->label());
139 p += QPointF(labelRect.width(), 0);
123 QPointF textStart;
140 else
124 QRectF armRect = PieSlice::labelArmPath(armStart, centerAngle, s->labelArmLength(), textRect.width(), &textStart).boundingRect();
141 p += QPointF(-labelRect.width(), 0);
125 textRect.moveBottomLeft(textStart);
142
126 sliceRect = sliceRect.united(armRect);
143 // calculate how much the radius must get smaller to fit that point in the base rectangle
127 sliceRect = sliceRect.united(textRect);
144 qreal dt = m_rect.top() - p.y();
128 }
145 if (dt > delta) delta = dt;
129
146 qreal dl = m_rect.left() - p.x();
130
147 if (dl > delta) delta = dl;
131 qreal dt = m_rect.top() - sliceRect.top();
148 qreal dr = p.x() - m_rect.right();
132 if (dt > delta)
149 if (dr > delta) delta = dr;
133 delta = dt;
150 qreal db = p.y() - m_rect.bottom();
134 qreal dl = m_rect.left() - sliceRect.left();
151 if (db > delta) delta = db;
135 if (dl > delta)
152
136 delta = dl;
153 //if (!m_rect.contains(p)) qDebug() << s->label() << dt << dl << dr << db << "delta" << delta;
137 qreal dr = sliceRect.right() - m_rect.right();
138 if (dr > delta)
139 delta = dr;
140 qreal db = sliceRect.bottom() - m_rect.bottom();
141 if (db > delta)
142 delta = db;
143
144 /*
145 if (s->label() == "Slice 5") {
146 m_debugRect = sliceRect;
147 qDebug() << "dt:" << dt << ", dl:" << dl << ", dr:" << dr << ", db:" << db << ", delta:" << delta;
148 }
149 */
154 }
150 }
155
151
156 // shrink the pie rectangle so that everything outside it fits the base rectangle
152 // shrink the pie rectangle so that everything outside it fits the base rectangle
157 pieRect.adjust(delta, delta, -delta, -delta);
153 pieRect.adjust(delta, delta, -delta, -delta);
158
154
155 /*
156 // apply size factor (range 0.0 ... 1.0)
157 pieRect.setWidth(pieRect.width() * m_series->sizeFactor());
158 pieRect.setHeight(pieRect.height() * m_series->sizeFactor());
159
160 // position the pie rectangle (again)
161 center = m_rect.center(); // default position is center
162 dx = pieRect.width() / 2;
163 dy = pieRect.height() / 2;
164 if (m_series->position() & QPieSeries::PiePositionLeft)
165 center.setX(m_rect.left() + dx);
166 if (m_series->position() & QPieSeries::PiePositionRight)
167 center.setX(m_rect.right() - dx);
168 if (m_series->position() & QPieSeries::PiePositionHCenter)
169 center.setX(m_rect.center().x());
170 if (m_series->position() & QPieSeries::PiePositionTop)
171 center.setY(m_rect.top() + dy);
172 if (m_series->position() & QPieSeries::PiePositionBottom)
173 center.setY(m_rect.bottom() - dy);
174 if (m_series->position() & QPieSeries::PiePositionVCenter)
175 center.setY(m_rect.center().y());
176 pieRect.moveCenter(center);
177 */
178
159 // update slices
179 // update slices
160 if (m_pieRect != pieRect) {
180 if (m_pieRect != pieRect) {
161 m_pieRect = pieRect;
181 m_pieRect = pieRect;
@@ -166,6 +186,8 void PiePresenter::updateGeometry()
166 s->update();
186 s->update();
167 }
187 }
168 }
188 }
189
190 update();
169 }
191 }
170
192
171 void PiePresenter::addSlice(QPieSlice* sliceData)
193 void PiePresenter::addSlice(QPieSlice* sliceData)
@@ -44,6 +44,7 private:
44 QPieSeries *m_series;
44 QPieSeries *m_series;
45 QRectF m_rect;
45 QRectF m_rect;
46 QRectF m_pieRect;
46 QRectF m_pieRect;
47 QRectF m_debugRect;
47 };
48 };
48
49
49 QTCOMMERCIALCHART_END_NAMESPACE
50 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,5 +1,4
1 #include "pieslice_p.h"
1 #include "pieslice_p.h"
2 #include "pieslicelabel_p.h"
3 #include "piepresenter_p.h"
2 #include "piepresenter_p.h"
4 #include "qpieseries.h"
3 #include "qpieseries.h"
5 #include "qpieslice.h"
4 #include "qpieslice.h"
@@ -20,11 +19,11 QPointF offset(qreal angle, qreal length)
20
19
21 PieSlice::PieSlice(QGraphicsItem* parent)
20 PieSlice::PieSlice(QGraphicsItem* parent)
22 :QGraphicsObject(parent),
21 :QGraphicsObject(parent),
23 m_slicelabel(new PieSliceLabel(this)),
24 m_startAngle(0),
22 m_startAngle(0),
25 m_angleSpan(0),
23 m_angleSpan(0),
26 m_isExploded(false),
24 m_isExploded(false),
27 m_explodeDistance(0)
25 m_explodeDistance(0),
26 m_labelVisible(false)
28 {
27 {
29 setAcceptHoverEvents(true);
28 setAcceptHoverEvents(true);
30 setAcceptedMouseButtons(Qt::LeftButton);
29 setAcceptedMouseButtons(Qt::LeftButton);
@@ -37,19 +36,31 PieSlice::~PieSlice()
37
36
38 QRectF PieSlice::boundingRect() const
37 QRectF PieSlice::boundingRect() const
39 {
38 {
40 return m_path.boundingRect();
39 return m_slicePath.boundingRect();
41 }
40 }
42
41
43 QPainterPath PieSlice::shape() const
42 QPainterPath PieSlice::shape() const
44 {
43 {
45 return m_path;
44 return m_slicePath;
46 }
45 }
47
46
48 void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
47 void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
49 {
48 {
49 painter->save();
50 painter->setPen(m_pen);
50 painter->setPen(m_pen);
51 painter->setBrush(m_brush);
51 painter->setBrush(m_brush);
52 painter->drawPath(m_path);
52 painter->drawPath(m_slicePath);
53 painter->restore();
54
55 if (m_labelVisible) {
56 painter->save();
57 painter->setPen(m_labelArmPen);
58 painter->drawPath(m_labelArmPath);
59 painter->restore();
60
61 painter->setFont(m_labelFont);
62 painter->drawText(m_labelTextRect.bottomLeft(), m_labelText);
63 }
53 }
64 }
54
65
55 void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
66 void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
@@ -79,32 +90,22 void PieSlice::updateGeometry()
79
90
80 prepareGeometryChange();
91 prepareGeometryChange();
81
92
82 // calculate center angle
93 // update slice path
83 qreal centerAngle = m_startAngle + (m_angleSpan/2);
94 QPointF center = m_pieRect.center();
95 qreal radius = m_pieRect.height() / 2;
96 qreal centerAngle;
97 QPointF armStart;
98 m_slicePath = slicePath(center, radius, m_startAngle, m_angleSpan, m_isExploded, m_explodeDistance, &centerAngle, &armStart);
84
99
85 // adjust rect for exploding
100 // update text rect
86 QRectF rect = m_pieRect;
101 m_labelTextRect = labelTextRect(m_labelFont, m_labelText);
87 if (m_isExploded) {
88 qreal dx = qSin(centerAngle*(PI/180)) * m_explodeDistance;
89 qreal dy = -qCos(centerAngle*(PI/180)) * m_explodeDistance;
90 rect.translate(dx, dy);
91 }
92
102
93 // update slice path
103 // update label arm path
94 // TODO: draw the shape so that it might have a hole in the center
104 QPointF labelTextStart;
95 QPainterPath path;
105 m_labelArmPath = labelArmPath(armStart, centerAngle, m_labelArmLength, m_labelTextRect.width(), &labelTextStart);
96 path.moveTo(rect.center());
97 path.arcTo(rect, -m_startAngle + 90, -m_angleSpan);
98 path.closeSubpath();
99 m_path = path;
100
106
101 // update label position
107 // update text position
102 qreal radius = rect.height() / 2;
108 m_labelTextRect.moveBottomLeft(labelTextStart);
103 QPointF edgeCenter = rect.center() + offset(centerAngle, radius + PIESLICE_LABEL_GAP);
104 m_slicelabel->setArmStartPoint(edgeCenter);
105 m_slicelabel->setArmAngle(centerAngle);
106 m_slicelabel->updateGeometry();
107 m_slicelabel->update();
108
109
109 //qDebug() << "PieSlice::updateGeometry" << m_slicelabel->text() << boundingRect() << m_angle << m_span;
110 //qDebug() << "PieSlice::updateGeometry" << m_slicelabel->text() << boundingRect() << m_angle << m_span;
110 }
111 }
@@ -116,20 +117,85 void PieSlice::updateData(const QPieSlice* sliceData)
116 m_startAngle = sliceData->startAngle();
117 m_startAngle = sliceData->startAngle();
117 m_angleSpan = sliceData->m_angleSpan;
118 m_angleSpan = sliceData->m_angleSpan;
118 m_isExploded = sliceData->isExploded();
119 m_isExploded = sliceData->isExploded();
119 m_explodeDistance = sliceData->explodeDistance(); // TODO: expose to public API
120 m_explodeDistance = sliceData->explodeDistance();
120 m_pen = sliceData->pen();
121 m_pen = sliceData->pen();
121 m_brush = sliceData->brush();
122 m_brush = sliceData->brush();
122
123
123 m_slicelabel->setVisible(sliceData->isLabelVisible());
124 m_labelVisible = sliceData->isLabelVisible();
124 m_slicelabel->setText(sliceData->label());
125 m_labelText = sliceData->label();
125 m_slicelabel->setPen(sliceData->labelPen());
126 m_labelFont = sliceData->labelFont();
126 m_slicelabel->setFont(sliceData->labelFont());
127 m_labelArmLength = sliceData->labelArmLength();
127 m_slicelabel->setArmLength(sliceData->labelArmLength());
128 m_labelArmPen = sliceData->labelPen();
128
129
129 updateGeometry();
130 updateGeometry();
130 update();
131 update();
131 }
132 }
132
133
134 QPainterPath PieSlice::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, bool exploded, qreal explodeDistance, qreal* centerAngle, QPointF* armStart)
135 {
136 // calculate center angle
137 *centerAngle = startAngle + (angleSpan/2);
138
139 // calculate slice rectangle
140 QRectF rect(center.x()-radius, center.y()-radius, radius*2, radius*2);
141
142 // adjust rect for exploding
143 if (exploded) {
144 qreal dx = qSin(*centerAngle*(PI/180)) * explodeDistance;
145 qreal dy = -qCos(*centerAngle*(PI/180)) * explodeDistance;
146 rect.translate(dx, dy);
147 }
148
149 // slice path
150 // TODO: draw the shape so that it might have a hole in the center
151 QPainterPath path;
152 path.moveTo(rect.center());
153 path.arcTo(rect, -startAngle + 90, -angleSpan);
154 path.closeSubpath();
155
156 // calculate label arm start point
157 *armStart = center;
158 if (exploded)
159 *armStart += offset(*centerAngle, explodeDistance + radius + PIESLICE_LABEL_GAP);
160 else
161 *armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
162
163 return path;
164 }
165
166 QPainterPath PieSlice::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF* textStart)
167 {
168 qreal dx = qSin(angle*(PI/180)) * length;
169 qreal dy = -qCos(angle*(PI/180)) * length;
170 QPointF parm1 = start + QPointF(dx, dy);
171
172 QPointF parm2 = parm1;
173 if (angle < 180) { // arm swings the other way on the left side
174 parm2 += QPointF(textWidth, 0);
175 *textStart = parm1;
176 }
177 else {
178 parm2 += QPointF(-textWidth,0);
179 *textStart = parm2;
180 }
181
182 // elevate the text position a bit so that it does not hit the line
183 *textStart += QPointF(0, -5);
184
185 QPainterPath path;
186 path.moveTo(start);
187 path.lineTo(parm1);
188 path.lineTo(parm2);
189
190 return path;
191 }
192
193 QRectF PieSlice::labelTextRect(QFont font, QString text)
194 {
195 QFontMetricsF fm(font);
196 return fm.boundingRect(text);
197 }
198
133 #include "moc_pieslice_p.cpp"
199 #include "moc_pieslice_p.cpp"
134
200
135 QTCOMMERCIALCHART_END_NAMESPACE
201 QTCOMMERCIALCHART_END_NAMESPACE
@@ -43,22 +43,29 public Q_SLOTS:
43 void updateData(const QPieSlice *sliceData);
43 void updateData(const QPieSlice *sliceData);
44
44
45 public:
45 public:
46 PieSliceLabel* label() { return m_slicelabel; }
46 static QPainterPath slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, bool exploded, qreal explodeDistance, qreal* centerAngle, QPointF* armStart);
47 static QPainterPath labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF* textStart);
48 static QRectF labelTextRect(QFont font, QString text);
47
49
48 private:
50 private:
49 PieSliceLabel* m_slicelabel;
50
51 QRectF m_pieRect;
51 QRectF m_pieRect;
52 QPainterPath m_path;
53
52
53 QPainterPath m_slicePath;
54 qreal m_startAngle;
54 qreal m_startAngle;
55 qreal m_angleSpan;
55 qreal m_angleSpan;
56
57 bool m_isExploded;
56 bool m_isExploded;
58 qreal m_explodeDistance;
57 qreal m_explodeDistance;
59
58 bool m_labelVisible;
60 QPen m_pen;
59 QPen m_pen;
61 QBrush m_brush;
60 QBrush m_brush;
61
62 QPainterPath m_labelArmPath;
63 qreal m_labelArmLength;
64 QPen m_labelArmPen;
65
66 QRectF m_labelTextRect;
67 QFont m_labelFont;
68 QString m_labelText;
62 };
69 };
63
70
64 QTCOMMERCIALCHART_END_NAMESPACE
71 QTCOMMERCIALCHART_END_NAMESPACE
@@ -118,9 +118,10 bool QPieSeries::ChangeSet::isEmpty() const
118 QPieSeries::QPieSeries(QObject *parent) :
118 QPieSeries::QPieSeries(QObject *parent) :
119 QSeries(parent),
119 QSeries(parent),
120 m_sizeFactor(1.0),
120 m_sizeFactor(1.0),
121 m_position(PiePositionMaximized),
121 m_position(PiePositionCenter),
122 m_sizePolicy(PieSizePolicyMaximized),
122 m_pieStartAngle(0),
123 m_pieStartAngle(0),
123 m_pieAngleSpan(360)
124 m_pieEndAngle(360)
124 {
125 {
125
126
126 }
127 }
@@ -295,6 +296,7 qreal QPieSeries::sizeFactor() const
295 */
296 */
296 void QPieSeries::setPosition(PiePosition position)
297 void QPieSeries::setPosition(PiePosition position)
297 {
298 {
299 // TODO: sanity check
298 if (m_position != position) {
300 if (m_position != position) {
299 m_position = position;
301 m_position = position;
300 emit positionChanged();
302 emit positionChanged();
@@ -310,22 +312,55 QPieSeries::PiePosition QPieSeries::position() const
310 return m_position;
312 return m_position;
311 }
313 }
312
314
313
314 /*!
315 /*!
315 Sets the \a startAngle and \a angleSpan of this series.
316 Sets the \a sizePolicy of the pie.
317 \sa PieSizePolicy, sizePolicy()
318 */
319 void QPieSeries::setSizePolicy(PieSizePolicy sizePolicy)
320 {
321 // TODO: sanity check
322 if (m_sizePolicy != sizePolicy) {
323 m_sizePolicy = sizePolicy;
324 emit sizePolicyChanged();
325 }
326 }
316
327
317 Full pie is 360 degrees where 0 degrees is at 12 a'clock.
328 /*!
329 Gets the size policy of the pie.
330 \sa PieSizePolicy, setSizePolicy()
318 */
331 */
319 void QPieSeries::setSpan(qreal startAngle, qreal angleSpan)
332 QPieSeries::PieSizePolicy QPieSeries::sizePolicy() const
320 {
333 {
321 if (startAngle >= 0 && startAngle < 360 &&
334 return m_sizePolicy;
322 angleSpan > 0 && angleSpan <= 360) {
335 }
336
337
338 void QPieSeries::setStartAngle(qreal startAngle)
339 {
340 if (startAngle >= 0 && startAngle <= 360 && startAngle != m_pieStartAngle && startAngle <= m_pieEndAngle) {
323 m_pieStartAngle = startAngle;
341 m_pieStartAngle = startAngle;
324 m_pieAngleSpan = angleSpan;
325 updateDerivativeData();
342 updateDerivativeData();
326 }
343 }
327 }
344 }
328
345
346 qreal QPieSeries::startAngle() const
347 {
348 return m_pieStartAngle;
349 }
350
351 void QPieSeries::setEndAngle(qreal endAngle)
352 {
353 if (endAngle >= 0 && endAngle <= 360 && endAngle != m_pieEndAngle && endAngle >= m_pieStartAngle) {
354 m_pieEndAngle = endAngle;
355 updateDerivativeData();
356 }
357 }
358
359 qreal QPieSeries::endAngle() const
360 {
361 return m_pieEndAngle;
362 }
363
329 /*!
364 /*!
330 Sets the all the slice labels \a visible or invisible.
365 Sets the all the slice labels \a visible or invisible.
331
366
@@ -449,6 +484,7 void QPieSeries::updateDerivativeData()
449
484
450 // update slice attributes
485 // update slice attributes
451 qreal sliceAngle = m_pieStartAngle;
486 qreal sliceAngle = m_pieStartAngle;
487 qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
452 foreach (QPieSlice* s, m_slices) {
488 foreach (QPieSlice* s, m_slices) {
453
489
454 bool changed = false;
490 bool changed = false;
@@ -459,7 +495,7 void QPieSeries::updateDerivativeData()
459 changed = true;
495 changed = true;
460 }
496 }
461
497
462 qreal sliceSpan = m_pieAngleSpan * percentage;
498 qreal sliceSpan = pieSpan * percentage;
463 if (s->m_angleSpan != sliceSpan) {
499 if (s->m_angleSpan != sliceSpan) {
464 s->m_angleSpan = sliceSpan;
500 s->m_angleSpan = sliceSpan;
465 changed = true;
501 changed = true;
@@ -20,14 +20,28 class QTCOMMERCIALCHART_EXPORT QPieSeries : public QSeries
20 Q_OBJECT
20 Q_OBJECT
21
21
22 public:
22 public:
23 enum PiePosition {
23
24 PiePositionMaximized = 0,
24 enum PiePositionFlag {
25 PiePositionTopLeft,
25 PiePositionLeft = 0x1,
26 PiePositionTopRight,
26 PiePositionRight = 0x2,
27 PiePositionBottomLeft,
27 PiePositionHCenter = 0x4,
28 PiePositionBottomRight
28 PiePositionTop = 0x10,
29 PiePositionBottom = 0x20,
30 PiePositionVCenter = 0x40,
31 PiePositionCenter = PiePositionHCenter | PiePositionVCenter
32 };
33
34 Q_DECLARE_FLAGS(PiePosition, PiePositionFlag)
35
36 enum PieSizePolicyFlag {
37 PieSizePolicyMaximized = 0,
38 PieSizePolicyReserveSpaceForLabels = 0x1,
39 PieSizePolicyReserveSpaceForExploding = 0x2,
40 PieSizePolicyReserveSpaceForAll = PieSizePolicyReserveSpaceForLabels | PieSizePolicyReserveSpaceForExploding
29 };
41 };
30
42
43 Q_DECLARE_FLAGS(PieSizePolicy, PieSizePolicyFlag)
44
31 class ChangeSet
45 class ChangeSet
32 {
46 {
33 public:
47 public:
@@ -58,27 +72,38 public: // from QChartSeries
58 QSeriesType type() const;
72 QSeriesType type() const;
59
73
60 public:
74 public:
61 void replace(QList<QPieSlice*> slices);
75
62 void add(QList<QPieSlice*> slices);
76 // slice setters
63 void add(QPieSlice* slice);
77 void add(QPieSlice* slice);
64 QPieSlice* add(qreal value, QString name);
78 void add(QList<QPieSlice*> slices);
65 QPieSeries& operator << (QPieSlice* slice);
79 void replace(QList<QPieSlice*> slices);
66 void remove(QPieSlice* slice);
80 void remove(QPieSlice* slice);
67 void clear();
81 void clear();
68
82
69 int count() const;
83 // sluce getters
70 QList<QPieSlice*> slices() const;
84 QList<QPieSlice*> slices() const;
71
85
72 void setSizeFactor(qreal sizeFactor);
86 // calculated data
73 qreal sizeFactor() const;
87 int count() const;
88 qreal total() const;
89
90 // pie customization
74 void setPosition(PiePosition position);
91 void setPosition(PiePosition position);
75 PiePosition position() const;
92 PiePosition position() const;
76 void setSpan(qreal startAngle, qreal angleSpan);
93 void setSizePolicy(PieSizePolicy policy);
94 PieSizePolicy sizePolicy() const;
95 void setSizeFactor(qreal sizeFactor);
96 qreal sizeFactor() const;
97 void setStartAngle(qreal startAngle);
98 qreal startAngle() const;
99 void setEndAngle(qreal endAngle);
100 qreal endAngle() const;
77
101
102 // convenience function
103 QPieSeries& operator << (QPieSlice* slice);
104 QPieSlice* add(qreal value, QString name);
78 void setLabelsVisible(bool visible = true);
105 void setLabelsVisible(bool visible = true);
79
106
80 qreal total() const;
81
82 // TODO: find slices?
107 // TODO: find slices?
83 // QList<QPieSlice*> findByValue(qreal value);
108 // QList<QPieSlice*> findByValue(qreal value);
84 // ...
109 // ...
@@ -88,15 +113,19 public:
88
113
89 // TODO: general graphics customization
114 // TODO: general graphics customization
90 // setDrawStyle(2d|3d)
115 // setDrawStyle(2d|3d)
91 // setDropShadows(bool)
116 // setDropShadows
92
117
93 Q_SIGNALS:
118 Q_SIGNALS:
119
94 void changed(const QPieSeries::ChangeSet& changeSet);
120 void changed(const QPieSeries::ChangeSet& changeSet);
121
95 void clicked(QPieSlice* slice);
122 void clicked(QPieSlice* slice);
96 void hoverEnter(QPieSlice* slice);
123 void hoverEnter(QPieSlice* slice);
97 void hoverLeave(QPieSlice* slice);
124 void hoverLeave(QPieSlice* slice);
125
98 void sizeFactorChanged();
126 void sizeFactorChanged();
99 void positionChanged();
127 void positionChanged();
128 void sizePolicyChanged();
100
129
101 private Q_SLOTS: // TODO: should be private and not visible in the interface at all
130 private Q_SLOTS: // TODO: should be private and not visible in the interface at all
102 void sliceChanged();
131 void sliceChanged();
@@ -117,9 +146,10 private:
117 QList<QPieSlice*> m_slices;
146 QList<QPieSlice*> m_slices;
118 qreal m_sizeFactor;
147 qreal m_sizeFactor;
119 PiePosition m_position;
148 PiePosition m_position;
149 PieSizePolicy m_sizePolicy;
120 qreal m_total;
150 qreal m_total;
121 qreal m_pieStartAngle;
151 qreal m_pieStartAngle;
122 qreal m_pieAngleSpan;
152 qreal m_pieEndAngle;
123 };
153 };
124
154
125 QTCOMMERCIALCHART_END_NAMESPACE
155 QTCOMMERCIALCHART_END_NAMESPACE
@@ -21,10 +21,15 public:
21 virtual ~QPieSlice();
21 virtual ~QPieSlice();
22
22
23 // data
23 // data
24 void setValue(qreal value);
24 qreal value() const;
25 qreal value() const;
26 void setLabel(QString label);
25 QString label() const;
27 QString label() const;
28 void setLabelVisible(bool visible);
26 bool isLabelVisible() const;
29 bool isLabelVisible() const;
30 void setExploded(bool exploded);
27 bool isExploded() const;
31 bool isExploded() const;
32 void setExplodeDistance(qreal distance);
28 qreal explodeDistance() const;
33 qreal explodeDistance() const;
29
34
30 // generated data
35 // generated data
@@ -33,38 +38,27 public:
33 qreal endAngle() const;
38 qreal endAngle() const;
34
39
35 // customization
40 // customization
41 void setPen(QPen pen);
36 QPen pen() const;
42 QPen pen() const;
43 void setBrush(QBrush brush);
37 QBrush brush() const;
44 QBrush brush() const;
45 void setLabelPen(QPen pen);
38 QPen labelPen() const;
46 QPen labelPen() const;
47 void setLabelFont(QFont font);
39 QFont labelFont() const;
48 QFont labelFont() const;
49 void setLabelArmLength(qreal len);
40 qreal labelArmLength() const;
50 qreal labelArmLength() const;
41
51
52 // TODO: label position in general
53 // setLabelFlags(inside|outside|labelArmOn|labelArmOff|???)
54 // setLabelOrientation(horizontal|vertical|same as slice center angle|???)
55
42 Q_SIGNALS:
56 Q_SIGNALS:
43 void clicked();
57 void clicked();
44 void hoverEnter();
58 void hoverEnter();
45 void hoverLeave();
59 void hoverLeave();
46 void changed();
60 void changed();
47
61
48 public Q_SLOTS:
49
50 // data
51 void setLabel(QString label);
52 void setLabelVisible(bool visible);
53 void setValue(qreal value);
54 void setExploded(bool exploded);
55 void setExplodeDistance(qreal distance);
56
57 // customization
58 void setPen(QPen pen);
59 void setBrush(QBrush brush);
60 void setLabelFont(QFont font);
61 void setLabelPen(QPen pen);
62 void setLabelArmLength(qreal len);
63
64 // TODO: label position in general
65 // setLabelFlags(inside|outside|labelArmOn|labelArmOff|???)
66 // setLabelOrientation(horizontal|vertical|same as slice center angle|???)
67
68 private:
62 private:
69
63
70 // TODO: use private class
64 // TODO: use private class
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now