##// 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
@@ -1,20 +1,21
1 1 TEMPLATE = subdirs
2 2 SUBDIRS += linechart \
3 3 zoomlinechart \
4 4 colorlinechart \
5 5 barchart \
6 6 stackedbarchart \
7 7 percentbarchart \
8 8 scatter \
9 9 piechart \
10 piechartcustomization \
10 11 piechartdrilldown \
11 12 dynamiclinechart \
12 13 axischart \
13 14 multichart \
14 15 gdpbarchart \
15 16 presenterchart \
16 17 chartview \
17 18 scatterinteractions \
18 19 splinechart \
19 20 areachart \
20 21 stackedbarchartdrilldown
@@ -1,18 +1,16
1 1 INCLUDEPATH += $$PWD
2 2 DEPENDPATH += $$PWD
3 3
4 4 SOURCES += \
5 5 $$PWD/qpieseries.cpp \
6 6 $$PWD/pieslice.cpp \
7 7 $$PWD/piepresenter.cpp \
8 $$PWD/pieslicelabel.cpp \
9 8 $$PWD/qpieslice.cpp
10 9
11 10 PRIVATE_HEADERS += \
12 11 $$PWD/piepresenter_p.h \
13 12 $$PWD/pieslice_p.h \
14 $$PWD/pieslicelabel_p.h
15 13
16 14 PUBLIC_HEADERS += \
17 15 $$PWD/qpieseries.h \
18 16 $$PWD/qpieslice.h
@@ -1,220 +1,242
1
2 1 #include "piepresenter_p.h"
3 2 #include "pieslice_p.h"
4 3 #include "qpieslice.h"
5 #include "pieslicelabel_p.h"
6 4 #include "qpieseries.h"
7 #include <qmath.h>
8 5 #include <QDebug>
9 #include <QFontMetrics>
6 #include <QPainter>
10 7
11 8
12 9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
13 10
14 11 PiePresenter::PiePresenter(QGraphicsItem *parent, QPieSeries *series)
15 12 :ChartItem(parent),
16 13 m_series(series)
17 14 {
18 15 Q_ASSERT(series);
19 16 connect(series, SIGNAL(changed(const QPieSeries::ChangeSet&)), this, SLOT(handleSeriesChanged(const QPieSeries::ChangeSet&)));
20 17 connect(series, SIGNAL(sizeFactorChanged()), this, SLOT(updateGeometry()));
21 18 connect(series, SIGNAL(positionChanged()), this, SLOT(updateGeometry()));
19 connect(series, SIGNAL(sizePolicyChanged()), this, SLOT(updateGeometry()));
22 20
23 21 if (m_series->count()) {
24 22 QPieSeries::ChangeSet changeSet;
25 23 changeSet.appendAdded(m_series->m_slices);
26 24 handleSeriesChanged(changeSet);
27 25 }
28 26 }
29 27
30 28 PiePresenter::~PiePresenter()
31 29 {
32 30 // slices deleted automatically through QGraphicsItem
33 31 }
34 32
35 33 void PiePresenter::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
36 34 {
37 35 // TODO: paint shadows for all components
38 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 41 void PiePresenter::handleSeriesChanged(const QPieSeries::ChangeSet& changeSet)
42 42 {
43 43 //qDebug() << "PiePresenter::handleSeriesChanged()";
44 44 //qDebug() << " added : " << changeSet.added();
45 45 //qDebug() << " changed: " << changeSet.changed();
46 46 //qDebug() << " removed: " << changeSet.removed();
47 47
48 48 foreach (QPieSlice* s, changeSet.added())
49 49 addSlice(s);
50 50
51 51 foreach (QPieSlice* s, changeSet.changed())
52 52 updateSlice(s);
53 53
54 54 foreach (QPieSlice* s, changeSet.removed())
55 55 deleteSlice(s);
56 56
57 57 // every change possibly changes the actual pie size
58 58 updateGeometry();
59 59 }
60 60
61 61 void PiePresenter::handleDomainChanged(const Domain& domain)
62 62 {
63 63 // TODO
64 64 }
65 65
66 66 void PiePresenter::handleGeometryChanged(const QRectF& rect)
67 67 {
68 68 m_rect = rect;
69 69 prepareGeometryChange();
70 70 updateGeometry();
71 71 }
72 72
73 73 void PiePresenter::updateGeometry()
74 74 {
75 75 if (!m_rect.isValid() || m_rect.isEmpty())
76 76 return;
77 77
78 78 // calculate maximum rectangle for pie
79 79 QRectF pieRect = m_rect;
80 80 if (pieRect.width() < pieRect.height()) {
81 pieRect.setWidth(pieRect.width() * m_series->sizeFactor());
82 81 pieRect.setHeight(pieRect.width());
83 pieRect.moveCenter(m_rect.center());
84 82 } else {
85 pieRect.setHeight(pieRect.height() * m_series->sizeFactor());
86 83 pieRect.setWidth(pieRect.height());
87 pieRect.moveCenter(m_rect.center());
88 84 }
89 85
90 86 // position the pie rectangle
91 switch (m_series->position()) {
92 case QPieSeries::PiePositionTopLeft: {
93 pieRect.setHeight(pieRect.height() / 2);
94 pieRect.setWidth(pieRect.height());
95 pieRect.moveCenter(QPointF(m_rect.center().x() / 2, m_rect.center().y() / 2));
96 break;
97 }
98 case QPieSeries::PiePositionTopRight: {
99 pieRect.setHeight(pieRect.height() / 2);
100 pieRect.setWidth(pieRect.height());
101 pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, m_rect.center().y() / 2));
102 break;
103 }
104 case QPieSeries::PiePositionBottomLeft: {
105 pieRect.setHeight(pieRect.height() / 2);
106 pieRect.setWidth(pieRect.height());
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 }
87 QPointF center = m_rect.center(); // default position is center
88 qreal dx = pieRect.width() / 2;
89 qreal dy = pieRect.height() / 2;
90 if (m_series->position() & QPieSeries::PiePositionLeft)
91 center.setX(m_rect.left() + dx);
92 if (m_series->position() & QPieSeries::PiePositionRight)
93 center.setX(m_rect.right() - dx);
94 if (m_series->position() & QPieSeries::PiePositionHCenter)
95 center.setX(m_rect.center().x());
96 if (m_series->position() & QPieSeries::PiePositionTop)
97 center.setY(m_rect.top() + dy);
98 if (m_series->position() & QPieSeries::PiePositionBottom)
99 center.setY(m_rect.bottom() - dy);
100 if (m_series->position() & QPieSeries::PiePositionVCenter)
101 center.setY(m_rect.center().y());
102 pieRect.moveCenter(center);
119 103
120 104 // calculate how much space we need around the pie rectangle (labels & exploding)
121 105 qreal delta = 0;
122 106 qreal pieRadius = pieRect.height() / 2;
123 107 foreach (QPieSlice* s, m_series->m_slices) {
124 108
125 // calculate the farthest point in the slice from the pie center
126
127 // the arm
128 qreal centerAngle = s->m_startAngle + (s->m_angleSpan / 2);
129 qreal len = pieRadius + PIESLICE_LABEL_GAP + s->labelArmLength() + s->explodeDistance();
130 QPointF dp(qSin(centerAngle*(PI/180)) * len, -qCos(centerAngle*(PI/180)) * len);
131 QPointF p = pieRect.center() + dp;
132
133 // the label text
134 QFontMetricsF fm(s->labelFont());
135 QRectF labelRect = fm.boundingRect(s->label());
136 if (centerAngle < 90 || centerAngle > 270)
137 p += QPointF(0, -labelRect.height());
138 if (centerAngle < 180)
139 p += QPointF(labelRect.width(), 0);
140 else
141 p += QPointF(-labelRect.width(), 0);
142
143 // calculate how much the radius must get smaller to fit that point in the base rectangle
144 qreal dt = m_rect.top() - p.y();
145 if (dt > delta) delta = dt;
146 qreal dl = m_rect.left() - p.x();
147 if (dl > delta) delta = dl;
148 qreal dr = p.x() - m_rect.right();
149 if (dr > delta) delta = dr;
150 qreal db = p.y() - m_rect.bottom();
151 if (db > delta) delta = db;
152
153 //if (!m_rect.contains(p)) qDebug() << s->label() << dt << dl << dr << db << "delta" << delta;
109 bool exploded = s->isExploded();
110 if (m_series->sizePolicy() & QPieSeries::PieSizePolicyReserveSpaceForExploding)
111 exploded = true;
112
113 bool labelVisible = s->isLabelVisible();
114 if (m_series->sizePolicy() & QPieSeries::PieSizePolicyReserveSpaceForLabels)
115 labelVisible = true;
116
117 qreal centerAngle;
118 QPointF armStart;
119 QRectF sliceRect = PieSlice::slicePath(center, pieRadius, s->m_startAngle, s->m_angleSpan, exploded, s->explodeDistance(), &centerAngle, &armStart).boundingRect();
120
121 if (labelVisible) {
122 QRectF textRect = PieSlice::labelTextRect(s->labelFont(), s->label());
123 QPointF textStart;
124 QRectF armRect = PieSlice::labelArmPath(armStart, centerAngle, s->labelArmLength(), textRect.width(), &textStart).boundingRect();
125 textRect.moveBottomLeft(textStart);
126 sliceRect = sliceRect.united(armRect);
127 sliceRect = sliceRect.united(textRect);
128 }
129
130
131 qreal dt = m_rect.top() - sliceRect.top();
132 if (dt > delta)
133 delta = dt;
134 qreal dl = m_rect.left() - sliceRect.left();
135 if (dl > delta)
136 delta = dl;
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 152 // shrink the pie rectangle so that everything outside it fits the base rectangle
157 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 179 // update slices
160 180 if (m_pieRect != pieRect) {
161 181 m_pieRect = pieRect;
162 182 //qDebug() << "PiePresenter::updateGeometry()" << m_rect << m_pieRect;
163 183 foreach (PieSlice* s, m_slices.values()) {
164 184 s->setPieRect(m_pieRect);
165 185 s->updateGeometry();
166 186 s->update();
167 187 }
168 188 }
189
190 update();
169 191 }
170 192
171 193 void PiePresenter::addSlice(QPieSlice* sliceData)
172 194 {
173 195 //qDebug() << "PiePresenter::addSlice()" << sliceData;
174 196
175 197 if (m_slices.keys().contains(sliceData)) {
176 198 Q_ASSERT(0); // TODO: how to handle this nicely?
177 199 return;
178 200 }
179 201
180 202 // create slice
181 203 PieSlice *slice = new PieSlice(this);
182 204 slice->setPieRect(m_pieRect);
183 205 slice->updateData(sliceData);
184 206 slice->updateGeometry();
185 207 slice->update();
186 208 m_slices.insert(sliceData, slice);
187 209
188 210 // connect signals
189 211 connect(slice, SIGNAL(clicked()), sliceData, SIGNAL(clicked()));
190 212 connect(slice, SIGNAL(hoverEnter()), sliceData, SIGNAL(hoverEnter()));
191 213 connect(slice, SIGNAL(hoverLeave()), sliceData, SIGNAL(hoverLeave()));
192 214 }
193 215
194 216 void PiePresenter::updateSlice(QPieSlice* sliceData)
195 217 {
196 218 //qDebug() << "PiePresenter::updateSlice()" << sliceData;
197 219
198 220 if (!m_slices.contains(sliceData)) {
199 221 Q_ASSERT(0); // TODO: how to handle this nicely?
200 222 return;
201 223 }
202 224
203 225 m_slices[sliceData]->updateData(sliceData);
204 226 }
205 227
206 228 void PiePresenter::deleteSlice(QPieSlice* sliceData)
207 229 {
208 230 //qDebug() << "PiePresenter::deleteSlice()" << sliceData;
209 231
210 232 if (!m_slices.contains(sliceData)) {
211 233 Q_ASSERT(0); // TODO: how to handle this nicely?
212 234 return;
213 235 }
214 236
215 237 delete m_slices.take(sliceData);
216 238 }
217 239
218 240 #include "moc_piepresenter_p.cpp"
219 241
220 242 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,51 +1,52
1 1 #ifndef PIEPRESENTER_H
2 2 #define PIEPRESENTER_H
3 3
4 4 #include "chartitem_p.h"
5 5 #include "qpieseries.h"
6 6 #include <QSignalMapper>
7 7
8 8 class QGraphicsItem;
9 9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
10 10 class PieSlice;
11 11
12 12 #define PI 3.14159265 // TODO: is this defined in some header?
13 13
14 14 class PiePresenter : public QObject, public ChartItem
15 15 {
16 16 Q_OBJECT
17 17
18 18 public:
19 19 // TODO: use a generic data class instead of x and y
20 20 PiePresenter(QGraphicsItem *parent, QPieSeries *series);
21 21 ~PiePresenter();
22 22
23 23 public: // from QGraphicsItem
24 24 QRectF boundingRect() const { return m_rect; }
25 25 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
26 26
27 27 public:
28 28 QRectF pieRect() const { return m_pieRect; }
29 29
30 30 public Q_SLOTS:
31 31 void handleSeriesChanged(const QPieSeries::ChangeSet& changeSet);
32 32 void handleDomainChanged(const Domain& domain);
33 33 void handleGeometryChanged(const QRectF& rect);
34 34 void updateGeometry();
35 35
36 36 private:
37 37 void addSlice(QPieSlice* sliceData);
38 38 void updateSlice(QPieSlice* sliceData);
39 39 void deleteSlice(QPieSlice* sliceData);
40 40
41 41 private:
42 42 friend class PieSlice;
43 43 QHash<QPieSlice*, PieSlice*> m_slices;
44 44 QPieSeries *m_series;
45 45 QRectF m_rect;
46 46 QRectF m_pieRect;
47 QRectF m_debugRect;
47 48 };
48 49
49 50 QTCOMMERCIALCHART_END_NAMESPACE
50 51
51 52 #endif // PIEPRESENTER_H
@@ -1,135 +1,201
1 1 #include "pieslice_p.h"
2 #include "pieslicelabel_p.h"
3 2 #include "piepresenter_p.h"
4 3 #include "qpieseries.h"
5 4 #include "qpieslice.h"
6 5 #include <QPainter>
7 6 #include <QDebug>
8 7 #include <qmath.h>
9 8 #include <QGraphicsSceneEvent>
10 9 #include <QTime>
11 10
12 11 QTCOMMERCIALCHART_BEGIN_NAMESPACE
13 12
14 13 QPointF offset(qreal angle, qreal length)
15 14 {
16 15 qreal dx = qSin(angle*(PI/180)) * length;
17 16 qreal dy = qCos(angle*(PI/180)) * length;
18 17 return QPointF(dx, -dy);
19 18 }
20 19
21 20 PieSlice::PieSlice(QGraphicsItem* parent)
22 21 :QGraphicsObject(parent),
23 m_slicelabel(new PieSliceLabel(this)),
24 22 m_startAngle(0),
25 23 m_angleSpan(0),
26 24 m_isExploded(false),
27 m_explodeDistance(0)
25 m_explodeDistance(0),
26 m_labelVisible(false)
28 27 {
29 28 setAcceptHoverEvents(true);
30 29 setAcceptedMouseButtons(Qt::LeftButton);
31 30 }
32 31
33 32 PieSlice::~PieSlice()
34 33 {
35 34
36 35 }
37 36
38 37 QRectF PieSlice::boundingRect() const
39 38 {
40 return m_path.boundingRect();
39 return m_slicePath.boundingRect();
41 40 }
42 41
43 42 QPainterPath PieSlice::shape() const
44 43 {
45 return m_path;
44 return m_slicePath;
46 45 }
47 46
48 47 void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
49 48 {
49 painter->save();
50 50 painter->setPen(m_pen);
51 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 66 void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
56 67 {
57 68 emit hoverEnter();
58 69 }
59 70
60 71 void PieSlice::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
61 72 {
62 73 emit hoverLeave();
63 74 }
64 75
65 76 void PieSlice::mousePressEvent(QGraphicsSceneMouseEvent* /*event*/)
66 77 {
67 78 emit clicked();
68 79 }
69 80
70 81 void PieSlice::setPieRect(QRectF rect)
71 82 {
72 83 m_pieRect = rect;
73 84 }
74 85
75 86 void PieSlice::updateGeometry()
76 87 {
77 88 if (!m_pieRect.isValid() || m_pieRect.isEmpty())
78 89 return;
79 90
80 91 prepareGeometryChange();
81 92
82 // calculate center angle
83 qreal centerAngle = m_startAngle + (m_angleSpan/2);
93 // update slice path
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
86 QRectF rect = m_pieRect;
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 }
100 // update text rect
101 m_labelTextRect = labelTextRect(m_labelFont, m_labelText);
92 102
93 // update slice path
94 // TODO: draw the shape so that it might have a hole in the center
95 QPainterPath path;
96 path.moveTo(rect.center());
97 path.arcTo(rect, -m_startAngle + 90, -m_angleSpan);
98 path.closeSubpath();
99 m_path = path;
103 // update label arm path
104 QPointF labelTextStart;
105 m_labelArmPath = labelArmPath(armStart, centerAngle, m_labelArmLength, m_labelTextRect.width(), &labelTextStart);
100 106
101 // update label position
102 qreal radius = rect.height() / 2;
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();
107 // update text position
108 m_labelTextRect.moveBottomLeft(labelTextStart);
108 109
109 110 //qDebug() << "PieSlice::updateGeometry" << m_slicelabel->text() << boundingRect() << m_angle << m_span;
110 111 }
111 112
112 113 void PieSlice::updateData(const QPieSlice* sliceData)
113 114 {
114 115 // TODO: compare what has changes to avoid unneccesary geometry updates
115 116
116 117 m_startAngle = sliceData->startAngle();
117 118 m_angleSpan = sliceData->m_angleSpan;
118 119 m_isExploded = sliceData->isExploded();
119 m_explodeDistance = sliceData->explodeDistance(); // TODO: expose to public API
120 m_explodeDistance = sliceData->explodeDistance();
120 121 m_pen = sliceData->pen();
121 122 m_brush = sliceData->brush();
122 123
123 m_slicelabel->setVisible(sliceData->isLabelVisible());
124 m_slicelabel->setText(sliceData->label());
125 m_slicelabel->setPen(sliceData->labelPen());
126 m_slicelabel->setFont(sliceData->labelFont());
127 m_slicelabel->setArmLength(sliceData->labelArmLength());
124 m_labelVisible = sliceData->isLabelVisible();
125 m_labelText = sliceData->label();
126 m_labelFont = sliceData->labelFont();
127 m_labelArmLength = sliceData->labelArmLength();
128 m_labelArmPen = sliceData->labelPen();
128 129
129 130 updateGeometry();
130 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 199 #include "moc_pieslice_p.cpp"
134 200
135 201 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,66 +1,73
1 1 #ifndef PIESLICE_H
2 2 #define PIESLICE_H
3 3
4 4 #include "qchartglobal.h"
5 5 #include "charttheme_p.h"
6 6 #include "qpieseries.h"
7 7 #include <QGraphicsItem>
8 8 #include <QRectF>
9 9 #include <QColor>
10 10 #include <QPen>
11 11
12 12 #define PIESLICE_LABEL_GAP 5
13 13
14 14 QTCOMMERCIALCHART_BEGIN_NAMESPACE
15 15 class PiePresenter;
16 16 class PieSliceLabel;
17 17 class QPieSlice;
18 18
19 19 class PieSlice : public QGraphicsObject
20 20 {
21 21 Q_OBJECT
22 22
23 23 public:
24 24 PieSlice(QGraphicsItem* parent = 0);
25 25 ~PieSlice();
26 26
27 27 public: // from QGraphicsItem
28 28 QRectF boundingRect() const;
29 29 QPainterPath shape() const;
30 30 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
31 31 void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
32 32 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
33 33 void mousePressEvent(QGraphicsSceneMouseEvent *event);
34 34
35 35 Q_SIGNALS:
36 36 void clicked();
37 37 void hoverEnter();
38 38 void hoverLeave();
39 39
40 40 public Q_SLOTS:
41 41 void setPieRect(QRectF rect);
42 42 void updateGeometry();
43 43 void updateData(const QPieSlice *sliceData);
44 44
45 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 50 private:
49 PieSliceLabel* m_slicelabel;
50
51 51 QRectF m_pieRect;
52 QPainterPath m_path;
53 52
53 QPainterPath m_slicePath;
54 54 qreal m_startAngle;
55 55 qreal m_angleSpan;
56
57 56 bool m_isExploded;
58 57 qreal m_explodeDistance;
59
58 bool m_labelVisible;
60 59 QPen m_pen;
61 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 71 QTCOMMERCIALCHART_END_NAMESPACE
65 72
66 73 #endif // PIESLICE_H
@@ -1,481 +1,517
1 1 #include "qpieseries.h"
2 2 #include "qpieslice.h"
3 3 #include <QDebug>
4 4
5 5 QTCOMMERCIALCHART_BEGIN_NAMESPACE
6 6
7 7
8 8 /*!
9 9 \class QPieSeries::ChangeSet
10 10 \brief Defines the changes in the series.
11 11
12 12 Contains the changes that have occurred in the series. Lists of added, changed and removed slices.
13 13
14 14 \sa QPieSeries::changed()
15 15 */
16 16
17 17 /*!
18 18 \internal
19 19 */
20 20 void QPieSeries::ChangeSet::appendAdded(QPieSlice* slice)
21 21 {
22 22 if (!m_added.contains(slice))
23 23 m_added << slice;
24 24 }
25 25
26 26 /*!
27 27 \internal
28 28 */
29 29 void QPieSeries::ChangeSet::appendAdded(QList<QPieSlice*> slices)
30 30 {
31 31 foreach (QPieSlice* s, slices)
32 32 appendAdded(s);
33 33 }
34 34
35 35 /*!
36 36 \internal
37 37 */
38 38 void QPieSeries::ChangeSet::appendChanged(QPieSlice* slice)
39 39 {
40 40 if (!m_changed.contains(slice))
41 41 m_changed << slice;
42 42 }
43 43
44 44 /*!
45 45 \internal
46 46 */
47 47 void QPieSeries::ChangeSet::appendRemoved(QPieSlice* slice)
48 48 {
49 49 if (!m_removed.contains(slice))
50 50 m_removed << slice;
51 51 }
52 52
53 53 /*!
54 54 Returns a list of slices that have been added to the series.
55 55 \sa QPieSeries::changed()
56 56 */
57 57 QList<QPieSlice*> QPieSeries::ChangeSet::added() const
58 58 {
59 59 return m_added;
60 60 }
61 61
62 62 /*!
63 63 Returns a list of slices that have been changed in the series.
64 64 \sa QPieSeries::changed()
65 65 */
66 66 QList<QPieSlice*> QPieSeries::ChangeSet::changed() const
67 67 {
68 68 return m_changed;
69 69 }
70 70
71 71 /*!
72 72 Returns a list of slices that have been removed from the series.
73 73 \sa QPieSeries::changed()
74 74 */
75 75 QList<QPieSlice*> QPieSeries::ChangeSet::removed() const
76 76 {
77 77 return m_removed;
78 78 }
79 79
80 80
81 81 /*!
82 82 Returns true if there are no added/changed or removed slices in the change set.
83 83 */
84 84 bool QPieSeries::ChangeSet::isEmpty() const
85 85 {
86 86 if (m_added.count() || m_changed.count() || m_removed.count())
87 87 return false;
88 88 return true;
89 89 }
90 90
91 91 /*!
92 92 \enum QPieSeries::PiePosition
93 93
94 94 This enum describes pie position within its bounding rectangle
95 95
96 96 \value PiePositionMaximized
97 97 \value PiePositionTopLeft
98 98 \value PiePositionTopRight
99 99 \value PiePositionBottomLeft
100 100 \value PiePositionBottomRight
101 101 */
102 102
103 103 /*!
104 104 \class QPieSeries
105 105 \brief Pie series API for QtCommercial Charts
106 106
107 107 The pie series defines a pie chart which consists of pie slices which are QPieSlice objects.
108 108 The slices can have any values as the QPieSeries will calculate its relative value to the sum of all slices.
109 109 The actual slice size is determined by that relative value.
110 110
111 111 By default the pie is defined as a full pie but it can be a partial pie.
112 112 This can be done by setting a starting angle and angle span to the series.
113 113 */
114 114
115 115 /*!
116 116 Constructs a series object which is a child of \a parent.
117 117 */
118 118 QPieSeries::QPieSeries(QObject *parent) :
119 119 QSeries(parent),
120 120 m_sizeFactor(1.0),
121 m_position(PiePositionMaximized),
121 m_position(PiePositionCenter),
122 m_sizePolicy(PieSizePolicyMaximized),
122 123 m_pieStartAngle(0),
123 m_pieAngleSpan(360)
124 m_pieEndAngle(360)
124 125 {
125 126
126 127 }
127 128
128 129 /*!
129 130 Destroys the object. Note that adding series to QChart transfers the ownership to the chart.
130 131 */
131 132 QPieSeries::~QPieSeries()
132 133 {
133 134
134 135 }
135 136
136 137 /*!
137 138 Returns QChartSeries::SeriesTypePie.
138 139 */
139 140 QSeries::QSeriesType QPieSeries::type() const
140 141 {
141 142 return QSeries::SeriesTypePie;
142 143 }
143 144
144 145 /*!
145 146 Sets an array of \a slices to the series replacing the existing slices.
146 147 Slice ownership is passed to the series.
147 148 */
148 149 void QPieSeries::replace(QList<QPieSlice*> slices)
149 150 {
150 151 clear();
151 152 add(slices);
152 153 }
153 154
154 155 /*!
155 156 Adds an array of \a slices to the series.
156 157 Slice ownership is passed to the series.
157 158 */
158 159 void QPieSeries::add(QList<QPieSlice*> slices)
159 160 {
160 161 ChangeSet changeSet;
161 162 foreach (QPieSlice* s, slices) {
162 163 s->setParent(this);
163 164 m_slices << s;
164 165 changeSet.appendAdded(s);
165 166 }
166 167
167 168 updateDerivativeData();
168 169
169 170 foreach (QPieSlice* s, slices) {
170 171 connect(s, SIGNAL(changed()), this, SLOT(sliceChanged()));
171 172 connect(s, SIGNAL(clicked()), this, SLOT(sliceClicked()));
172 173 connect(s, SIGNAL(hoverEnter()), this, SLOT(sliceHoverEnter()));
173 174 connect(s, SIGNAL(hoverLeave()), this, SLOT(sliceHoverLeave()));
174 175 }
175 176
176 177 emit changed(changeSet);
177 178 }
178 179
179 180 /*!
180 181 Adds a single \a slice to the series.
181 182 Slice ownership is passed to the series.
182 183 */
183 184 void QPieSeries::add(QPieSlice* slice)
184 185 {
185 186 add(QList<QPieSlice*>() << slice);
186 187 }
187 188
188 189 /*!
189 190 Adds a single \a slice to the series and returns a reference to the series.
190 191 Slice ownership is passed to the series.
191 192 */
192 193 QPieSeries& QPieSeries::operator << (QPieSlice* slice)
193 194 {
194 195 add(slice);
195 196 return *this;
196 197 }
197 198
198 199
199 200 /*!
200 201 Adds a single slice to the series with give \a value and \a name.
201 202 Slice ownership is passed to the series.
202 203 */
203 204 QPieSlice* QPieSeries::add(qreal value, QString name)
204 205 {
205 206 QPieSlice* slice = new QPieSlice(value, name);
206 207 add(slice);
207 208 return slice;
208 209 }
209 210
210 211 /*!
211 212 Removes a single \a slice from the series and deletes the slice.
212 213
213 214 Do not reference this pointer after this call.
214 215 */
215 216 void QPieSeries::remove(QPieSlice* slice)
216 217 {
217 218 if (!m_slices.removeOne(slice)) {
218 219 Q_ASSERT(0); // TODO: how should this be reported?
219 220 return;
220 221 }
221 222
222 223 ChangeSet changeSet;
223 224 changeSet.appendRemoved(slice);
224 225 emit changed(changeSet);
225 226
226 227 delete slice;
227 228 slice = NULL;
228 229
229 230 updateDerivativeData();
230 231 }
231 232
232 233 /*!
233 234 Clears all slices from the series.
234 235 */
235 236 void QPieSeries::clear()
236 237 {
237 238 if (m_slices.count() == 0)
238 239 return;
239 240
240 241 ChangeSet changeSet;
241 242 foreach (QPieSlice* s, m_slices) {
242 243 changeSet.appendRemoved(s);
243 244 m_slices.removeOne(s);
244 245 delete s;
245 246 }
246 247 emit changed(changeSet);
247 248 updateDerivativeData();
248 249 }
249 250
250 251 /*!
251 252 Counts the number of the slices in this series.
252 253 */
253 254 int QPieSeries::count() const
254 255 {
255 256 return m_slices.count();
256 257 }
257 258
258 259 /*!
259 260 Returns a list of slices that belong to this series.
260 261 */
261 262 QList<QPieSlice*> QPieSeries::slices() const
262 263 {
263 264 return m_slices;
264 265 }
265 266
266 267 /*!
267 268 Sets the size \a factor of the pie. 1.0 is the default value.
268 269 Note that the pie will not grow beyond its absolute maximum size.
269 270 In practice its use is to make the pie appear smaller.
270 271 \sa sizeFactor()
271 272 */
272 273 void QPieSeries::setSizeFactor(qreal factor)
273 274 {
274 275 if (factor < 0.0)
275 276 return;
276 277
277 278 if (m_sizeFactor != factor) {
278 279 m_sizeFactor = factor;
279 280 emit sizeFactorChanged();
280 281 }
281 282 }
282 283
283 284 /*!
284 285 Gets the size factor of the pie.
285 286 \sa setSizeFactor()
286 287 */
287 288 qreal QPieSeries::sizeFactor() const
288 289 {
289 290 return m_sizeFactor;
290 291 }
291 292
292 293 /*!
293 294 Sets the \a position of the pie within its bounding rectangle.
294 295 \sa PiePosition, position()
295 296 */
296 297 void QPieSeries::setPosition(PiePosition position)
297 298 {
299 // TODO: sanity check
298 300 if (m_position != position) {
299 301 m_position = position;
300 302 emit positionChanged();
301 303 }
302 304 }
303 305
304 306 /*!
305 307 Gets the position of the pie within its bounding rectangle.
306 308 \sa PiePosition, setPosition()
307 309 */
308 310 QPieSeries::PiePosition QPieSeries::position() const
309 311 {
310 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 &&
322 angleSpan > 0 && angleSpan <= 360) {
334 return m_sizePolicy;
335 }
336
337
338 void QPieSeries::setStartAngle(qreal startAngle)
339 {
340 if (startAngle >= 0 && startAngle <= 360 && startAngle != m_pieStartAngle && startAngle <= m_pieEndAngle) {
323 341 m_pieStartAngle = startAngle;
324 m_pieAngleSpan = angleSpan;
325 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 365 Sets the all the slice labels \a visible or invisible.
331 366
332 367 \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible()
333 368 */
334 369 void QPieSeries::setLabelsVisible(bool visible)
335 370 {
336 371 foreach (QPieSlice* s, m_slices)
337 372 s->setLabelVisible(visible);
338 373 }
339 374
340 375 /*!
341 376 Returns the sum of all slice values in this series.
342 377
343 378 \sa QPieSlice::value(), QPieSlice::setValue()
344 379 */
345 380 qreal QPieSeries::total() const
346 381 {
347 382 return m_total;
348 383 }
349 384
350 385 /*!
351 386 \fn void QPieSeries::changed(const QPieSeries::ChangeSet& changeSet)
352 387
353 388 This signal emitted when something has changed in the series.
354 389 The \a changeSet contains the details of which slices have been added, changed or removed.
355 390
356 391 \sa QPieSeries::ChangeSet, QPieSlice::changed()
357 392 */
358 393
359 394 /*!
360 395 \fn void QPieSeries::clicked(QPieSlice* slice)
361 396
362 397 This signal is emitted when a \a slice has been clicked.
363 398
364 399 \sa QPieSlice::clicked()
365 400 */
366 401
367 402 /*!
368 403 \fn void QPieSeries::hoverEnter(QPieSlice* slice)
369 404
370 405 This signal is emitted when user has hovered over a \a slice.
371 406
372 407 \sa QPieSlice::hoverEnter()
373 408 */
374 409
375 410 /*!
376 411 \fn void QPieSeries::hoverLeave(QPieSlice* slice)
377 412
378 413 This signal is emitted when user has hovered away from a \a slice.
379 414
380 415 \sa QPieSlice::hoverLeave()
381 416 */
382 417
383 418 /*!
384 419 \fn void QPieSeries::sizeFactorChanged()
385 420
386 421 This signal is emitted when size factor has been changed.
387 422
388 423 \sa sizeFactor(), setSizeFactor()
389 424 */
390 425
391 426 /*!
392 427 \fn void QPieSeries::positionChanged()
393 428
394 429 This signal is emitted when position of the pie has been changed.
395 430
396 431 \sa position(), setPosition()
397 432 */
398 433
399 434 void QPieSeries::sliceChanged()
400 435 {
401 436 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
402 437 Q_ASSERT(m_slices.contains(slice));
403 438
404 439 ChangeSet changeSet;
405 440 changeSet.appendChanged(slice);
406 441 emit changed(changeSet);
407 442
408 443 updateDerivativeData();
409 444 }
410 445
411 446 void QPieSeries::sliceClicked()
412 447 {
413 448 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
414 449 Q_ASSERT(m_slices.contains(slice));
415 450 emit clicked(slice);
416 451 }
417 452
418 453 void QPieSeries::sliceHoverEnter()
419 454 {
420 455 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
421 456 Q_ASSERT(m_slices.contains(slice));
422 457 emit hoverEnter(slice);
423 458 }
424 459
425 460 void QPieSeries::sliceHoverLeave()
426 461 {
427 462 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
428 463 Q_ASSERT(m_slices.contains(slice));
429 464 emit hoverLeave(slice);
430 465 }
431 466
432 467 void QPieSeries::updateDerivativeData()
433 468 {
434 469 m_total = 0;
435 470
436 471 // nothing to do?
437 472 if (m_slices.count() == 0)
438 473 return;
439 474
440 475 // calculate total
441 476 foreach (QPieSlice* s, m_slices)
442 477 m_total += s->value();
443 478
444 479 // we must have some values
445 480 if (m_total == 0) {
446 481 qDebug() << "QPieSeries::updateDerivativeData() total == 0";
447 482 Q_ASSERT(m_total > 0); // TODO: is this the correct way to handle this?
448 483 }
449 484
450 485 // update slice attributes
451 486 qreal sliceAngle = m_pieStartAngle;
487 qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
452 488 foreach (QPieSlice* s, m_slices) {
453 489
454 490 bool changed = false;
455 491
456 492 qreal percentage = s->value() / m_total;
457 493 if (s->m_percentage != percentage) {
458 494 s->m_percentage = percentage;
459 495 changed = true;
460 496 }
461 497
462 qreal sliceSpan = m_pieAngleSpan * percentage;
498 qreal sliceSpan = pieSpan * percentage;
463 499 if (s->m_angleSpan != sliceSpan) {
464 500 s->m_angleSpan = sliceSpan;
465 501 changed = true;
466 502 }
467 503
468 504 if (s->m_startAngle != sliceAngle) {
469 505 s->m_startAngle = sliceAngle;
470 506 changed = true;
471 507 }
472 508 sliceAngle += sliceSpan;
473 509
474 510 if (changed)
475 511 emit s->changed();
476 512 }
477 513 }
478 514
479 515 #include "moc_qpieseries.cpp"
480 516
481 517 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,127 +1,157
1 1 #ifndef PIESERIES_H
2 2 #define PIESERIES_H
3 3
4 4 #include "qseries.h"
5 5 #include <QObject>
6 6 #include <QRectF>
7 7 #include <QColor>
8 8 #include <QPen>
9 9 #include <QBrush>
10 10 #include <QSignalMapper>
11 11
12 12 class QGraphicsObject;
13 13 QTCOMMERCIALCHART_BEGIN_NAMESPACE
14 14 class PiePresenter;
15 15 class PieSlice;
16 16 class QPieSlice;
17 17
18 18 class QTCOMMERCIALCHART_EXPORT QPieSeries : public QSeries
19 19 {
20 20 Q_OBJECT
21 21
22 22 public:
23 enum PiePosition {
24 PiePositionMaximized = 0,
25 PiePositionTopLeft,
26 PiePositionTopRight,
27 PiePositionBottomLeft,
28 PiePositionBottomRight
23
24 enum PiePositionFlag {
25 PiePositionLeft = 0x1,
26 PiePositionRight = 0x2,
27 PiePositionHCenter = 0x4,
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 45 class ChangeSet
32 46 {
33 47 public:
34 48
35 49 // TODO: these should not really be exposed to the public API
36 50 void appendAdded(QPieSlice* slice);
37 51 void appendAdded(QList<QPieSlice*> slices);
38 52 void appendChanged(QPieSlice* slice);
39 53 void appendRemoved(QPieSlice* slice);
40 54
41 55 QList<QPieSlice*> added() const;
42 56 QList<QPieSlice*> changed() const;
43 57 QList<QPieSlice*> removed() const;
44 58
45 59 bool isEmpty() const;
46 60
47 61 private:
48 62 QList<QPieSlice*> m_added;
49 63 QList<QPieSlice*> m_changed;
50 64 QList<QPieSlice*> m_removed;
51 65 };
52 66
53 67 public:
54 68 QPieSeries(QObject *parent = 0);
55 69 virtual ~QPieSeries();
56 70
57 71 public: // from QChartSeries
58 72 QSeriesType type() const;
59 73
60 74 public:
61 void replace(QList<QPieSlice*> slices);
62 void add(QList<QPieSlice*> slices);
75
76 // slice setters
63 77 void add(QPieSlice* slice);
64 QPieSlice* add(qreal value, QString name);
65 QPieSeries& operator << (QPieSlice* slice);
78 void add(QList<QPieSlice*> slices);
79 void replace(QList<QPieSlice*> slices);
66 80 void remove(QPieSlice* slice);
67 81 void clear();
68 82
69 int count() const;
83 // sluce getters
70 84 QList<QPieSlice*> slices() const;
71 85
72 void setSizeFactor(qreal sizeFactor);
73 qreal sizeFactor() const;
86 // calculated data
87 int count() const;
88 qreal total() const;
89
90 // pie customization
74 91 void setPosition(PiePosition position);
75 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 105 void setLabelsVisible(bool visible = true);
79 106
80 qreal total() const;
81
82 107 // TODO: find slices?
83 108 // QList<QPieSlice*> findByValue(qreal value);
84 109 // ...
85 110
86 111 // TODO: sorting slices?
87 112 // void sort(QPieSeries::SortByValue|label|??)
88 113
89 114 // TODO: general graphics customization
90 115 // setDrawStyle(2d|3d)
91 // setDropShadows(bool)
116 // setDropShadows
92 117
93 118 Q_SIGNALS:
119
94 120 void changed(const QPieSeries::ChangeSet& changeSet);
121
95 122 void clicked(QPieSlice* slice);
96 123 void hoverEnter(QPieSlice* slice);
97 124 void hoverLeave(QPieSlice* slice);
125
98 126 void sizeFactorChanged();
99 127 void positionChanged();
128 void sizePolicyChanged();
100 129
101 130 private Q_SLOTS: // TODO: should be private and not visible in the interface at all
102 131 void sliceChanged();
103 132 void sliceClicked();
104 133 void sliceHoverEnter();
105 134 void sliceHoverLeave();
106 135
107 136 private:
108 137 void updateDerivativeData();
109 138
110 139 private:
111 140 Q_DISABLE_COPY(QPieSeries)
112 141
113 142 // TODO: use PIML
114 143 friend class PiePresenter;
115 144 friend class PieSlice;
116 145
117 146 QList<QPieSlice*> m_slices;
118 147 qreal m_sizeFactor;
119 148 PiePosition m_position;
149 PieSizePolicy m_sizePolicy;
120 150 qreal m_total;
121 151 qreal m_pieStartAngle;
122 qreal m_pieAngleSpan;
152 qreal m_pieEndAngle;
123 153 };
124 154
125 155 QTCOMMERCIALCHART_END_NAMESPACE
126 156
127 157 #endif // PIESERIES_H
@@ -1,97 +1,91
1 1 #ifndef QPIESLICE_H
2 2 #define QPIESLICE_H
3 3
4 4 #include <qchartglobal.h>
5 5 #include <QObject>
6 6 #include <QPen>
7 7 #include <QBrush>
8 8 #include <QFont>
9 9
10 10 QTCOMMERCIALCHART_BEGIN_NAMESPACE
11 11
12 12 class QTCOMMERCIALCHART_EXPORT QPieSlice : public QObject
13 13 {
14 14 Q_OBJECT
15 15 Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY changed)
16 16 Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY changed)
17 17
18 18 public:
19 19 QPieSlice(QObject *parent = 0);
20 20 QPieSlice(qreal value, QString label, QObject *parent = 0);
21 21 virtual ~QPieSlice();
22 22
23 23 // data
24 void setValue(qreal value);
24 25 qreal value() const;
26 void setLabel(QString label);
25 27 QString label() const;
28 void setLabelVisible(bool visible);
26 29 bool isLabelVisible() const;
30 void setExploded(bool exploded);
27 31 bool isExploded() const;
32 void setExplodeDistance(qreal distance);
28 33 qreal explodeDistance() const;
29 34
30 35 // generated data
31 36 qreal percentage() const;
32 37 qreal startAngle() const;
33 38 qreal endAngle() const;
34 39
35 40 // customization
41 void setPen(QPen pen);
36 42 QPen pen() const;
43 void setBrush(QBrush brush);
37 44 QBrush brush() const;
45 void setLabelPen(QPen pen);
38 46 QPen labelPen() const;
47 void setLabelFont(QFont font);
39 48 QFont labelFont() const;
49 void setLabelArmLength(qreal len);
40 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 56 Q_SIGNALS:
43 57 void clicked();
44 58 void hoverEnter();
45 59 void hoverLeave();
46 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 62 private:
69 63
70 64 // TODO: use private class
71 65 friend class QPieSeries;
72 66 friend class PiePresenter;
73 67 friend class PieSlice;
74 68
75 69 // data
76 70 qreal m_value;
77 71 QString m_label;
78 72 bool m_isLabelVisible;
79 73 bool m_isExploded;
80 74 qreal m_explodeDistance;
81 75
82 76 // generated data
83 77 qreal m_percentage;
84 78 qreal m_startAngle;
85 79 qreal m_angleSpan;
86 80
87 81 // customization
88 82 QPen m_pen;
89 83 QBrush m_brush;
90 84 QPen m_labelPen;
91 85 QFont m_labelFont;
92 86 qreal m_labelArmLength;
93 87 };
94 88
95 89 QTCOMMERCIALCHART_END_NAMESPACE
96 90
97 91 #endif // QPIESLICE_H
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now