##// END OF EJS Templates
Refactoring piechart API (and internals)
Jani Honkonen -
r174:0d781305aa75
parent child
Show More
@@ -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, true));
21 series->add(QPieSlice(2, "test2", Qt::green));
22 series->add(QPieSlice(3, "test3", Qt::blue));
23 series->add(QPieSlice(4, "test4", Qt::darkRed, true));
24 series->add(QPieSlice(5, "test5", Qt::darkGreen));
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 slice.m_color = colors.at(i);
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_pieSeries(series)
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_pieSeries->sizeFactor());
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_pieSeries->sizeFactor());
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_pieSeries->position()) {
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 QList<PieSlice*> m_slices;
36 QPieSeries *m_pieSeries;
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 :QGraphicsItem(presenter),
23 m_label(new QGraphicsTextItem(this)),
24 m_seriesIndex(seriesIndex),
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(presenter);
30 Q_ASSERT(series);
29 31 setAcceptHoverEvents(true);
30 32 setAcceptedMouseButtons(Qt::LeftButton);
31 updateGeometry();
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(m_brush);
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 m_center = rect.center() + offset(centerAngle, radius / 2);
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 QPieSlice PieSlice::sliceData()
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 QGraphicsItem
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 int m_seriesIndex;
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 PieChangeSet changeSet;
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 PieChangeSet changeSet;
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::update(int index, QPieSlice slice)
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 = "<empty>", QColor color = QColor::Invalid, bool exploded = false)
21 :m_value(value), m_label(label), m_color(color), m_isExploded(exploded) {}
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 add(QPieSlice slice);
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