@@ -0,0 +1,76 | |||
|
1 | #include "pieslicelabel.h" | |
|
2 | #include <QPainter> | |
|
3 | #include <qmath.h> | |
|
4 | ||
|
5 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | |
|
6 | ||
|
7 | #define PI 3.14159265 | |
|
8 | ||
|
9 | PieSliceLabel::PieSliceLabel(QGraphicsItem* parent) | |
|
10 | :QGraphicsItem(parent) | |
|
11 | { | |
|
12 | m_pen = QPen(Qt::black); | |
|
13 | } | |
|
14 | ||
|
15 | PieSliceLabel::~PieSliceLabel() | |
|
16 | { | |
|
17 | ||
|
18 | } | |
|
19 | ||
|
20 | QRectF PieSliceLabel::boundingRect() const | |
|
21 | { | |
|
22 | return m_rect; | |
|
23 | } | |
|
24 | ||
|
25 | QPainterPath PieSliceLabel::shape() const | |
|
26 | { | |
|
27 | return m_path; | |
|
28 | } | |
|
29 | ||
|
30 | void PieSliceLabel::paint(QPainter *painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) | |
|
31 | { | |
|
32 | painter->setRenderHint(QPainter::Antialiasing); | |
|
33 | painter->setPen(m_pen); | |
|
34 | painter->drawPath(m_path); | |
|
35 | } | |
|
36 | ||
|
37 | void PieSliceLabel::updateGeometry(const QPointF& startPoint, qreal armAngle, qreal armLength) | |
|
38 | { | |
|
39 | prepareGeometryChange(); | |
|
40 | ||
|
41 | QPainterPath path; | |
|
42 | path.moveTo(startPoint); | |
|
43 | ||
|
44 | // draw arm | |
|
45 | qreal dx = qSin(armAngle*(PI/180)) * armLength; | |
|
46 | qreal dy = -qCos(armAngle*(PI/180)) * armLength; | |
|
47 | QPointF p1 = startPoint + QPointF(dx, dy); | |
|
48 | path.lineTo(p1); | |
|
49 | ||
|
50 | QPointF p2 = p1; | |
|
51 | QPointF pt = p1; | |
|
52 | if (armAngle < 180) { | |
|
53 | p2 += QPointF(50, 0); | |
|
54 | } else { | |
|
55 | p2 += QPointF(-50,0); | |
|
56 | pt = p2; | |
|
57 | } | |
|
58 | path.lineTo(p2); | |
|
59 | ||
|
60 | QFont font; | |
|
61 | pt += QPointF(0,-2); | |
|
62 | path.addText(pt, font, m_label); | |
|
63 | ||
|
64 | m_path = path; | |
|
65 | m_rect = path.boundingRect(); | |
|
66 | } | |
|
67 | ||
|
68 | void PieSliceLabel::setLabel(QString label) | |
|
69 | { | |
|
70 | m_label = label; | |
|
71 | // TODO: animation? | |
|
72 | } | |
|
73 | ||
|
74 | ||
|
75 | ||
|
76 | QTCOMMERCIALCHART_END_NAMESPACE |
@@ -0,0 +1,36 | |||
|
1 | #ifndef PIELABEL_H | |
|
2 | #define PIELABEL_H | |
|
3 | ||
|
4 | #include "qchartglobal.h" | |
|
5 | #include <QGraphicsItem> | |
|
6 | #include <QPen> | |
|
7 | ||
|
8 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | |
|
9 | ||
|
10 | class PieSliceLabel : public QGraphicsItem | |
|
11 | { | |
|
12 | public: | |
|
13 | PieSliceLabel(QGraphicsItem* parent = 0); | |
|
14 | ~PieSliceLabel(); | |
|
15 | ||
|
16 | public: // from QGraphicsItem | |
|
17 | QRectF boundingRect() const; | |
|
18 | QPainterPath shape() const; | |
|
19 | void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); | |
|
20 | ||
|
21 | public: | |
|
22 | void updateGeometry(const QPointF& startPoint, qreal armAngle, qreal armLength); | |
|
23 | void setLabel(QString label); | |
|
24 | QString label() const {return m_label;} | |
|
25 | void setPen(QPen pen); | |
|
26 | ||
|
27 | private: | |
|
28 | QString m_label; | |
|
29 | QPainterPath m_path; | |
|
30 | QRectF m_rect; | |
|
31 | QPen m_pen; | |
|
32 | }; | |
|
33 | ||
|
34 | QTCOMMERCIALCHART_END_NAMESPACE | |
|
35 | ||
|
36 | #endif // PIELABEL_H |
@@ -17,11 +17,11 int main(int argc, char *argv[]) | |||
|
17 | 17 | Q_ASSERT(series); |
|
18 | 18 | |
|
19 | 19 | // Add test data to the series |
|
20 |
series->add(QPieSlice(1, "test1", Qt::red, tr |
|
|
21 |
series->add(QPieSlice(2, "test2" |
|
|
22 |
series->add(QPieSlice(3, "test3" |
|
|
23 |
series->add(QPieSlice(4, "test4" |
|
|
24 |
series->add(QPieSlice(5, "test5" |
|
|
20 | series->add(QPieSlice(1, "test1", true, true, QPen(Qt::red, 2), QBrush(Qt::red))); | |
|
21 | series->add(QPieSlice(2, "test2")); | |
|
22 | series->add(QPieSlice(3, "test3")); | |
|
23 | series->add(QPieSlice(4, "test4")); | |
|
24 | series->add(QPieSlice(5, "test5")); | |
|
25 | 25 | |
|
26 | 26 | // Use the chart widget as the central widget |
|
27 | 27 | QMainWindow w; |
@@ -198,14 +198,12 void ChartTheme::decorate(PiePresenter* item, QPieSeries* series, int /*count*/) | |||
|
198 | 198 | } |
|
199 | 199 | |
|
200 | 200 | // finally update colors |
|
201 | QList<QPieSlice> slices; | |
|
202 | for (int i=0; i<series->count(); i++) { | |
|
203 | QPieSlice slice = series->slice(i); | |
|
204 |
s |
|
|
205 | slices << slice; | |
|
201 | foreach (QPieSliceId id, series->ids()) { | |
|
202 | QPieSlice s = series->slice(id); | |
|
203 | s.setPen(QPen(Qt::black)); // TODO: get from theme | |
|
204 | s.setBrush(colors.takeFirst()); | |
|
205 | series->update(s); | |
|
206 | 206 | } |
|
207 | ||
|
208 | series->set(slices); | |
|
209 | 207 | } |
|
210 | 208 | |
|
211 | 209 |
@@ -4,11 +4,13 DEPENDPATH += $$PWD | |||
|
4 | 4 | SOURCES += \ |
|
5 | 5 | $$PWD/qpieseries.cpp \ |
|
6 | 6 | $$PWD/pieslice.cpp \ |
|
7 | $$PWD/piepresenter.cpp | |
|
7 | $$PWD/piepresenter.cpp \ | |
|
8 | $$PWD/pieslicelabel.cpp | |
|
8 | 9 | |
|
9 | 10 | PRIVATE_HEADERS += \ |
|
10 | 11 | $$PWD/piepresenter.h \ |
|
11 | $$PWD/pieslice.h | |
|
12 | $$PWD/pieslice.h \ | |
|
13 | $$PWD/pieslicelabel.h | |
|
12 | 14 | |
|
13 | 15 | PUBLIC_HEADERS += \ |
|
14 | 16 | $$PWD/qpieseries.h |
@@ -6,57 +6,50 | |||
|
6 | 6 | |
|
7 | 7 | QTCOMMERCIALCHART_BEGIN_NAMESPACE |
|
8 | 8 | |
|
9 |
PiePresenter::PiePresenter(QGraphicsItem *parent, QPieSeries *series) |
|
|
10 | ChartItem(parent), | |
|
11 |
m_ |
|
|
9 | PiePresenter::PiePresenter(QGraphicsItem *parent, QPieSeries *series) | |
|
10 | :ChartItem(parent), | |
|
11 | m_series(series) | |
|
12 | 12 | { |
|
13 | Q_ASSERT(parent); | |
|
14 | 13 | Q_ASSERT(series); |
|
15 | ||
|
16 | m_rect = parentItem()->boundingRect(); | |
|
17 | setAcceptHoverEvents(true); | |
|
18 | qsrand(QTime::currentTime().msec()); // for random color generation | |
|
19 | ||
|
20 | connect(series, SIGNAL(changed(const PieChangeSet&)), this, SLOT(handleSeriesChanged(const PieChangeSet&))); | |
|
14 | connect(series, SIGNAL(changed(const QPieSeries::ChangeSet&)), this, SLOT(handleSeriesChanged(const QPieSeries::ChangeSet&))); | |
|
21 | 15 | connect(series, SIGNAL(sizeFactorChanged()), this, SLOT(updateGeometry())); |
|
22 | 16 | connect(series, SIGNAL(positionChanged()), this, SLOT(updateGeometry())); |
|
23 | 17 | } |
|
24 | 18 | |
|
25 | 19 | PiePresenter::~PiePresenter() |
|
26 | 20 | { |
|
27 | while (m_slices.count()) | |
|
28 | delete m_slices.takeLast(); | |
|
21 | // slices deleted automatically through QGraphicsItem | |
|
29 | 22 | } |
|
30 | 23 | |
|
31 | void PiePresenter::handleSeriesChanged(const PieChangeSet& /*changeSet*/) | |
|
24 | void PiePresenter::paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) | |
|
32 | 25 | { |
|
33 | const qreal fullPie = 360; | |
|
34 | qreal total = 0; | |
|
35 | ||
|
36 | // calculate total and set random color if there is no color | |
|
37 | for (int i=0; i<m_pieSeries->count(); i++) { | |
|
38 | QPieSlice& slice = m_pieSeries->m_slices[i]; | |
|
39 | total += slice.m_value; | |
|
40 | if (slice.m_color == QColor::Invalid) { | |
|
41 | slice.m_color.setRed(qrand() % 255); | |
|
42 | slice.m_color.setGreen(qrand() % 255); | |
|
43 | slice.m_color.setBlue(qrand() % 255); | |
|
44 | } | |
|
26 | // TODO: paint shadows for all components | |
|
27 | // - get paths from items & merge & offset and draw with shadow color? | |
|
28 | } | |
|
29 | ||
|
30 | void PiePresenter::handleSeriesChanged(const QPieSeries::ChangeSet& changeSet) | |
|
31 | { | |
|
32 | qDebug() << "PiePresenter::handleSeriesChanged()"; | |
|
33 | qDebug() << " added : " << changeSet.m_added; | |
|
34 | qDebug() << " changed: " << changeSet.m_changed; | |
|
35 | qDebug() << " removed: " << changeSet.m_removed; | |
|
36 | ||
|
37 | // ignore changeset when there are no visual slices | |
|
38 | // changeset might not be valid about the added slices | |
|
39 | if (m_slices.count() == 0) { | |
|
40 | foreach (QPieSliceId id, m_series->m_slices.keys()) | |
|
41 | addSlice(id); | |
|
42 | return; | |
|
45 | 43 | } |
|
46 | 44 | |
|
47 | // TODO: no need to create new slices in case size changed; we should re-use the existing ones | |
|
48 | while (m_slices.count()) | |
|
49 | delete m_slices.takeLast(); | |
|
45 | foreach (QPieSliceId id, changeSet.m_removed) | |
|
46 | deleteSlice(id); | |
|
50 | 47 | |
|
51 | // create slices | |
|
52 | qreal angle = 0; | |
|
53 | for (int i=0; i<m_pieSeries->count(); i++) { | |
|
54 | QPieSlice sliceData = m_pieSeries->slice(i); | |
|
55 | qreal span = sliceData.m_value / total * fullPie; | |
|
56 | PieSlice *slice = new PieSlice(this, i, angle, span); | |
|
57 | m_slices.append(slice); | |
|
58 | angle += span; | |
|
59 | } | |
|
48 | foreach (QPieSliceId id, changeSet.m_changed) | |
|
49 | updateSlice(id); | |
|
50 | ||
|
51 | foreach (QPieSliceId id, changeSet.m_added) | |
|
52 | addSlice(id); | |
|
60 | 53 | } |
|
61 | 54 | |
|
62 | 55 | void PiePresenter::updateGeometry() |
@@ -66,16 +59,16 void PiePresenter::updateGeometry() | |||
|
66 | 59 | m_pieRect = m_rect; |
|
67 | 60 | |
|
68 | 61 | if (m_pieRect.width() < m_pieRect.height()) { |
|
69 |
m_pieRect.setWidth(m_pieRect.width() * m_ |
|
|
62 | m_pieRect.setWidth(m_pieRect.width() * m_series->sizeFactor()); | |
|
70 | 63 | m_pieRect.setHeight(m_pieRect.width()); |
|
71 | 64 | m_pieRect.moveCenter(m_rect.center()); |
|
72 | 65 | } else { |
|
73 |
m_pieRect.setHeight(m_pieRect.height() * m_ |
|
|
66 | m_pieRect.setHeight(m_pieRect.height() * m_series->sizeFactor()); | |
|
74 | 67 | m_pieRect.setWidth(m_pieRect.height()); |
|
75 | 68 | m_pieRect.moveCenter(m_rect.center()); |
|
76 | 69 | } |
|
77 | 70 | |
|
78 |
switch (m_ |
|
|
71 | switch (m_series->position()) { | |
|
79 | 72 | case QPieSeries::PiePositionTopLeft: { |
|
80 | 73 | m_pieRect.setHeight(m_pieRect.height() / 2); |
|
81 | 74 | m_pieRect.setWidth(m_pieRect.height()); |
@@ -104,10 +97,16 void PiePresenter::updateGeometry() | |||
|
104 | 97 | break; |
|
105 | 98 | } |
|
106 | 99 | |
|
107 | qDebug() << "presentation rect:" << m_rect; | |
|
108 | qDebug() << "pie rect:" << m_pieRect; | |
|
109 | foreach (PieSlice *slice, m_slices) | |
|
110 | slice->updateGeometry(); | |
|
100 | // update slice geometry | |
|
101 | const qreal fullPie = 360; | |
|
102 | qreal angle = 0; | |
|
103 | foreach (QPieSliceId id, m_slices.keys()) { | |
|
104 | qreal span = fullPie * m_series->slice(id).percentage(); | |
|
105 | m_slices[id]->updateGeometry(m_pieRect, angle, span); | |
|
106 | angle += span; | |
|
107 | } | |
|
108 | ||
|
109 | //qDebug() << "PiePresenter::updateGeometry" << m_rect << m_pieRect; | |
|
111 | 110 | } |
|
112 | 111 | |
|
113 | 112 | void PiePresenter::handleDomainChanged(const Domain& domain) |
@@ -119,7 +118,51 void PiePresenter::handleGeometryChanged(const QRectF& rect) | |||
|
119 | 118 | { |
|
120 | 119 | m_rect = rect; |
|
121 | 120 | updateGeometry(); |
|
121 | } | |
|
122 | 122 | |
|
123 | void PiePresenter::addSlice(QPieSliceId id) | |
|
124 | { | |
|
125 | qDebug() << "PiePresenter::addSlice()" << id; | |
|
126 | ||
|
127 | if (m_slices.contains(id)) { | |
|
128 | qWarning() << "PiePresenter::addSlice(): slice already exists!" << id; | |
|
129 | updateSlice(id); | |
|
130 | return; | |
|
131 | } | |
|
132 | ||
|
133 | // create slice | |
|
134 | PieSlice *slice = new PieSlice(id, m_series, this); | |
|
135 | m_slices.insert(id, slice); | |
|
136 | ||
|
137 | updateGeometry(); | |
|
138 | } | |
|
139 | ||
|
140 | void PiePresenter::updateSlice(QPieSliceId id) | |
|
141 | { | |
|
142 | qDebug() << "PiePresenter::updateSlice()" << id; | |
|
143 | ||
|
144 | // TODO: animation | |
|
145 | if (m_slices.contains(id)) | |
|
146 | m_slices.value(id)->updateData(); | |
|
147 | else { | |
|
148 | qWarning() << "PiePresenter::updateSlice(): slice does not exist!" << id; | |
|
149 | addSlice(id); | |
|
150 | } | |
|
151 | ||
|
152 | updateGeometry(); | |
|
153 | } | |
|
154 | ||
|
155 | void PiePresenter::deleteSlice(QPieSliceId id) | |
|
156 | { | |
|
157 | qDebug() << "PiePresenter::deleteSlice()" << id; | |
|
158 | ||
|
159 | // TODO: animation | |
|
160 | if (m_slices.contains(id)) | |
|
161 | delete m_slices.take(id); | |
|
162 | else | |
|
163 | qWarning() << "PiePresenter::deleteSlice(): slice does not exist!" << id; | |
|
164 | ||
|
165 | updateGeometry(); | |
|
123 | 166 | } |
|
124 | 167 | |
|
125 | 168 | #include "moc_piepresenter.cpp" |
@@ -3,6 +3,7 | |||
|
3 | 3 | |
|
4 | 4 | #include "chartitem_p.h" |
|
5 | 5 | #include "qpieseries.h" |
|
6 | #include <QSignalMapper> | |
|
6 | 7 | |
|
7 | 8 | class QGraphicsItem; |
|
8 | 9 | QTCOMMERCIALCHART_BEGIN_NAMESPACE |
@@ -19,21 +20,26 public: | |||
|
19 | 20 | |
|
20 | 21 | public: // from QGraphicsItem |
|
21 | 22 | QRectF boundingRect() const { return m_rect; } |
|
22 |
void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) |
|
|
23 | void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); | |
|
23 | 24 | |
|
24 | 25 | public: |
|
25 | 26 | QRectF pieRect() const { return m_pieRect; } |
|
26 | 27 | |
|
27 | 28 | public Q_SLOTS: |
|
28 | void handleSeriesChanged(const PieChangeSet& changeSet); | |
|
29 | void handleSeriesChanged(const QPieSeries::ChangeSet& changeSet); | |
|
29 | 30 | void handleDomainChanged(const Domain& domain); |
|
30 | 31 | void handleGeometryChanged(const QRectF& rect); |
|
31 | 32 | void updateGeometry(); |
|
32 | 33 | |
|
33 | 34 | private: |
|
35 | void addSlice(QPieSliceId id); | |
|
36 | void updateSlice(QPieSliceId id); | |
|
37 | void deleteSlice(QPieSliceId id); | |
|
38 | ||
|
39 | private: | |
|
34 | 40 | friend class PieSlice; |
|
35 |
Q |
|
|
36 |
QPieSeries *m_ |
|
|
41 | QHash<QPieSliceId, PieSlice*> m_slices; | |
|
42 | QPieSeries *m_series; | |
|
37 | 43 | QRectF m_rect; |
|
38 | 44 | QRectF m_pieRect; |
|
39 | 45 | }; |
@@ -1,10 +1,12 | |||
|
1 | 1 | #include "pieslice.h" |
|
2 | #include "pieslicelabel.h" | |
|
2 | 3 | #include "piepresenter.h" |
|
3 | 4 | #include "qpieseries.h" |
|
4 | 5 | #include <QPainter> |
|
5 | 6 | #include <QDebug> |
|
6 | 7 | #include <qmath.h> |
|
7 | 8 | #include <QGraphicsSceneEvent> |
|
9 | #include <QTime> | |
|
8 | 10 | |
|
9 | 11 | QTCOMMERCIALCHART_BEGIN_NAMESPACE |
|
10 | 12 | |
@@ -18,25 +20,22 QPointF offset(qreal angle, qreal length) | |||
|
18 | 20 | return QPointF(dx, -dy); |
|
19 | 21 | } |
|
20 | 22 | |
|
21 | PieSlice::PieSlice(PiePresenter *presenter, int seriesIndex, qreal startAngle, qreal span) | |
|
22 |
:QGraphics |
|
|
23 | m_label(new QGraphicsTextItem(this)), | |
|
24 |
m_series |
|
|
25 | m_startAngle(startAngle), | |
|
26 | m_span(span) | |
|
23 | PieSlice::PieSlice(QPieSliceId id, QPieSeries *series, QGraphicsItem* parent) | |
|
24 | :QGraphicsObject(parent), | |
|
25 | m_id(id), | |
|
26 | m_series(series), | |
|
27 | m_slicelabel(new PieSliceLabel(this)), | |
|
28 | m_isHovering(false) | |
|
27 | 29 | { |
|
28 |
Q_ASSERT( |
|
|
30 | Q_ASSERT(series); | |
|
29 | 31 | setAcceptHoverEvents(true); |
|
30 | 32 | setAcceptedMouseButtons(Qt::LeftButton); |
|
31 |
update |
|
|
32 | ||
|
33 | // TODO: use themes | |
|
34 | m_pen = QPen(Qt::black); | |
|
35 | m_brush = QBrush(sliceData().m_color); | |
|
33 | updateData(); | |
|
36 | 34 | } |
|
37 | 35 | |
|
38 | 36 | PieSlice::~PieSlice() |
|
39 | 37 | { |
|
38 | qDebug() << "~PieSlice()" << m_id; | |
|
40 | 39 | } |
|
41 | 40 | |
|
42 | 41 | QRectF PieSlice::boundingRect() const |
@@ -51,72 +50,86 QPainterPath PieSlice::shape() const | |||
|
51 | 50 | |
|
52 | 51 | void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) |
|
53 | 52 | { |
|
54 | painter->setRenderHint(QPainter::Antialiasing); | |
|
53 | // set hover brush | |
|
54 | // TODO: what if we are using gradients... | |
|
55 | QBrush brush = m_data.brush(); | |
|
56 | if (m_isHovering) | |
|
57 | brush.setColor(brush.color().lighter()); | |
|
55 | 58 | |
|
56 | // TODO: themes | |
|
57 | painter->setPen(m_pen); | |
|
58 |
painter->setBrush( |
|
|
59 | painter->setRenderHint(QPainter::Antialiasing); | |
|
60 | painter->setPen(m_data.pen()); | |
|
61 | painter->setBrush(brush); | |
|
59 | 62 | painter->drawPath(m_path); |
|
60 | ||
|
61 | // Draw the label | |
|
62 | // TODO: do this better | |
|
63 | //QTextItem text; | |
|
64 | painter->drawText(m_center, sliceData().m_label); | |
|
65 | 63 | } |
|
66 | 64 | |
|
67 | void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* event) | |
|
65 | void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/) | |
|
68 | 66 | { |
|
69 | m_brush = QBrush(sliceData().m_color.lighter()); | |
|
67 | m_isHovering = true; | |
|
70 | 68 | update(); |
|
69 | // TODO: emit hoverEnter() | |
|
71 | 70 | } |
|
72 | 71 | |
|
73 | 72 | void PieSlice::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/) |
|
74 | 73 | { |
|
75 | m_brush = QBrush(sliceData().m_color); | |
|
74 | m_isHovering = false; | |
|
76 | 75 | update(); |
|
76 | // TODO: emit hoverLeave() | |
|
77 | 77 | } |
|
78 | 78 | |
|
79 | 79 | void PieSlice::mousePressEvent(QGraphicsSceneMouseEvent* /*event*/) |
|
80 | 80 | { |
|
81 | QPieSlice data = sliceData(); | |
|
82 | data.m_isExploded = !data.m_isExploded; | |
|
83 | (static_cast<PiePresenter*>(parentItem()))->m_pieSeries->update(m_seriesIndex, data); | |
|
81 | // TODO: emit clicked | |
|
82 | // TODO: should we let the user decide if this can be exploded? | |
|
83 | m_data.setExploded(!m_data.isExploded()); | |
|
84 | m_series->update(m_data); | |
|
84 | 85 | } |
|
85 | 86 | |
|
86 | void PieSlice::updateGeometry() | |
|
87 | void PieSlice::updateGeometry(QRectF rect, qreal startAngle, qreal span) | |
|
87 | 88 | { |
|
88 | 89 | prepareGeometryChange(); |
|
89 | 90 | |
|
90 | PiePresenter *presenter = static_cast<PiePresenter*>(parentItem()); | |
|
91 | QRectF rect = presenter->pieRect(); | |
|
92 | rect.adjust(EXPLODE_OFFSET, EXPLODE_OFFSET, -EXPLODE_OFFSET ,-EXPLODE_OFFSET); | |
|
91 | // calculate center angle | |
|
92 | qreal centerAngle = startAngle + (span/2); | |
|
93 | 93 | |
|
94 | qreal centerAngle = m_startAngle + (m_span/2); | |
|
95 | ||
|
96 | if (presenter->m_pieSeries->slice(m_seriesIndex).m_isExploded) { | |
|
94 | // adjust rect for exploding | |
|
95 | rect.adjust(EXPLODE_OFFSET, EXPLODE_OFFSET, -EXPLODE_OFFSET ,-EXPLODE_OFFSET); | |
|
96 | if (m_data.isExploded()) { | |
|
97 | 97 | QPointF d = offset((centerAngle), EXPLODE_OFFSET); |
|
98 | 98 | rect.translate(d.x(), d.y()); |
|
99 | 99 | } |
|
100 | 100 | |
|
101 | qreal angle = (-m_startAngle) + (90); | |
|
102 | qreal span = -m_span; | |
|
103 | ||
|
101 | // update slice path | |
|
102 | // TODO: draw the shape so that it might have a hole in the center | |
|
104 | 103 | QPainterPath path; |
|
105 | 104 | path.moveTo(rect.center()); |
|
106 | path.arcTo(rect, angle, span); | |
|
107 | ||
|
108 | // TODO: draw the shape so that it might have a hole in the center | |
|
109 | ||
|
105 | path.arcTo(rect, -startAngle + 90, -span); | |
|
110 | 106 | m_path = path; |
|
111 | 107 | m_rect = path.boundingRect(); |
|
112 | 108 | |
|
109 | // update label position | |
|
113 | 110 | qreal radius = rect.height() / 2; |
|
114 |
|
|
|
111 | QPointF edgeCenter = rect.center() + offset(centerAngle, radius + 5); | |
|
112 | m_slicelabel->updateGeometry(edgeCenter, centerAngle, 50); | |
|
113 | ||
|
114 | //qDebug() << "PieSlice::updateGeometry" << m_rect; | |
|
115 | 115 | } |
|
116 | 116 | |
|
117 |
|
|
|
117 | void PieSlice::updateData() | |
|
118 | 118 | { |
|
119 | return (static_cast<PiePresenter*>(parentItem()))->m_pieSeries->slice(m_seriesIndex); | |
|
119 | if (!m_series->m_slices.contains(m_id)) | |
|
120 | qWarning() << "PieSlice::updateData(): cannot find slice data!" << m_id; | |
|
121 | ||
|
122 | QPieSlice data = m_series->slice(m_id); | |
|
123 | // TODO: find out what has changed and trigger some animation | |
|
124 | m_data = data; | |
|
125 | ||
|
126 | update(); | |
|
127 | ||
|
128 | m_slicelabel->setLabel(m_data.label()); | |
|
129 | m_slicelabel->setVisible(m_data.isLabelVisible()); | |
|
130 | m_slicelabel->update(); | |
|
120 | 131 | } |
|
121 | 132 | |
|
133 | #include "moc_pieslice.cpp" | |
|
134 | ||
|
122 | 135 | QTCOMMERCIALCHART_END_NAMESPACE |
@@ -11,11 +11,14 | |||
|
11 | 11 | |
|
12 | 12 | QTCOMMERCIALCHART_BEGIN_NAMESPACE |
|
13 | 13 | class PiePresenter; |
|
14 | class PieSliceLabel; | |
|
14 | 15 | |
|
15 |
class PieSlice : public QGraphics |
|
|
16 | class PieSlice : public QGraphicsObject | |
|
16 | 17 | { |
|
18 | Q_OBJECT | |
|
19 | ||
|
17 | 20 | public: |
|
18 | PieSlice(PiePresenter *piePresenter, int seriesIndex, qreal startAngle, qreal span); | |
|
21 | PieSlice(QPieSliceId id, QPieSeries *series, QGraphicsItem* parent = 0); | |
|
19 | 22 | ~PieSlice(); |
|
20 | 23 | |
|
21 | 24 | public: // from QGraphicsItem |
@@ -26,22 +29,21 public: // from QGraphicsItem | |||
|
26 | 29 | void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); |
|
27 | 30 | void mousePressEvent(QGraphicsSceneMouseEvent *event); |
|
28 | 31 | |
|
29 | public: | |
|
30 | void updateGeometry(); | |
|
32 | Q_SIGNALS: | |
|
33 | void clicked(); | |
|
31 | 34 | |
|
32 | private: | |
|
33 | QPieSlice sliceData(); | |
|
35 | public: | |
|
36 | void updateGeometry(QRectF rect, qreal startAngle, qreal span); | |
|
37 | void updateData(); | |
|
34 | 38 | |
|
35 | 39 | private: |
|
36 | QGraphicsTextItem* m_label; | |
|
37 |
|
|
|
38 | qreal m_startAngle; | |
|
39 | qreal m_span; | |
|
40 | QPieSliceId m_id; | |
|
41 | QPieSeries* m_series; | |
|
42 | QPieSlice m_data; | |
|
43 | PieSliceLabel* m_slicelabel; | |
|
40 | 44 | QPainterPath m_path; |
|
41 | 45 | QRectF m_rect; |
|
42 | QPointF m_center; | |
|
43 | QPen m_pen; | |
|
44 | QBrush m_brush; | |
|
46 | bool m_isHovering; | |
|
45 | 47 | }; |
|
46 | 48 | |
|
47 | 49 | QTCOMMERCIALCHART_END_NAMESPACE |
@@ -8,7 +8,8 QTCOMMERCIALCHART_BEGIN_NAMESPACE | |||
|
8 | 8 | QPieSeries::QPieSeries(QObject *parent) : |
|
9 | 9 | QChartSeries(parent), |
|
10 | 10 | m_sizeFactor(1.0), |
|
11 | m_position(PiePositionMaximized) | |
|
11 | m_position(PiePositionMaximized), | |
|
12 | m_sliceIdSeed(0) | |
|
12 | 13 | { |
|
13 | 14 | } |
|
14 | 15 | |
@@ -19,70 +20,93 QPieSeries::~QPieSeries() | |||
|
19 | 20 | |
|
20 | 21 | bool QPieSeries::setData(QList<qreal> data) |
|
21 | 22 | { |
|
23 | // TODO: remove this function | |
|
22 | 24 | QList<QPieSlice> slices; |
|
23 | 25 | foreach (int value, data) |
|
24 | 26 | slices << QPieSlice(value, QString::number(value)); |
|
25 | 27 | return set(slices); |
|
26 | 28 | } |
|
27 | 29 | |
|
28 | bool QPieSeries::set(QList<QPieSlice> slices) | |
|
30 | bool QPieSeries::set(const QList<QPieSlice>& slices) | |
|
29 | 31 | { |
|
30 | 32 | if (!slices.count()) |
|
31 | 33 | return false; |
|
32 | 34 | |
|
33 |
|
|
|
35 | ChangeSet changeSet; | |
|
34 | 36 | |
|
35 | for (int i=slices.count(); i<m_slices.count(); i++) | |
|
36 | changeSet.m_removed << i; | |
|
37 | foreach (QPieSlice s, m_slices.values()) | |
|
38 | changeSet.m_removed << s.id(); | |
|
37 | 39 | |
|
38 | for (int i=0; i<slices.count(); i++) { | |
|
39 | if (i < m_slices.count()) | |
|
40 | changeSet.m_changed << i; | |
|
41 | else | |
|
42 | changeSet.m_added << i; | |
|
40 | m_slices.clear(); | |
|
41 | ||
|
42 | foreach (QPieSlice s, slices) { | |
|
43 | s.m_id = generateSliceId(); | |
|
44 | m_slices.insert(s.id(), s); | |
|
45 | changeSet.m_added << s.id(); | |
|
43 | 46 | } |
|
44 | 47 | |
|
45 | m_slices = slices; | |
|
48 | updateDerivativeData(); | |
|
46 | 49 | emit changed(changeSet); |
|
50 | ||
|
47 | 51 | return true; |
|
48 | 52 | } |
|
49 | 53 | |
|
50 | bool QPieSeries::add(QList<QPieSlice> slices) | |
|
54 | bool QPieSeries::add(const QList<QPieSlice>& slices) | |
|
51 | 55 | { |
|
52 | 56 | if (!slices.count()) |
|
53 | 57 | return false; |
|
54 | 58 | |
|
55 |
|
|
|
56 | for (int i=0; i<slices.count(); i++) | |
|
57 | changeSet.m_added << m_slices.count() + i; | |
|
59 | ChangeSet changeSet; | |
|
60 | foreach (QPieSlice s, slices) { | |
|
61 | s.m_id = generateSliceId(); | |
|
62 | m_slices.insert(s.id(), s); | |
|
63 | changeSet.m_added << s.id(); | |
|
64 | } | |
|
58 | 65 | |
|
59 | m_slices += slices; | |
|
66 | updateDerivativeData(); | |
|
60 | 67 | emit changed(changeSet); |
|
68 | ||
|
61 | 69 | return true; |
|
62 | 70 | } |
|
63 | 71 | |
|
64 | bool QPieSeries::add(QPieSlice slice) | |
|
72 | bool QPieSeries::add(const QPieSlice& slice) | |
|
65 | 73 | { |
|
66 | 74 | return add(QList<QPieSlice>() << slice); |
|
67 | 75 | } |
|
68 | 76 | |
|
69 | QPieSlice QPieSeries::slice(int index) const | |
|
77 | bool QPieSeries::update(const QPieSlice& slice) | |
|
70 | 78 | { |
|
71 | if ((index >= 0) && (index < m_slices.count())) | |
|
72 | return m_slices.at(index); | |
|
73 | return QPieSlice(); | |
|
79 | if (!m_slices.contains(slice.id())) | |
|
80 | return false; // series does not contain this slice | |
|
81 | ||
|
82 | m_slices[slice.id()] = slice; | |
|
83 | ||
|
84 | ChangeSet changeSet; | |
|
85 | changeSet.m_changed << slice.id(); | |
|
86 | updateDerivativeData(); | |
|
87 | emit changed(changeSet); | |
|
88 | ||
|
89 | return true; | |
|
74 | 90 | } |
|
75 | 91 | |
|
76 |
bool QPieSeries:: |
|
|
92 | bool QPieSeries::remove(QPieSliceId id) | |
|
77 | 93 | { |
|
78 | if ((index >= 0) && (index < m_slices.count())) { | |
|
79 | m_slices[index] = slice; | |
|
80 | PieChangeSet changeSet; | |
|
81 | changeSet.m_changed << index; | |
|
82 | emit changed(changeSet); | |
|
83 | return true; | |
|
84 | } | |
|
85 | return false; | |
|
94 | if (!m_slices.contains(id)) | |
|
95 | return false; // series does not contain this slice | |
|
96 | ||
|
97 | m_slices.remove(id); | |
|
98 | ||
|
99 | ChangeSet changeSet; | |
|
100 | changeSet.m_removed << id; | |
|
101 | updateDerivativeData(); | |
|
102 | emit changed(changeSet); | |
|
103 | ||
|
104 | return true; | |
|
105 | } | |
|
106 | ||
|
107 | QPieSlice QPieSeries::slice(QPieSliceId id) const | |
|
108 | { | |
|
109 | return m_slices.value(id); | |
|
86 | 110 | } |
|
87 | 111 | |
|
88 | 112 | void QPieSeries::setSizeFactor(qreal factor) |
@@ -104,6 +128,25 void QPieSeries::setPosition(PiePosition position) | |||
|
104 | 128 | } |
|
105 | 129 | } |
|
106 | 130 | |
|
131 | QPieSliceId QPieSeries::generateSliceId() | |
|
132 | { | |
|
133 | // Id is quint64 so it should be enough for us. | |
|
134 | // Note that id is not unique between pie series. | |
|
135 | return m_sliceIdSeed++; | |
|
136 | } | |
|
137 | ||
|
138 | void QPieSeries::updateDerivativeData() | |
|
139 | { | |
|
140 | m_total = 0; | |
|
141 | foreach (const QPieSlice& s, m_slices.values()) | |
|
142 | m_total += s.value(); | |
|
143 | ||
|
144 | Q_ASSERT(m_total > 0); // TODO: remove this before release | |
|
145 | ||
|
146 | foreach (QPieSliceId id, m_slices.keys()) | |
|
147 | m_slices[id].m_percentage = m_slices.value(id).value() / m_total; | |
|
148 | } | |
|
149 | ||
|
107 | 150 | #include "moc_qpieseries.cpp" |
|
108 | 151 | |
|
109 | 152 | QTCOMMERCIALCHART_END_NAMESPACE |
@@ -5,33 +5,62 | |||
|
5 | 5 | #include <QObject> |
|
6 | 6 | #include <QRectF> |
|
7 | 7 | #include <QColor> |
|
8 | #include <QPen> | |
|
9 | #include <QBrush> | |
|
8 | 10 | |
|
9 | 11 | class QGraphicsObject; |
|
10 | 12 | QTCOMMERCIALCHART_BEGIN_NAMESPACE |
|
11 | 13 | class PiePresenter; |
|
12 | 14 | class PieSlice; |
|
13 | 15 | |
|
16 | typedef quint64 QPieSliceId; | |
|
17 | ||
|
14 | 18 | class QPieSlice |
|
15 | 19 | { |
|
16 | 20 | public: |
|
17 | 21 | QPieSlice() |
|
18 | :m_value(0), m_label("<empty>"), m_color(QColor::Invalid), m_isExploded(false) {} | |
|
22 | :m_id(-1), m_value(0), m_isLabelVisible(true), m_isExploded(false), m_percentage(0) {} | |
|
19 | 23 | |
|
20 |
QPieSlice(qreal value, QString label = |
|
|
21 |
:m_value(value), m_label(label), m_ |
|
|
22 | public: | |
|
24 | QPieSlice(qreal value, QString label = QString(), bool labelVisible = true, bool exploded = false, QPen pen = QPen(), QBrush brush = QBrush()) | |
|
25 | :m_id(-1), m_value(value), m_label(label), m_isLabelVisible(labelVisible), m_isExploded(exploded), m_pen(pen), m_brush(brush), m_percentage(0) {} | |
|
26 | ||
|
27 | QPieSliceId id() const { return m_id; } | |
|
28 | ||
|
29 | void setValue(qreal value) { m_value = value; } | |
|
30 | qreal value() const { return m_value; } | |
|
31 | ||
|
32 | void setLabel(QString label) { m_label = label; } | |
|
33 | QString label() const { return m_label; } | |
|
34 | ||
|
35 | void setLabelVisible(bool visible) { m_isLabelVisible = visible; } | |
|
36 | bool isLabelVisible() const { return m_isLabelVisible; } | |
|
37 | ||
|
38 | void setExploded(bool exploded) { m_isExploded = exploded; } | |
|
39 | bool isExploded() const { return m_isExploded; } | |
|
40 | ||
|
41 | void setPen(QPen pen) { m_pen = pen; } | |
|
42 | QPen pen() const { return m_pen; } | |
|
43 | ||
|
44 | void setBrush(QBrush brush) { m_brush = brush; } | |
|
45 | QBrush brush() const { return m_brush; } | |
|
46 | ||
|
47 | qreal percentage() const { return m_percentage; } | |
|
48 | ||
|
49 | private: | |
|
50 | ||
|
51 | // TODO: use private class | |
|
52 | friend class QPieSeries; | |
|
53 | ||
|
54 | QPieSliceId m_id; | |
|
23 | 55 | qreal m_value; |
|
24 | 56 | QString m_label; |
|
25 | QColor m_color; // TODO: should we even define color here? | |
|
57 | bool m_isLabelVisible; | |
|
26 | 58 | bool m_isExploded; |
|
27 | }; | |
|
28 | 59 | |
|
29 | class PieChangeSet | |
|
30 | { | |
|
31 | public: | |
|
32 | QList<int> m_added; | |
|
33 | QList<int> m_removed; | |
|
34 | QList<int> m_changed; | |
|
60 | QPen m_pen; | |
|
61 | QBrush m_brush; | |
|
62 | ||
|
63 | qreal m_percentage; // generated content | |
|
35 | 64 | }; |
|
36 | 65 | |
|
37 | 66 | class QTCOMMERCIALCHART_EXPORT QPieSeries : public QChartSeries |
@@ -47,24 +76,38 public: | |||
|
47 | 76 | PiePositionBottomRight |
|
48 | 77 | }; |
|
49 | 78 | |
|
79 | class ChangeSet | |
|
80 | { | |
|
81 | public: | |
|
82 | QList<QPieSliceId> m_added; | |
|
83 | QList<QPieSliceId> m_removed; | |
|
84 | QList<QPieSliceId> m_changed; | |
|
85 | }; | |
|
86 | ||
|
50 | 87 | public: |
|
51 | 88 | QPieSeries(QObject *parent = 0); |
|
52 | 89 | ~QPieSeries(); |
|
53 | 90 | |
|
54 | 91 | public: // from QChartSeries |
|
55 | 92 | QChartSeriesType type() const { return QChartSeries::SeriesTypePie; } |
|
56 | virtual bool setData(QList<qreal> data); | |
|
93 | virtual bool setData(QList<qreal> data); // TODO: remove this | |
|
57 | 94 | |
|
58 | 95 | public: |
|
59 | bool set(QList<QPieSlice> slices); | |
|
60 | bool add(QList<QPieSlice> slices); | |
|
61 |
bool |
|
|
96 | // TODO: should we return id/bool or what? | |
|
97 | // TODO: should we prefer passing a modifiable reference? | |
|
98 | bool set(const QList<QPieSlice>& slices); | |
|
99 | bool add(const QList<QPieSlice>& slices); | |
|
100 | bool add(const QPieSlice& slice); | |
|
101 | bool update(const QPieSlice& slice); | |
|
102 | bool remove(QPieSliceId id); | |
|
62 | 103 | |
|
63 | 104 | int count() const { return m_slices.count(); } |
|
64 | 105 | |
|
65 | QList<QPieSlice> slices() const { return m_slices; } | |
|
66 | QPieSlice slice(int index) const; | |
|
67 | bool update(int index, QPieSlice slice); | |
|
106 | QList<QPieSlice> slices() const { return m_slices.values(); } | |
|
107 | QList<QPieSliceId> ids() const { return m_slices.keys(); } | |
|
108 | QPieSlice slice(QPieSliceId id) const; | |
|
109 | ||
|
110 | // TODO: sorting? | |
|
68 | 111 | |
|
69 | 112 | // TODO: convenience functions |
|
70 | 113 | //void updateValue(int sliceIndex, qreal value); |
@@ -72,6 +115,11 public: | |||
|
72 | 115 | //void updateColor(int sliceIndex, QColor color); |
|
73 | 116 | //void updateExploded(int slizeIndex, bool exploded); |
|
74 | 117 | |
|
118 | // TODO: customization | |
|
119 | // set/get pen/brush | |
|
120 | // - for label | |
|
121 | // - for whole pie/slice | |
|
122 | ||
|
75 | 123 | void setSizeFactor(qreal sizeFactor); |
|
76 | 124 | qreal sizeFactor() const { return m_sizeFactor; } |
|
77 | 125 | |
@@ -79,17 +127,29 public: | |||
|
79 | 127 | PiePosition position() const { return m_position; } |
|
80 | 128 | |
|
81 | 129 | Q_SIGNALS: |
|
82 | void changed(const PieChangeSet& changeSet); | |
|
130 | void changed(const QPieSeries::ChangeSet& changeSet); | |
|
83 | 131 | void sizeFactorChanged(); |
|
84 | 132 | void positionChanged(); |
|
133 | //void sliceClicked(QPieSliceId id); | |
|
134 | //void sliceHoverEnter(QPieSliceId id); | |
|
135 | //void sliceHoverLeave(QPieSliceId id); | |
|
136 | ||
|
137 | private: | |
|
138 | QPieSliceId generateSliceId(); | |
|
139 | void updateDerivativeData(); | |
|
85 | 140 | |
|
86 | 141 | private: |
|
87 | 142 | Q_DISABLE_COPY(QPieSeries) |
|
88 | friend class PiePresenter; | |
|
143 | ||
|
89 | 144 | // TODO: use PIML |
|
90 | QList<QPieSlice> m_slices; | |
|
145 | friend class PiePresenter; | |
|
146 | friend class PieSlice; | |
|
147 | ||
|
148 | QHash<QPieSliceId, QPieSlice> m_slices; | |
|
91 | 149 | qreal m_sizeFactor; |
|
92 | 150 | PiePosition m_position; |
|
151 | qreal m_total; | |
|
152 | QPieSliceId m_sliceIdSeed; | |
|
93 | 153 | }; |
|
94 | 154 | |
|
95 | 155 | QTCOMMERCIALCHART_END_NAMESPACE |
General Comments 0
You need to be logged in to leave comments.
Login now