##// END OF EJS Templates
Make pie fit better inside its given rectangle. Label texts still go outside. Needs a bit more work...
Jani Honkonen -
r289:46889b1144e0
parent child
Show More
@@ -1,43 +1,40
1 1 #include <QtGui/QApplication>
2 2 #include <QMainWindow>
3 3 #include <qchartglobal.h>
4 4 #include <qchartview.h>
5 5 #include <qpieseries.h>
6 6 #include <qpieslice.h>
7 7 #include "customslice.h"
8 8
9 9 QTCOMMERCIALCHART_USE_NAMESPACE
10 10
11 11 int main(int argc, char *argv[])
12 12 {
13 13 QApplication a(argc, argv);
14 14
15 15 QMainWindow window;
16 16
17 17 QPieSeries *series = new QPieSeries();
18 18 series->add(5, "Slice 1");
19 19 series->add(2, "Slice 2");
20 20 series->add(3, "Slice 3");
21 21 series->add(4, "Slice 4");
22 22 series->add(5, "Slice 5");
23 23 series->add(6, "Slice 6");
24 24 series->add(7, "Slice 7");
25 25 series->add(new CustomSlice(8));
26 26 series->enableClickExplodes(true);
27 27 series->enableHoverHighlight(true);
28 28
29 foreach (QPieSlice*s, series->slices())
30 qDebug() << s->angle() << s->span() << s->percentage();
31
32 29 QChartView* chartView = new QChartView(&window);
33 30 chartView->setRenderHint(QPainter::Antialiasing);
34 31 chartView->addSeries(series);
35 32 chartView->setChartTitle("simple piechart");
36 33 chartView->setChartTheme(QChart::ChartThemeIcy);
37 34
38 35 window.setCentralWidget(chartView);
39 36 window.resize(600, 600);
40 37 window.show();
41 38
42 39 return a.exec();
43 40 }
@@ -1,158 +1,210
1 1
2 2 #include "piepresenter.h"
3 3 #include "pieslice.h"
4 4 #include "qpieslice.h"
5 #include "pieslicelabel.h"
6 #include "qpieseries.h"
7 #include <qmath.h>
5 8 #include <QDebug>
6 #include <QTime>
9 #include <QFontMetrics>
10
7 11
8 12 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9 13
10 14 PiePresenter::PiePresenter(QGraphicsItem *parent, QPieSeries *series)
11 15 :ChartItem(parent),
12 16 m_series(series)
13 17 {
14 18 Q_ASSERT(series);
15 19 connect(series, SIGNAL(changed(const QPieSeries::ChangeSet&)), this, SLOT(handleSeriesChanged(const QPieSeries::ChangeSet&)));
16 20 connect(series, SIGNAL(sizeFactorChanged()), this, SLOT(updateGeometry()));
17 21 connect(series, SIGNAL(positionChanged()), this, SLOT(updateGeometry()));
22
23 if (m_series->count()) {
24 QPieSeries::ChangeSet changeSet;
25 changeSet.appendAdded(m_series->m_slices);
26 handleSeriesChanged(changeSet);
27 }
18 28 }
19 29
20 30 PiePresenter::~PiePresenter()
21 31 {
22 32 // slices deleted automatically through QGraphicsItem
23 33 }
24 34
25 void PiePresenter::paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *)
35 void PiePresenter::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
26 36 {
27 37 // TODO: paint shadows for all components
28 38 // - get paths from items & merge & offset and draw with shadow color?
29 39 }
30 40
31 41 void PiePresenter::handleSeriesChanged(const QPieSeries::ChangeSet& changeSet)
32 42 {
33 43 //qDebug() << "PiePresenter::handleSeriesChanged()";
34 44 //qDebug() << " added : " << changeSet.added();
35 45 //qDebug() << " changed: " << changeSet.changed();
36 46 //qDebug() << " removed: " << changeSet.removed();
37 47
38 // ignore changeset when there are no visual slices
39 // changeset might not be valid about the added slices
40 if (m_slices.count() == 0) {
41 foreach (QPieSlice* s, m_series->m_slices)
42 addSlice(s);
43 return;
44 }
48 foreach (QPieSlice* s, changeSet.added())
49 addSlice(s);
50
51 foreach (QPieSlice* s, changeSet.changed())
52 updateSlice(s);
45 53
46 54 foreach (QPieSlice* s, changeSet.removed())
47 55 deleteSlice(s);
48 56
49 foreach (QPieSlice* s, changeSet.added())
50 addSlice(s);
57 // every change possibly changes the actual pie size
58 updateGeometry();
51 59 }
52 60
53 61 void PiePresenter::handleDomainChanged(const Domain& domain)
54 62 {
55 63 // TODO
56 64 }
57 65
58 66 void PiePresenter::handleGeometryChanged(const QRectF& rect)
59 67 {
60 68 m_rect = rect;
69 prepareGeometryChange();
61 70 updateGeometry();
62 71 }
63 72
64 73 void PiePresenter::updateGeometry()
65 74 {
66 prepareGeometryChange();
75 if (!m_rect.isValid() || m_rect.isEmpty())
76 return;
67 77
78 // calculate maximum rectangle for pie
68 79 QRectF pieRect = m_rect;
69
70 80 if (pieRect.width() < pieRect.height()) {
71 81 pieRect.setWidth(pieRect.width() * m_series->sizeFactor());
72 82 pieRect.setHeight(pieRect.width());
73 83 pieRect.moveCenter(m_rect.center());
74 84 } else {
75 85 pieRect.setHeight(pieRect.height() * m_series->sizeFactor());
76 86 pieRect.setWidth(pieRect.height());
77 87 pieRect.moveCenter(m_rect.center());
78 88 }
79 89
90 // position the pie rectangle
80 91 switch (m_series->position()) {
81 92 case QPieSeries::PiePositionTopLeft: {
82 93 pieRect.setHeight(pieRect.height() / 2);
83 94 pieRect.setWidth(pieRect.height());
84 95 pieRect.moveCenter(QPointF(m_rect.center().x() / 2, m_rect.center().y() / 2));
85 96 break;
86 97 }
87 98 case QPieSeries::PiePositionTopRight: {
88 99 pieRect.setHeight(pieRect.height() / 2);
89 100 pieRect.setWidth(pieRect.height());
90 101 pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, m_rect.center().y() / 2));
91 102 break;
92 103 }
93 104 case QPieSeries::PiePositionBottomLeft: {
94 105 pieRect.setHeight(pieRect.height() / 2);
95 106 pieRect.setWidth(pieRect.height());
96 107 pieRect.moveCenter(QPointF(m_rect.center().x() / 2, (m_rect.center().y() / 2) * 3));
97 108 break;
98 109 }
99 110 case QPieSeries::PiePositionBottomRight: {
100 111 pieRect.setHeight(pieRect.height() / 2);
101 112 pieRect.setWidth(pieRect.height());
102 113 pieRect.moveCenter(QPointF((m_rect.center().x() / 2) * 3, (m_rect.center().y() / 2) * 3));
103 114 break;
104 115 }
105 116 default:
106 117 break;
107 118 }
108 119
120 // calculate how much space we need around the pie rectangle (labels & exploding)
121 qreal delta = 0;
122 qreal pieRadius = pieRect.height() / 2;
123 foreach (QPieSlice* s, m_series->m_slices) {
124
125 // calculate the farthest point in the slice from the pie center
126 qreal centerAngle = s->angle() + (s->angleSpan() / 2);
127 qreal len = pieRadius + s->labelArmLength() + s->explodeDistance();
128 QPointF dp(qSin(centerAngle*(PI/180)) * len, -qCos(centerAngle*(PI/180)) * len);
129 QPointF p = pieRect.center() + dp;
130
131 // TODO: consider the label text
132
133 // calculate how much the radius must get smaller to fit that point in the base rectangle
134 qreal dt = m_rect.top() - p.y();
135 if (dt > delta) delta = dt;
136 qreal dl = m_rect.left() - p.x();
137 if (dl > delta) delta = dl;
138 qreal dr = p.x() - m_rect.right();
139 if (dr > delta) delta = dr;
140 qreal db = p.y() - m_rect.bottom();
141 if (db > delta) delta = db;
142
143 //if (!m_rect.contains(p)) qDebug() << s->label() << dt << dl << dr << db << "delta" << delta;
144 }
145
146 // shrink the pie rectangle so that everything outside it fits the base rectangle
147 pieRect.adjust(delta, delta, -delta, -delta);
148
149 // update slices
109 150 if (m_pieRect != pieRect) {
110 151 m_pieRect = pieRect;
111 //qDebug() << "PiePresenter::updateGeometry()" << m_pieRect;
152 //qDebug() << "PiePresenter::updateGeometry()" << m_rect << m_pieRect;
112 153 foreach (PieSlice* s, m_slices.values()) {
113 154 s->setPieRect(m_pieRect);
114 155 s->updateGeometry();
156 s->update();
115 157 }
116 158 }
117 159 }
118 160
119 161 void PiePresenter::addSlice(QPieSlice* sliceData)
120 162 {
121 163 //qDebug() << "PiePresenter::addSlice()" << sliceData;
122 164
123 165 if (m_slices.keys().contains(sliceData)) {
124 //qWarning() << "PiePresenter::addSlice(): slice already exists!" << sliceData;
125 Q_ASSERT(0);
166 Q_ASSERT(0); // TODO: how to handle this nicely?
126 167 return;
127 168 }
128 169
129 170 // create slice
130 171 PieSlice *slice = new PieSlice(this);
131 172 slice->setPieRect(m_pieRect);
132 173 slice->updateData(sliceData);
133 174 slice->updateGeometry();
134 175 slice->update();
135 176 m_slices.insert(sliceData, slice);
136 177
137 178 // connect signals
138 connect(sliceData, SIGNAL(changed()), slice, SLOT(handleSliceDataChanged()));
139 179 connect(slice, SIGNAL(clicked()), sliceData, SIGNAL(clicked()));
140 180 connect(slice, SIGNAL(hoverEnter()), sliceData, SIGNAL(hoverEnter()));
141 181 connect(slice, SIGNAL(hoverLeave()), sliceData, SIGNAL(hoverLeave()));
142 182 }
143 183
184 void PiePresenter::updateSlice(QPieSlice* sliceData)
185 {
186 //qDebug() << "PiePresenter::updateSlice()" << sliceData;
187
188 if (!m_slices.contains(sliceData)) {
189 Q_ASSERT(0); // TODO: how to handle this nicely?
190 return;
191 }
192
193 m_slices[sliceData]->updateData(sliceData);
194 }
195
144 196 void PiePresenter::deleteSlice(QPieSlice* sliceData)
145 197 {
146 198 //qDebug() << "PiePresenter::deleteSlice()" << sliceData;
147 199
148 if (m_slices.contains(sliceData))
149 delete m_slices.take(sliceData);
150 else {
151 // nothing to remove
152 Q_ASSERT(0); // TODO: remove before release
200 if (!m_slices.contains(sliceData)) {
201 Q_ASSERT(0); // TODO: how to handle this nicely?
202 return;
153 203 }
204
205 delete m_slices.take(sliceData);
154 206 }
155 207
156 208 #include "moc_piepresenter.cpp"
157 209
158 210 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,48 +1,51
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 #define PI 3.14159265 // TODO: is this defined in some header?
13
12 14 class PiePresenter : public QObject, public ChartItem
13 15 {
14 16 Q_OBJECT
15 17
16 18 public:
17 19 // TODO: use a generic data class instead of x and y
18 20 PiePresenter(QGraphicsItem *parent, QPieSeries *series);
19 21 ~PiePresenter();
20 22
21 23 public: // from QGraphicsItem
22 24 QRectF boundingRect() const { return m_rect; }
23 25 void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
24 26
25 27 public:
26 28 QRectF pieRect() const { return m_pieRect; }
27 29
28 30 public Q_SLOTS:
29 31 void handleSeriesChanged(const QPieSeries::ChangeSet& changeSet);
30 32 void handleDomainChanged(const Domain& domain);
31 33 void handleGeometryChanged(const QRectF& rect);
32 34 void updateGeometry();
33 35
34 36 private:
35 37 void addSlice(QPieSlice* sliceData);
38 void updateSlice(QPieSlice* sliceData);
36 39 void deleteSlice(QPieSlice* sliceData);
37 40
38 41 private:
39 42 friend class PieSlice;
40 43 QHash<QPieSlice*, PieSlice*> m_slices;
41 44 QPieSeries *m_series;
42 45 QRectF m_rect;
43 46 QRectF m_pieRect;
44 47 };
45 48
46 49 QTCOMMERCIALCHART_END_NAMESPACE
47 50
48 51 #endif // PIEPRESENTER_H
@@ -1,140 +1,135
1 1 #include "pieslice.h"
2 2 #include "pieslicelabel.h"
3 3 #include "piepresenter.h"
4 4 #include "qpieseries.h"
5 5 #include "qpieslice.h"
6 6 #include <QPainter>
7 7 #include <QDebug>
8 8 #include <qmath.h>
9 9 #include <QGraphicsSceneEvent>
10 10 #include <QTime>
11 11
12 12 QTCOMMERCIALCHART_BEGIN_NAMESPACE
13 13
14 #define PI 3.14159265
15 #define EXPLODE_OFFSET 20
16
17 14 QPointF offset(qreal angle, qreal length)
18 15 {
19 16 qreal dx = qSin(angle*(PI/180)) * length;
20 17 qreal dy = qCos(angle*(PI/180)) * length;
21 18 return QPointF(dx, -dy);
22 19 }
23 20
24 21 PieSlice::PieSlice(QGraphicsItem* parent)
25 22 :QGraphicsObject(parent),
26 23 m_slicelabel(new PieSliceLabel(this)),
27 24 m_angle(0),
28 m_span(0),
29 m_isExploded(false)
25 m_angleSpan(0),
26 m_isExploded(false),
27 m_explodeDistance(0)
30 28 {
31 29 setAcceptHoverEvents(true);
32 30 setAcceptedMouseButtons(Qt::LeftButton);
33 31 }
34 32
35 33 PieSlice::~PieSlice()
36 34 {
37 35
38 36 }
39 37
40 38 QRectF PieSlice::boundingRect() const
41 39 {
42 40 return m_path.boundingRect();
43 41 }
44 42
45 43 QPainterPath PieSlice::shape() const
46 44 {
47 45 return m_path;
48 46 }
49 47
50 48 void PieSlice::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
51 49 {
52 50 painter->setPen(m_pen);
53 51 painter->setBrush(m_brush);
54 52 painter->drawPath(m_path);
55 53 }
56 54
57 55 void PieSlice::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
58 56 {
59 57 emit hoverEnter();
60 58 }
61 59
62 60 void PieSlice::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
63 61 {
64 62 emit hoverLeave();
65 63 }
66 64
67 65 void PieSlice::mousePressEvent(QGraphicsSceneMouseEvent* /*event*/)
68 66 {
69 67 emit clicked();
70 68 }
71 69
72 70 void PieSlice::setPieRect(QRectF rect)
73 71 {
74 72 m_pieRect = rect;
75 73 }
76 74
77 75 void PieSlice::updateGeometry()
78 76 {
77 if (!m_pieRect.isValid() || m_pieRect.isEmpty())
78 return;
79
79 80 prepareGeometryChange();
80 81
81 82 // calculate center angle
82 qreal centerAngle = m_angle + (m_span/2);
83 qreal centerAngle = m_angle + (m_angleSpan/2);
83 84
84 85 // adjust rect for exploding
85 86 QRectF rect = m_pieRect;
86 rect.adjust(EXPLODE_OFFSET, EXPLODE_OFFSET, -EXPLODE_OFFSET ,-EXPLODE_OFFSET);
87 87 if (m_isExploded) {
88 QPointF d = offset((centerAngle), EXPLODE_OFFSET);
89 rect.translate(d.x(), d.y());
88 qreal dx = qSin(centerAngle*(PI/180)) * m_explodeDistance;
89 qreal dy = -qCos(centerAngle*(PI/180)) * m_explodeDistance;
90 rect.translate(dx, dy);
90 91 }
91 92
92 93 // update slice path
93 94 // TODO: draw the shape so that it might have a hole in the center
94 95 QPainterPath path;
95 96 path.moveTo(rect.center());
96 path.arcTo(rect, -m_angle + 90, -m_span);
97 path.arcTo(rect, -m_angle + 90, -m_angleSpan);
97 98 path.closeSubpath();
98 99 m_path = path;
99 100
100 101 // update label position
101 102 qreal radius = rect.height() / 2;
102 103 QPointF edgeCenter = rect.center() + offset(centerAngle, radius + 5);
103 104 m_slicelabel->setArmStartPoint(edgeCenter);
104 105 m_slicelabel->setArmAngle(centerAngle);
105 106 m_slicelabel->updateGeometry();
107 m_slicelabel->update();
106 108
107 109 //qDebug() << "PieSlice::updateGeometry" << m_slicelabel->text() << boundingRect() << m_angle << m_span;
108 110 }
109 111
110 void PieSlice::handleSliceDataChanged()
111 {
112 QPieSlice *slice = qobject_cast<QPieSlice*>(sender());
113 Q_ASSERT(slice);
114 updateData(slice);
115 }
116
117 112 void PieSlice::updateData(const QPieSlice* sliceData)
118 113 {
119 114 // TODO: compare what has changes to avoid unneccesary geometry updates
120 115
121 116 m_angle = sliceData->angle();
122 m_span = sliceData->span();
117 m_angleSpan = sliceData->angleSpan();
123 118 m_isExploded = sliceData->isExploded();
119 m_explodeDistance = sliceData->explodeDistance(); // TODO: expose to public API
124 120 m_pen = sliceData->pen();
125 121 m_brush = sliceData->brush();
126 122
127 123 m_slicelabel->setVisible(sliceData->isLabelVisible());
128 124 m_slicelabel->setText(sliceData->label());
129 125 m_slicelabel->setPen(sliceData->labelPen());
130 126 m_slicelabel->setFont(sliceData->labelFont());
131 m_slicelabel->setArmLength(sliceData->labelArmLenght());
127 m_slicelabel->setArmLength(sliceData->labelArmLength());
132 128
133 129 updateGeometry();
134 130 update();
135 m_slicelabel->update();
136 131 }
137 132
138 133 #include "moc_pieslice.cpp"
139 134
140 135 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,60 +1,64
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 QTCOMMERCIALCHART_BEGIN_NAMESPACE
13 13 class PiePresenter;
14 14 class PieSliceLabel;
15 15 class QPieSlice;
16 16
17 17 class PieSlice : public QGraphicsObject
18 18 {
19 19 Q_OBJECT
20 20
21 21 public:
22 22 PieSlice(QGraphicsItem* parent = 0);
23 23 ~PieSlice();
24 24
25 25 public: // from QGraphicsItem
26 26 QRectF boundingRect() const;
27 27 QPainterPath shape() const;
28 28 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
29 29 void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
30 30 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
31 31 void mousePressEvent(QGraphicsSceneMouseEvent *event);
32 32
33 33 Q_SIGNALS:
34 34 void clicked();
35 35 void hoverEnter();
36 36 void hoverLeave();
37 37
38 38 public Q_SLOTS:
39 void handleSliceDataChanged();
40 39 void setPieRect(QRectF rect);
41 40 void updateGeometry();
42 41 void updateData(const QPieSlice *sliceData);
43 42
43 public:
44 PieSliceLabel* label() { return m_slicelabel; }
45
44 46 private:
45 47 PieSliceLabel* m_slicelabel;
46 48
47 49 QRectF m_pieRect;
48 50 QPainterPath m_path;
49 51
50 52 qreal m_angle;
51 qreal m_span;
53 qreal m_angleSpan;
54
52 55 bool m_isExploded;
56 qreal m_explodeDistance;
53 57
54 58 QPen m_pen;
55 59 QBrush m_brush;
56 60 };
57 61
58 62 QTCOMMERCIALCHART_END_NAMESPACE
59 63
60 64 #endif // PIESLICE_H
@@ -1,299 +1,306
1 1 #include "qpieseries.h"
2 2 #include "qpieslice.h"
3 3 #include "piepresenter.h"
4 4 #include "pieslice.h"
5 #include <QFontMetrics>
5 6 #include <QDebug>
6 7
7 8 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 9
9 10 void QPieSeries::ChangeSet::appendAdded(QPieSlice* slice)
10 11 {
11 12 if (!m_added.contains(slice))
12 13 m_added << slice;
13 14 }
14 15
16 void QPieSeries::ChangeSet::appendAdded(QList<QPieSlice*> slices)
17 {
18 foreach (QPieSlice* s, slices)
19 appendAdded(s);
20 }
21
15 22 void QPieSeries::ChangeSet::appendChanged(QPieSlice* slice)
16 23 {
17 24 if (!m_changed.contains(slice))
18 25 m_changed << slice;
19 26 }
20 27
21 28 void QPieSeries::ChangeSet::appendRemoved(QPieSlice* slice)
22 29 {
23 30 if (!m_removed.contains(slice))
24 31 m_removed << slice;
25 32 }
26 33
27 34 QList<QPieSlice*> QPieSeries::ChangeSet::added() const
28 35 {
29 36 return m_added;
30 37 }
31 38
32 39 QList<QPieSlice*> QPieSeries::ChangeSet::changed() const
33 40 {
34 41 return m_changed;
35 42 }
36 43
37 44 QList<QPieSlice*> QPieSeries::ChangeSet::removed() const
38 45 {
39 46 return m_removed;
40 47 }
41 48
42 49 bool QPieSeries::ChangeSet::isEmpty() const
43 50 {
44 51 if (m_added.count() || m_changed.count() || m_removed.count())
45 52 return false;
46 53 return true;
47 54 }
48 55
49 56
50 57 QPieSeries::QPieSeries(QObject *parent) :
51 58 QChartSeries(parent),
52 59 m_sizeFactor(1.0),
53 60 m_position(PiePositionMaximized),
54 61 m_pieStartAngle(0),
55 62 m_pieSpan(360)
56 63 {
57 64
58 65 }
59 66
60 67 QPieSeries::~QPieSeries()
61 68 {
62 69
63 70 }
64 71
65 72 bool QPieSeries::setData(QList<qreal> data)
66 73 {
67 74 // TODO: remove this function
68 75 QList<QPieSlice*> slices;
69 76 foreach (qreal value, data)
70 77 slices << new QPieSlice(value, QString::number(value));
71 78 set(slices);
72 79 return true;
73 80 }
74 81
75 82 void QPieSeries::set(QList<QPieSlice*> slices)
76 83 {
77 84 clear();
78 85 add(slices);
79 86 }
80 87
81 88 void QPieSeries::add(QList<QPieSlice*> slices)
82 89 {
83 90 ChangeSet changeSet;
84 91 foreach (QPieSlice* s, slices) {
85 92 s->setParent(this);
86 93 m_slices << s;
87 94 changeSet.appendAdded(s);
88 95 }
89 96
90 97 updateDerivativeData();
91 98
92 99 foreach (QPieSlice* s, slices) {
93 100 connect(s, SIGNAL(changed()), this, SLOT(sliceChanged()));
94 101 connect(s, SIGNAL(clicked()), this, SLOT(sliceClicked()));
95 102 connect(s, SIGNAL(hoverEnter()), this, SLOT(sliceHoverEnter()));
96 103 connect(s, SIGNAL(hoverLeave()), this, SLOT(sliceHoverLeave()));
97 104 }
98 105
99 106 emit changed(changeSet);
100 107 }
101 108
102 109 void QPieSeries::add(QPieSlice* slice)
103 110 {
104 111 add(QList<QPieSlice*>() << slice);
105 112 }
106 113
107 114 QPieSlice* QPieSeries::add(qreal value, QString name)
108 115 {
109 116 QPieSlice* slice = new QPieSlice(value, name);
110 117 add(slice);
111 118 return slice;
112 119 }
113 120
114 121 void QPieSeries::remove(QPieSlice* slice)
115 122 {
116 123 if (!m_slices.removeOne(slice)) {
117 Q_ASSERT(0); // TODO: remove before release
124 Q_ASSERT(0); // TODO: how should this be reported?
118 125 return;
119 126 }
120 127
121 128 ChangeSet changeSet;
122 129 changeSet.appendRemoved(slice);
123 130 emit changed(changeSet);
124 131
125 132 delete slice;
126 133 slice = NULL;
127 134
128 135 updateDerivativeData();
129 136 }
130 137
131 138 void QPieSeries::clear()
132 139 {
133 140 if (m_slices.count() == 0)
134 141 return;
135 142
136 143 ChangeSet changeSet;
137 144 foreach (QPieSlice* s, m_slices) {
138 145 changeSet.appendRemoved(s);
139 146 m_slices.removeOne(s);
140 147 delete s;
141 148 }
142 149 emit changed(changeSet);
143 150 updateDerivativeData();
144 151 }
145 152
146 153 void QPieSeries::setSizeFactor(qreal factor)
147 154 {
148 155 if (factor < 0.0)
149 156 return;
150 157
151 158 if (m_sizeFactor != factor) {
152 159 m_sizeFactor = factor;
153 160 emit sizeFactorChanged();
154 161 }
155 162 }
156 163
157 164 void QPieSeries::setPosition(PiePosition position)
158 165 {
159 166 if (m_position != position) {
160 167 m_position = position;
161 168 emit positionChanged();
162 169 }
163 170 }
164 171
165 172 void QPieSeries::setSpan(qreal startAngle, qreal span)
166 173 {
167 174 if (startAngle >= 0 && startAngle < 360 &&
168 175 span > 0 && span <= 360) {
169 176 m_pieStartAngle = startAngle;
170 177 m_pieSpan = span;
171 178 updateDerivativeData();
172 179 }
173 180 }
174 181
175 182 void QPieSeries::setLabelsVisible(bool visible)
176 183 {
177 184 foreach (QPieSlice* s, m_slices)
178 185 s->setLabelVisible(visible);
179 186 }
180 187
181 188 void QPieSeries::enableClickExplodes(bool enable)
182 189 {
183 190 if (enable)
184 191 connect(this, SIGNAL(clicked(QPieSlice*)), this, SLOT(toggleExploded(QPieSlice*)));
185 192 else
186 193 disconnect(this, SLOT(toggleExploded(QPieSlice*)));
187 194 }
188 195
189 196 void QPieSeries::enableHoverHighlight(bool enable)
190 197 {
191 198 if (enable) {
192 199 connect(this, SIGNAL(hoverEnter(QPieSlice*)), this, SLOT(highlightOn(QPieSlice*)));
193 200 connect(this, SIGNAL(hoverLeave(QPieSlice*)), this, SLOT(highlightOff(QPieSlice*)));
194 201 } else {
195 202 disconnect(this, SLOT(hoverEnter(QPieSlice*)));
196 203 disconnect(this, SLOT(hoverLeave(QPieSlice*)));
197 204 }
198 205 }
199 206
200 207 void QPieSeries::sliceChanged()
201 208 {
202 209 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
203 210 Q_ASSERT(m_slices.contains(slice));
204 211
205 212 ChangeSet changeSet;
206 213 changeSet.appendChanged(slice);
207 214 emit changed(changeSet);
208 215
209 216 updateDerivativeData();
210 217 }
211 218
212 219 void QPieSeries::sliceClicked()
213 220 {
214 221 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
215 222 Q_ASSERT(m_slices.contains(slice));
216 223 emit clicked(slice);
217 224 }
218 225
219 226 void QPieSeries::sliceHoverEnter()
220 227 {
221 228 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
222 229 Q_ASSERT(m_slices.contains(slice));
223 230 emit hoverEnter(slice);
224 231 }
225 232
226 233 void QPieSeries::sliceHoverLeave()
227 234 {
228 235 QPieSlice* slice = qobject_cast<QPieSlice *>(sender());
229 236 Q_ASSERT(m_slices.contains(slice));
230 237 emit hoverLeave(slice);
231 238 }
232 239
233 240 void QPieSeries::toggleExploded(QPieSlice* slice)
234 241 {
235 242 Q_ASSERT(slice);
236 243 slice->setExploded(!slice->isExploded());
237 244 }
238 245
239 246 void QPieSeries::highlightOn(QPieSlice* slice)
240 247 {
241 248 Q_ASSERT(slice);
242 249 QColor c = slice->brush().color().lighter();
243 250 slice->setBrush(c);
244 251 }
245 252
246 253 void QPieSeries::highlightOff(QPieSlice* slice)
247 254 {
248 255 Q_ASSERT(slice);
249 256 QColor c = slice->brush().color().darker(150);
250 257 slice->setBrush(c);
251 258 }
252 259
253 260 void QPieSeries::updateDerivativeData()
254 261 {
255 262 m_total = 0;
256 263
257 264 // nothing to do?
258 265 if (m_slices.count() == 0)
259 266 return;
260 267
261 268 // calculate total
262 269 foreach (QPieSlice* s, m_slices)
263 270 m_total += s->value();
264 271
265 272 // we must have some values
266 Q_ASSERT(m_total > 0); // TODO
273 Q_ASSERT(m_total > 0); // TODO: is this the correct way to handle this?
267 274
268 275 // update slice attributes
269 276 qreal sliceAngle = m_pieStartAngle;
270 277 foreach (QPieSlice* s, m_slices) {
271 278
272 279 bool changed = false;
273 280
274 281 qreal percentage = s->value() / m_total;
275 282 if (s->m_percentage != percentage) {
276 283 s->m_percentage = percentage;
277 284 changed = true;
278 285 }
279 286
280 287 qreal sliceSpan = m_pieSpan * percentage;
281 if (s->m_span != sliceSpan) {
282 s->m_span = sliceSpan;
288 if (s->m_angleSpan != sliceSpan) {
289 s->m_angleSpan = sliceSpan;
283 290 changed = true;
284 291 }
285 292
286 293 if (s->m_angle != sliceAngle) {
287 294 s->m_angle = sliceAngle;
288 295 changed = true;
289 296 }
290 297 sliceAngle += sliceSpan;
291 298
292 299 if (changed)
293 300 emit s->changed();
294 301 }
295 302 }
296 303
297 304 #include "moc_qpieseries.cpp"
298 305
299 306 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,127 +1,131
1 1 #ifndef PIESERIES_H
2 2 #define PIESERIES_H
3 3
4 4 #include "qchartseries.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 QChartSeries
19 19 {
20 20 Q_OBJECT
21 21
22 22 public:
23 23 enum PiePosition {
24 24 PiePositionMaximized = 0,
25 25 PiePositionTopLeft,
26 26 PiePositionTopRight,
27 27 PiePositionBottomLeft,
28 28 PiePositionBottomRight
29 29 };
30 30
31 31 class ChangeSet
32 32 {
33 33 public:
34 34 void appendAdded(QPieSlice* slice);
35 void appendAdded(QList<QPieSlice*> slices);
35 36 void appendChanged(QPieSlice* slice);
36 37 void appendRemoved(QPieSlice* slice);
37 38
38 39 QList<QPieSlice*> added() const;
39 40 QList<QPieSlice*> changed() const;
40 41 QList<QPieSlice*> removed() const;
41 42
42 43 bool isEmpty() const;
43 44
44 45 private:
45 46 QList<QPieSlice*> m_added;
46 47 QList<QPieSlice*> m_changed;
47 48 QList<QPieSlice*> m_removed;
48 49 };
49 50
50 51 public:
51 52 QPieSeries(QObject *parent = 0);
52 53 virtual ~QPieSeries();
53 54
54 55 public: // from QChartSeries
55 56 QChartSeriesType type() const { return QChartSeries::SeriesTypePie; }
56 57 virtual bool setData(QList<qreal> data); // TODO: remove this
57 58
58 59 public:
59 60 void set(QList<QPieSlice*> slices);
60 61 void add(QList<QPieSlice*> slices);
61 62 void add(QPieSlice* slice);
62 63 QPieSlice* add(qreal value, QString name);
63 64 void remove(QPieSlice* slice);
64 65 void clear();
65 66
66 67 int count() const { return m_slices.count(); }
67 68
68 69 QList<QPieSlice*> slices() const { return m_slices; }
69 70
70 // TODO: find slices?
71 // QList<QPieSlice*> findByValue(qreal value);
72 // ...
73
74 // TODO: sorting slices?
75 // void sort(QPieSeries::SortByValue)
76
77 71 void setSizeFactor(qreal sizeFactor);
78 72 qreal sizeFactor() const { return m_sizeFactor; }
79 73
80 74 void setPosition(PiePosition position);
81 75 PiePosition position() const { return m_position; }
82 76
83 77 void setSpan(qreal startAngle, qreal span);
84 78
85 79 void setLabelsVisible(bool visible);
86 80 void enableClickExplodes(bool enable);
87 81 void enableHoverHighlight(bool enable);
88 82
83 // TODO: find slices?
84 // QList<QPieSlice*> findByValue(qreal value);
85 // ...
86
87 // TODO: sorting slices?
88 // void sort(QPieSeries::SortByValue|label|??)
89
90 // TODO: general graphics customization
91 // setDrawStyle(2d|3d)
92 // setDropShadows(bool)
93
89 94 Q_SIGNALS:
90 95 void changed(const QPieSeries::ChangeSet& changeSet);
91 96 void clicked(QPieSlice* slice);
92 97 void hoverEnter(QPieSlice* slice);
93 98 void hoverLeave(QPieSlice* slice);
94 99 void sizeFactorChanged();
95 100 void positionChanged();
96 101
97 private Q_SLOTS: // should be private and not in the interface
102 private Q_SLOTS: // TODO: should be private and not visible in the interface at all
98 103 void sliceChanged();
99 104 void sliceClicked();
100 105 void sliceHoverEnter();
101 106 void sliceHoverLeave();
102
103 107 void toggleExploded(QPieSlice* slice);
104 108 void highlightOn(QPieSlice* slice);
105 109 void highlightOff(QPieSlice* slice);
106 110
107 111 private:
108 112 void updateDerivativeData();
109 113
110 114 private:
111 115 Q_DISABLE_COPY(QPieSeries)
112 116
113 117 // TODO: use PIML
114 118 friend class PiePresenter;
115 119 friend class PieSlice;
116 120
117 121 QList<QPieSlice*> m_slices;
118 122 qreal m_sizeFactor;
119 123 PiePosition m_position;
120 124 qreal m_total;
121 125 qreal m_pieStartAngle;
122 126 qreal m_pieSpan;
123 127 };
124 128
125 129 QTCOMMERCIALCHART_END_NAMESPACE
126 130
127 131 #endif // PIESERIES_H
@@ -1,181 +1,197
1 1 #include "qpieslice.h"
2 2
3 3 QTCOMMERCIALCHART_BEGIN_NAMESPACE
4 4
5 #define DEFAULT_PEN_COLOR Qt::black
6 #define DEFAULT_BRUSH_COLOR Qt::white
7 #define DEFAULT_LABEL_ARM_LENGTH 50
5 #define DEFAULT_PEN_COLOR Qt::black
6 #define DEFAULT_BRUSH_COLOR Qt::white
7 #define DEFAULT_LABEL_ARM_LENGTH 50
8 #define DEFAULT_EXPOLODE_DISTANCE 20
8 9
9 10 QPieSlice::QPieSlice(QObject *parent)
10 11 :QObject(parent),
11 12 m_value(0),
12 13 m_isLabelVisible(true),
13 14 m_isExploded(false),
15 m_explodeDistance(DEFAULT_EXPOLODE_DISTANCE),
14 16 m_percentage(0),
15 17 m_angle(0),
16 m_span(0),
18 m_angleSpan(0),
17 19 m_pen(DEFAULT_PEN_COLOR),
18 20 m_brush(DEFAULT_BRUSH_COLOR),
19 21 m_labelPen(DEFAULT_PEN_COLOR),
20 22 m_labelArmLength(DEFAULT_LABEL_ARM_LENGTH)
21 23 {
22 24
23 25 }
24 26
25 27 QPieSlice::QPieSlice(qreal value, QString label, bool labelVisible, QObject *parent)
26 28 :QObject(parent),
27 29 m_value(value),
28 30 m_label(label),
29 31 m_isLabelVisible(labelVisible),
30 32 m_isExploded(false),
33 m_explodeDistance(DEFAULT_EXPOLODE_DISTANCE),
31 34 m_percentage(0),
32 35 m_angle(0),
33 m_span(0),
36 m_angleSpan(0),
34 37 m_pen(DEFAULT_PEN_COLOR),
35 38 m_brush(DEFAULT_BRUSH_COLOR),
36 39 m_labelPen(DEFAULT_PEN_COLOR),
37 40 m_labelArmLength(DEFAULT_LABEL_ARM_LENGTH)
38 41 {
39 42
40 43 }
41 44
42 45 QPieSlice::~QPieSlice()
43 46 {
44 47
45 48 }
46 49
47 50 qreal QPieSlice::value() const
48 51 {
49 52 return m_value;
50 53 }
51 54
52 55 QString QPieSlice::label() const
53 56 {
54 57 return m_label;
55 58 }
56 59
57 60 bool QPieSlice::isLabelVisible() const
58 61 {
59 62 return m_isLabelVisible;
60 63 }
61 64
62 65 bool QPieSlice::isExploded() const
63 66 {
64 67 return m_isExploded;
65 68 }
66 69
70 qreal QPieSlice::explodeDistance() const
71 {
72 return m_explodeDistance;
73 }
74
67 75 qreal QPieSlice::percentage() const
68 76 {
69 77 return m_percentage;
70 78 }
71 79
72 80 qreal QPieSlice::angle() const
73 81 {
74 82 return m_angle;
75 83 }
76 84
77 qreal QPieSlice::span() const
85 qreal QPieSlice::angleSpan() const
78 86 {
79 return m_span;
87 return m_angleSpan;
80 88 }
81 89
82 90 QPen QPieSlice::pen() const
83 91 {
84 92 return m_pen;
85 93 }
86 94
87 95 QBrush QPieSlice::brush() const
88 96 {
89 97 return m_brush;
90 98 }
91 99
92 100 QPen QPieSlice::labelPen() const
93 101 {
94 102 return m_labelPen;
95 103 }
96 104
97 105 QFont QPieSlice::labelFont() const
98 106 {
99 107 return m_labelFont;
100 108 }
101 109
102 qreal QPieSlice::labelArmLenght() const
110 qreal QPieSlice::labelArmLength() const
103 111 {
104 112 return m_labelArmLength;
105 113 }
106 114
107 115 void QPieSlice::setValue(qreal value)
108 116 {
109 117 if (m_value != value) {
110 118 m_value = value;
111 119 emit changed();
112 120 }
113 121 }
114 122
115 123 void QPieSlice::setLabel(QString label)
116 124 {
117 125 if (m_label != label) {
118 126 m_label = label;
119 127 emit changed();
120 128 }
121 129 }
122 130
123 131 void QPieSlice::setLabelVisible(bool visible)
124 132 {
125 133 if (m_isLabelVisible != visible) {
126 134 m_isLabelVisible = visible;
127 135 emit changed();
128 136 }
129 137 }
130 138
131 139 void QPieSlice::setExploded(bool exploded)
132 140 {
133 141 if (m_isExploded != exploded) {
134 142 m_isExploded = exploded;
135 143 emit changed();
136 144 }
137 145 }
138 146
147 void QPieSlice::setExplodeDistance(qreal distance)
148 {
149 if (m_explodeDistance != distance) {
150 m_explodeDistance = distance;
151 emit changed();
152 }
153 }
154
139 155 void QPieSlice::setPen(QPen pen)
140 156 {
141 157 if (m_pen != pen) {
142 158 m_pen = pen;
143 159 emit changed();
144 160 }
145 161 }
146 162
147 163 void QPieSlice::setBrush(QBrush brush)
148 164 {
149 165 if (m_brush != brush) {
150 166 m_brush = brush;
151 167 emit changed();
152 168 }
153 169 }
154 170
155 171 void QPieSlice::setLabelFont(QFont font)
156 172 {
157 173 if (m_labelFont != font) {
158 174 m_labelFont = font;
159 175 emit changed();
160 176 }
161 177 }
162 178
163 179 void QPieSlice::setLabelPen(QPen pen)
164 180 {
165 181 if (m_labelPen != pen) {
166 182 m_labelPen = pen;
167 183 emit changed();
168 184 }
169 185 }
170 186
171 187 void QPieSlice::setLabelArmLength(qreal len)
172 188 {
173 189 if (m_labelArmLength != len) {
174 190 m_labelArmLength = len;
175 191 emit changed();
176 192 }
177 193 }
178 194
179 195 #include "moc_qpieslice.cpp"
180 196
181 197 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,88 +1,96
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 dataYChanged*/)
16 16 Q_PROPERTY(qreal value READ value WRITE setValue /*NOTIFY dataXChanged*/)
17 17
18 18 public:
19 19 QPieSlice(QObject *parent = 0);
20 20 QPieSlice(qreal value, QString label, bool labelVisible = true, QObject *parent = 0);
21 21 virtual ~QPieSlice();
22 22
23 23 // data
24 24 qreal value() const;
25 25 QString label() const;
26 26 bool isLabelVisible() const;
27 27 bool isExploded() const;
28 qreal explodeDistance() const;
28 29
29 30 // generated data
30 31 qreal percentage() const;
31 32 qreal angle() const;
32 qreal span() const;
33 qreal angleSpan() const;
33 34
34 35 // customization
35 36 QPen pen() const;
36 37 QBrush brush() const;
37 38 QPen labelPen() const;
38 39 QFont labelFont() const;
39 qreal labelArmLenght() const;
40 qreal labelArmLength() const;
40 41
41 42 Q_SIGNALS:
42 43 void clicked();
43 44 void hoverEnter();
44 45 void hoverLeave();
45 46 void changed();
46 47
47 48 public Q_SLOTS:
48 49
49 50 // data
50 51 void setLabel(QString label);
51 52 void setLabelVisible(bool visible);
52 53 void setValue(qreal value);
53 54 void setExploded(bool exploded);
55 void setExplodeDistance(qreal distance);
54 56
55 57 // customization
56 58 void setPen(QPen pen);
57 59 void setBrush(QBrush brush);
58 60 void setLabelFont(QFont font);
59 61 void setLabelPen(QPen pen);
60 62 void setLabelArmLength(qreal len);
61 63
64 // TODO: label position in general
65 // setLabelFlags(inside|outside|labelArmOn|labelArmOff|???)
66 // setLabelOrientation(horizontal|vertical|same as slice center angle|???)
67
62 68 private:
63 69
64 70 // TODO: use private class
65 71 friend class QPieSeries;
72 friend class PiePresenter;
66 73
67 74 // data
68 75 qreal m_value;
69 76 QString m_label;
70 77 bool m_isLabelVisible;
71 78 bool m_isExploded;
79 qreal m_explodeDistance;
72 80
73 81 // generated data
74 82 qreal m_percentage;
75 83 qreal m_angle;
76 qreal m_span;
84 qreal m_angleSpan;
77 85
78 86 // customization
79 87 QPen m_pen;
80 88 QBrush m_brush;
81 89 QPen m_labelPen;
82 90 QFont m_labelFont;
83 91 qreal m_labelArmLength;
84 92 };
85 93
86 94 QTCOMMERCIALCHART_END_NAMESPACE
87 95
88 96 #endif // QPIESLICE_H
General Comments 0
You need to be logged in to leave comments. Login now