##// END OF EJS Templates
Changed BoxPlot to use domain for calculating geometry points....
Mika Salmela -
r2554:93cea193b2c2
parent child
Show More
@@ -1,210 +1,203
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "boxplotchartitem_p.h"
22 22 #include "qboxplotseries_p.h"
23 23 #include "bar_p.h"
24 24 #include "qboxset_p.h"
25 25 #include "qabstractbarseries_p.h"
26 26 #include "qboxset.h"
27 27 #include "boxwhiskers_p.h"
28 28 #include <QPainter>
29 29
30 30 QTCOMMERCIALCHART_BEGIN_NAMESPACE
31 31
32 32 BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item) :
33 33 ChartItem(series->d_func(), item),
34 34 m_series(series),
35 35 m_animation(0),
36 m_animate(0),
37 m_domainMaxY(0.0),
38 m_domainMinY(0.0)
36 m_animate(0)
39 37 {
40 38 connect(series, SIGNAL(boxsetsRemoved(QList<QBoxSet *>)), this, SLOT(handleBoxsetRemove(QList<QBoxSet *>)));
41 39 connect(series->d_func(), SIGNAL(restructuredBoxes()), this, SLOT(handleDataStructureChanged()));
42 40 connect(series->d_func(), SIGNAL(updatedLayout()), this, SLOT(handleLayoutChanged()));
43 41 connect(series->d_func(), SIGNAL(updatedBoxes()), this, SLOT(handleUpdatedBars()));
44 42 connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdatedBars()));
45 43 // QBoxPlotSeriesPrivate calls handleDataStructureChanged(), don't do it here
46 44 setZValue(ChartPresenter::BoxPlotSeriesZValue);
47 45 }
48 46
49 47 BoxPlotChartItem::~BoxPlotChartItem()
50 48 {
51 49 }
52 50
53 51 void BoxPlotChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
54 52 {
55 53 Q_UNUSED(painter);
56 54 Q_UNUSED(option);
57 55 Q_UNUSED(widget);
58 56 }
59 57
60 58 void BoxPlotChartItem::setAnimation(BoxPlotAnimation *animation)
61 59 {
62 60 m_animation = animation;
63 61 if (m_animation) {
64 62 foreach (BoxWhiskers *item, m_boxTable.values())
65 63 m_animation->addBox(item);
66 64 handleDomainUpdated();
67 65 }
68 66 }
69 67
70 68 void BoxPlotChartItem::handleDataStructureChanged()
71 69 {
72 70 int setCount = m_series->count();
73 71
74 72 for (int s = 0; s < setCount; s++) {
75 73 QBoxSet *set = m_series->d_func()->boxSetAt(s);
76 74
77 75 BoxWhiskers *box = m_boxTable.value(set);
78 76 if (!box) {
79 77 // Item is not yet created, make a box and add it to hash table
80 78 box = new BoxWhiskers(set, domain(), this);
81 79 m_boxTable.insert(set, box);
82 80 connect(box, SIGNAL(clicked(QBoxSet *)), m_series, SIGNAL(clicked(QBoxSet *)));
83 81 connect(box, SIGNAL(hovered(bool, QBoxSet *)), m_series, SIGNAL(hovered(bool, QBoxSet *)));
84 82 connect(box, SIGNAL(clicked(QBoxSet *)), set, SIGNAL(clicked()));
85 83 connect(box, SIGNAL(hovered(bool, QBoxSet *)), set, SIGNAL(hovered(bool)));
86 84
87 85 // Set the decorative issues for the newly created box
88 86 box->setBrush(m_series->brush());
89 87 box->setPen(m_series->pen());
90 88 }
91 89 updateBoxGeometry(box, s);
92 90
93 box->updateGeometry();
91 box->updateGeometry(domain());
94 92
95 93 if (m_animation)
96 94 m_animation->addBox(box);
97 95 }
98 96
99 97 handleDomainUpdated();
100 98 }
101 99
102 100 void BoxPlotChartItem::handleUpdatedBars()
103 101 {
104 102 foreach (BoxWhiskers *item, m_boxTable.values()) {
105 103 item->setBrush(m_series->brush());
106 104 item->setPen(m_series->pen());
107 105 }
108 106 // Override with QBoxSet specific settings
109 107 foreach (QBoxSet *set, m_boxTable.keys()) {
110 108 if (set->brush().style() != Qt::NoBrush)
111 109 m_boxTable.value(set)->setBrush(set->brush());
112 110 if (set->pen().style() != Qt::NoPen)
113 111 m_boxTable.value(set)->setPen(set->pen());
114 112 }
115 113 }
116 114
117 115 void BoxPlotChartItem::handleBoxsetRemove(QList<QBoxSet*> barSets)
118 116 {
119 117 foreach (QBoxSet *set, barSets) {
120 118 BoxWhiskers *boxItem = m_boxTable.value(set);
121 119 m_boxTable.remove(set);
122 120 delete boxItem;
123 121 }
124 122 }
125 123
126 124 void BoxPlotChartItem::handleDomainUpdated()
127 125 {
128 126 if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0))
129 127 return;
130 128
131 // Set my bounding rect to same as domain size
132 m_boundingRect.setRect(0.0, 0.0, domain()->size().width(), domain()->size().height());
129 // Set my bounding rect to same as domain size. Add one pixel at the top (-1.0) and the bottom as 0.0 would
130 // snip a bit off from the whisker at the grid line
131 m_boundingRect.setRect(0.0, -1.0, domain()->size().width(), domain()->size().height() + 1.0);
133 132
134 133 foreach (BoxWhiskers *item, m_boxTable.values()) {
135 // Update the domain size for each BoxWhisker item
136 item->setDomainSize(domain()->size());
137 if (domain()->minY() != m_domainMinY || domain()->maxY() != m_domainMaxY) {
138 item->updateGeometry();
139 m_domainMinY = domain()->minY();
140 m_domainMaxY = domain()->maxY();
141 }
134 item->updateGeometry(domain());
142 135
143 136 // If the animation is set, start the animation for each BoxWhisker item
144 137 if (m_animation)
145 138 presenter()->startAnimation(m_animation->boxAnimation(item));
146 139 }
147 140 }
148 141
149 142 void BoxPlotChartItem::handleLayoutChanged()
150 143 {
151 144 foreach (BoxWhiskers *item, m_boxTable.values()) {
152 145 if (m_animation)
153 146 m_animation->setAnimationStart(item);
154 147
155 148 bool dirty = updateBoxGeometry(item, item->m_data.m_index);
156 149 if (dirty && m_animation)
157 150 presenter()->startAnimation(m_animation->boxChangeAnimation(item));
158 151 else
159 item->updateGeometry();
152 item->updateGeometry(domain());
160 153 }
161 154 }
162 155
163 156 QRectF BoxPlotChartItem::boundingRect() const
164 157 {
165 158 return m_boundingRect;
166 159 }
167 160
168 161 void BoxPlotChartItem::initializeLayout()
169 162 {
170 163 }
171 164
172 165 QVector<QRectF> BoxPlotChartItem::calculateLayout()
173 166 {
174 167 return QVector<QRectF>();
175 168 }
176 169
177 170 bool BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index)
178 171 {
179 172 bool changed = false;
180 173
181 174 QBoxSet *set = m_series->d_func()->boxSetAt(index);
182 175 BoxWhiskersData &data = box->m_data;
183 176
184 177 if ((data.m_lowerExtreme != set->at(0)) || (data.m_lowerQuartile != set->at(1)) ||
185 178 (data.m_median != set->at(2)) || (data.m_upperQuartile != set->at(3)) || (data.m_upperExtreme != set->at(4))) {
186 179 changed = true;
187 180 }
188 181
189 182 data.m_lowerExtreme = set->at(0);
190 183 data.m_lowerQuartile = set->at(1);
191 184 data.m_median = set->at(2);
192 185 data.m_upperQuartile = set->at(3);
193 186 data.m_upperExtreme = set->at(4);
194 187 data.m_index = index;
195 188 data.m_boxItems = m_series->count();
196 189
197 190 data.m_maxX = domain()->maxX();
198 191 data.m_minX = domain()->minX();
199 192 data.m_maxY = domain()->maxY();
200 193 data.m_minY = domain()->minY();
201 194
202 195 data.m_seriesIndex = m_seriesIndex;
203 196 data.m_seriesCount = m_seriesCount;
204 197
205 198 return changed;
206 199 }
207 200
208 201 #include "moc_boxplotchartitem_p.cpp"
209 202
210 203 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,87 +1,85
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 // W A R N I N G
22 22 // -------------
23 23 //
24 24 // This file is not part of the QtCommercial Chart API. It exists purely as an
25 25 // implementation detail. This header file may change from version to
26 26 // version without notice, or even be removed.
27 27 //
28 28 // We mean it.
29 29
30 30
31 31 #ifndef BOXPLOTCHARTITEM_H
32 32 #define BOXPLOTCHARTITEM_H
33 33
34 34 #include "boxwhiskers_p.h"
35 35 #include "qboxplotseries.h"
36 36 #include "chartitem_p.h"
37 37 #include "boxplotanimation_p.h"
38 38 #include "qboxset.h"
39 39 #include <QGraphicsItem>
40 40
41 41 QTCOMMERCIALCHART_BEGIN_NAMESPACE
42 42
43 43 class BoxPlotSeriesPrivate;
44 44
45 45 class BoxPlotChartItem : public ChartItem
46 46 {
47 47 Q_OBJECT
48 48 public:
49 49 BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item = 0);
50 50 ~BoxPlotChartItem();
51 51
52 52 void setAnimation(BoxPlotAnimation *animation);
53 53
54 54 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
55 55 QRectF boundingRect() const;
56 56
57 57 public Q_SLOTS:
58 58 void handleDataStructureChanged();
59 59 void handleDomainUpdated();
60 60 void handleLayoutChanged();
61 61 void handleUpdatedBars();
62 62 void handleBoxsetRemove(QList<QBoxSet *> barSets);
63 63
64 64 private:
65 65 virtual QVector<QRectF> calculateLayout();
66 66 void initializeLayout();
67 67 bool updateBoxGeometry(BoxWhiskers *box, int index);
68 68
69 69 protected:
70 70 friend class QBoxPlotSeriesPrivate;
71 71 QBoxPlotSeries *m_series; // Not owned.
72 72 QList<BoxWhiskers *> m_boxes;
73 73 QHash<QBoxSet *, BoxWhiskers *> m_boxTable;
74 74 int m_seriesIndex;
75 75 int m_seriesCount;
76 76
77 77 BoxPlotAnimation *m_animation;
78 78 bool m_animate;
79 qreal m_domainMaxY;
80 qreal m_domainMinY;
81 79
82 80 QRectF m_boundingRect;
83 81 };
84 82
85 83 QTCOMMERCIALCHART_END_NAMESPACE
86 84
87 85 #endif // BOXPLOTCHARTITEM_H
@@ -1,167 +1,171
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "boxwhiskers_p.h"
22 22 #include <QPainter>
23 23 #include <QWidget>
24 24
25 25 QTCOMMERCIALCHART_BEGIN_NAMESPACE
26 26
27 27 BoxWhiskers::BoxWhiskers(QBoxSet *set, AbstractDomain *domain, QGraphicsObject *parent) :
28 28 QGraphicsObject(parent),
29 29 m_boxSet(set),
30 30 m_domain(domain)
31 31 {
32 32 setAcceptHoverEvents(true);
33 33 setAcceptedMouseButtons(Qt::MouseButtonMask);
34 34 }
35 35
36 36 BoxWhiskers::~BoxWhiskers()
37 37 {
38 38 }
39 39
40 40 void BoxWhiskers::mousePressEvent(QGraphicsSceneMouseEvent *event)
41 41 {
42 42 Q_UNUSED(event)
43 43 emit clicked(m_boxSet);
44 44 }
45 45
46 46 void BoxWhiskers::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
47 47 {
48 48 Q_UNUSED(event)
49 49 emit hovered(true, m_boxSet);
50 50 }
51 51
52 52 void BoxWhiskers::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
53 53 {
54 54 Q_UNUSED(event)
55 55 emit hovered(false, m_boxSet);
56 56 }
57 57
58 58 void BoxWhiskers::setBrush(const QBrush &brush)
59 59 {
60 60 m_brush = brush;
61 61 update();
62 62 }
63 63
64 64 void BoxWhiskers::setPen(const QPen &pen)
65 65 {
66 66 m_pen = pen;
67 67 update();
68 68 }
69 69
70 70 void BoxWhiskers::setLayout(const BoxWhiskersData &data)
71 71 {
72 72 m_data = data;
73 73
74 updateGeometry();
74 updateGeometry(m_domain);
75 75 update();
76 76 }
77 77
78
79 78 QSizeF BoxWhiskers::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
80 79 {
81 80 Q_UNUSED(which)
82 81 Q_UNUSED(constraint)
83 82
84 83 return QSizeF();
85 84 }
86 85
87 86 void BoxWhiskers::setGeometry(const QRectF &rect)
88 87 {
89 88 Q_UNUSED(rect)
90 89 }
91 90
92 void BoxWhiskers::setDomainSize(const QSizeF &size)
93 {
94 m_domainSize = size;
95
96 updateBoundingRect();
97 }
98
99 91 QRectF BoxWhiskers::boundingRect() const
100 92 {
101 93 return m_boundingRect;
102 94 }
103 95
104 96 void BoxWhiskers::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
105 97 {
106 98 Q_UNUSED(option)
107 99 Q_UNUSED(widget)
108 100
109 101 painter->setPen(m_pen);
110 102 painter->setBrush(m_brush);
111 103 painter->setClipRect(parentItem()->boundingRect());
112 painter->scale(m_domainSize.width() / m_data.m_boxItems, m_domainSize.height() / m_domain->spanY());
113 104 painter->drawPath(m_boxPath);
114 105 }
115 106
116 void BoxWhiskers::updateGeometry()
107 void BoxWhiskers::updateGeometry(AbstractDomain *domain)
117 108 {
109 m_domain = domain;
110
118 111 prepareGeometryChange();
119 112
120 113 QPainterPath path;
114 m_boxPath = path;
115 m_boundingRect = m_boxPath.boundingRect();
121 116
122 117 qreal columnWidth = 1.0 / m_data.m_seriesCount;
123 qreal left = 0.25 * columnWidth + columnWidth * m_data.m_seriesIndex;
124 qreal right = 0.75 * columnWidth + columnWidth * m_data.m_seriesIndex;
125 qreal middle = 0.5 * columnWidth + columnWidth * m_data.m_seriesIndex;
126 qreal domainMaxY = m_domain->maxY();
118 qreal left = 0.25 * columnWidth + columnWidth * m_data.m_seriesIndex + m_data.m_index - 0.5;
119 qreal barWidth = columnWidth / 2.0;
120
121 QPointF geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_upperExtreme), m_validData);
122 if (!m_validData)
123 return;
124 qreal geometryLeft = geometryPoint.x();
125 qreal geometryUpperExtreme = geometryPoint.y();
126 geometryPoint = m_domain->calculateGeometryPoint(QPointF(left + barWidth, m_data.m_upperQuartile), m_validData);
127 if (!m_validData)
128 return;
129 qreal geometryRight = geometryPoint.x();
130 qreal geometryUpperQuartile = geometryPoint.y();
131 geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_lowerQuartile), m_validData);
132 if (!m_validData)
133 return;
134 qreal geometryLowerQuartile = geometryPoint.y();
135 geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_lowerExtreme), m_validData);
136 if (!m_validData)
137 return;
138 qreal geometryLowerExtreme = geometryPoint.y();
139 geometryPoint = m_domain->calculateGeometryPoint(QPointF(left, m_data.m_median), m_validData);
140 if (!m_validData)
141 return;
142 qreal geometryMedian = geometryPoint.y();
127 143
128 144 // Upper whisker
129 path.moveTo(left + m_data.m_index, domainMaxY - m_data.m_upperExtreme);
130 path.lineTo(right + m_data.m_index, domainMaxY - m_data.m_upperExtreme);
131 path.moveTo(middle + m_data.m_index, domainMaxY - m_data.m_upperExtreme);
132 path.lineTo(middle + m_data.m_index, domainMaxY - m_data.m_upperQuartile);
145 path.moveTo(geometryLeft, geometryUpperExtreme);
146 path.lineTo(geometryRight, geometryUpperExtreme);
147 path.moveTo((geometryLeft + geometryRight) / 2.0, geometryUpperExtreme);
148 path.lineTo((geometryLeft + geometryRight) / 2.0, geometryUpperQuartile);
133 149
134 150 // Middle Box
135 path.addRect(left + m_data.m_index, domainMaxY - m_data.m_upperQuartile,
136 0.5 * columnWidth, m_data.m_upperQuartile - m_data.m_lowerQuartile);
151 path.addRect(geometryLeft, geometryUpperQuartile, geometryRight - geometryLeft, geometryLowerQuartile - geometryUpperQuartile);
137 152
138 // Median/mean line
139 path.moveTo(left + m_data.m_index, domainMaxY - m_data.m_median);
140 path.lineTo(right + m_data.m_index, domainMaxY - m_data.m_median);
153 // Median line
154 path.moveTo(geometryLeft, geometryMedian);
155 path.lineTo(geometryRight, geometryMedian);
141 156
142 157 // Lower whisker
143 path.moveTo(left + m_data.m_index, domainMaxY - m_data.m_lowerExtreme);
144 path.lineTo(right + m_data.m_index, domainMaxY - m_data.m_lowerExtreme);
145 path.moveTo(middle + m_data.m_index, domainMaxY - m_data.m_lowerExtreme);
146 path.lineTo(middle + m_data.m_index, domainMaxY - m_data.m_lowerQuartile);
158 path.moveTo(geometryLeft, geometryLowerExtreme);
159 path.lineTo(geometryRight, geometryLowerExtreme);
160 path.moveTo((geometryLeft + geometryRight) / 2.0, geometryLowerQuartile);
161 path.lineTo((geometryLeft + geometryRight) / 2.0, geometryLowerExtreme);
147 162
148 163 path.closeSubpath();
149 164
150 165 m_boxPath = path;
151
152 updateBoundingRect();
153 }
154
155 void BoxWhiskers::updateBoundingRect()
156 {
157 qreal scaleY = m_domainSize.height() / (m_domain->maxY() - m_domain->minY());
158 qreal scaleX = m_domainSize.width() / m_data.m_boxItems;
159 QRectF br = m_boxPath.boundingRect();
160
161 m_boundingRect = QRectF(br.x() * scaleX, br.y() * scaleY,
162 br.width() * scaleX, br.height() * scaleY);
166 m_boundingRect = m_boxPath.boundingRect();
163 167 }
164 168
165 169 #include "moc_boxwhiskers_p.cpp"
166 170
167 171 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,96 +1,92
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 // W A R N I N G
22 22 // -------------
23 23 //
24 24 // This file is not part of the QtCommercial Chart API. It exists purely as an
25 25 // implementation detail. This header file may change from version to
26 26 // version without notice, or even be removed.
27 27 //
28 28 // We mean it.
29 29
30 30 #ifndef BOXWHISKERS_H
31 31 #define BOXWHISKERS_H
32 32
33 33 #include "boxwhiskersdata_p.h"
34 34 #include "qchartglobal.h"
35 35 #include "abstractdomain_p.h"
36 36 #include <QBoxSet>
37 37 #include <QGraphicsRectItem>
38 38 #include <QGraphicsLineItem>
39 39 #include <QGraphicsLayoutItem>
40 40 #include <QPainterPath>
41 41
42 42 QTCOMMERCIALCHART_BEGIN_NAMESPACE
43 43
44 44 class QBarSet;
45 45
46 46 class BoxWhiskers : public QGraphicsObject
47 47 {
48 48 Q_OBJECT
49 49
50 50 public:
51 51 BoxWhiskers(QBoxSet *set, AbstractDomain *domain, QGraphicsObject *parent);
52 52 ~BoxWhiskers();
53 53
54 54 void setBrush(const QBrush &brush);
55 55 void setPen(const QPen &pen);
56 56 void setLayout(const BoxWhiskersData &data);
57 void setDomainSize(const QSizeF &size);
58 57
59 58 void mousePressEvent(QGraphicsSceneMouseEvent *event);
60 59 void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
61 60 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
62 61
63 62 QRectF boundingRect() const;
64 63 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
65 64
66 void updateGeometry();
65 void updateGeometry(AbstractDomain *domain);
67 66 protected:
68 67 QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const;
69 68 void setGeometry(const QRectF &rect);
70 69
71 private:
72 void updateBoundingRect();
73
74 70 Q_SIGNALS:
75 71 void clicked(QBoxSet *boxset);
76 72 void hovered(bool status, QBoxSet *boxset);
77 73
78 74 private:
79 75 friend class BoxPlotChartItem;
80 76 friend class BoxPlotAnimation;
81 77
82 78 QBoxSet *m_boxSet;
83 79 AbstractDomain *m_domain;
84 80 QPainterPath m_boxPath;
85 81 QRectF m_boundingRect;
86 82 bool m_hovering;
87 83 bool m_validData;
88 84 QBrush m_brush;
89 85 QPen m_pen;
90 86 BoxWhiskersData m_data;
91 87 QSizeF m_domainSize;
92 88 };
93 89
94 90 QTCOMMERCIALCHART_END_NAMESPACE
95 91
96 92 #endif // BOXWHISKERS_H
@@ -1,680 +1,680
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "qboxplotseries.h"
22 22 #include "qboxplotseries_p.h"
23 23 #include "qboxplotlegendmarker.h"
24 24 #include "qbarcategoryaxis.h"
25 25 #include "boxplotchartitem_p.h"
26 26 #include "chartdataset_p.h"
27 27 #include "charttheme_p.h"
28 28 #include "qvalueaxis.h"
29 29 #include "charttheme_p.h"
30 30 #include "boxplotanimation_p.h"
31 31 #include "qchart_p.h"
32 32 #include "qboxset.h"
33 33 #include "qboxset_p.h"
34 34
35 35 QTCOMMERCIALCHART_BEGIN_NAMESPACE
36 36
37 37 /*!
38 38 \class QBoxPlotSeries
39 39 \brief Series for creating box-and-whiskers chart
40 40 \mainclass
41 41
42 42 QBoxPlotSeries represents a series of data shown as box-and-whisker bars. The purpose of this class is to act as
43 43 a container for single box-and-whisker items. Each item is drawn to own slot. If chart includes multiple instances of
44 44 QBoxPlotSeries then box-and-whiskers items with the same index are drawn to same slot.
45 45
46 46 See the \l {Box and Whiskers Example} {box-and-whiskers chart example} to learn how to create a box-and-whiskers chart.
47 47 \image examples_boxplotchart.png
48 48
49 49 \sa QBoxSet
50 50 */
51 51
52 52 /*!
53 53 \qmlclass BoxPlotSeries QBoxPlotSeries
54 54 \inherits QAbstractSeries
55 55
56 56 BoxPlotSeries represents a series of data shown as box-and-whisker bars. The purpose of this class is to act as
57 57 a container for single box-and-whisker items. Each item is drawn to own slot. If chart includes multiple instances of
58 58 BoxPlotSeries then box-and-whiskers items with the same index are drawn to same slot.
59 59
60 60 The following QML shows how to create a simple box-and-whiskers chart:
61 61 \code
62 62 import QtQuick 1.0
63 63 import QtCommercial.Chart 1.1
64 64
65 65 ChartView {
66 66 title: "Box Plot series"
67 67 width: 400
68 68 height: 300
69 69 theme: ChartView.ChartThemeBrownSand
70 70 legend.alignment: Qt.AlignBottom
71 71
72 72 BoxPlotSeries {
73 73 id: plotSeries
74 74 name: "Income"
75 75 BoxSet { label: "Jan"; values: [3, 4, 5.1, 6.2, 8.5] }
76 76 BoxSet { label: "Feb"; values: [5, 6, 7.5, 8.6, 11.8] }
77 77 BoxSet { label: "Mar"; values: [3.2, 5, 5.7, 8, 9.2] }
78 78 BoxSet { label: "Apr"; values: [3.8, 5, 6.4, 7, 8] }
79 79 BoxSet { label: "May"; values: [4, 5, 5.2, 6, 7] }
80 80 }
81 81 }
82 82 \endcode
83 83
84 84 \beginfloatleft
85 85 \image examples_qmlboxplot.png
86 86 \endfloat
87 87 \clearfloat
88 88 */
89 89
90 90 /*!
91 91 \fn QBoxPlotSeries::boxsetsAdded(QList<QBoxSet *> sets)
92 92 \brief Signal is emitted when a new \a sets of box-and-whiskers data is added to the series.
93 93 */
94 94
95 95 /*!
96 96 \fn QBoxPlotSeries::boxsetsRemoved(QList<QBoxSet *> sets)
97 97 \brief Signal is emitted when \a sets of box-and-whiskers data is removed from the series.
98 98 */
99 99
100 100 /*!
101 101 \fn QBoxPlotSeries::clicked(QBoxSet *boxset)
102 102 \brief Signal is emitted when the user clicks the \a boxset on the chart.
103 103 */
104 104
105 105 /*!
106 106 \fn QBoxPlotSeries::hovered(bool status, QBoxSet *boxset)
107 107 \brief Signal is emitted when there is change in hover \a status over \a boxset.
108 108 */
109 109
110 110 /*!
111 111 \fn QBoxPlotSeries::countChanged()
112 112 \brief Signal is emitted when there is change in count of box-and-whiskers items in the series.
113 113 */
114 114 /*!
115 115 \fn virtual SeriesType QBoxPlotSeries::type() const
116 116 \brief Returns type of series.
117 117 \sa QAbstractSeries, SeriesType
118 118 */
119 119 /*!
120 120 \qmlmethod BoxPlotSeries::append(const QString label, QVariantList values)
121 121 Appends a new box-and-whiskers set with \a label and \a values to the series.
122 122 */
123 123 /*!
124 124 \qmlmethod BoxPlotSeries::append(BoxSet *box)
125 125 Appends the \a box to the series.
126 126 */
127 127 /*!
128 128 \qmlmethod BoxPlotSeries::insert(int index, const QString label, QVariantList values)
129 129 Inserts a new box-and-whiskers set with \a label and \a values at the \a index position.
130 130 */
131 131 /*!
132 132 \qmlmethod BoxPlotSeries::remove(QBoxSet *boxset)
133 133 Removes the \a boxset from the series.
134 134 */
135 135 /*!
136 136 \qmlmethod BoxPlotSeries::clear()
137 137 Removes all boxsets from the series. Deletes removed sets.
138 138 */
139 139
140 140 /*!
141 141 \qmlsignal BoxPlotSeries::onClicked(BoxSet boxset);
142 142 Signal is emitted when the user clicks the \a boxset on the chart.
143 143 */
144 144 /*!
145 145 \qmlsignal BoxPlotSeries::onHovered(bool status, BoxSet boxset);
146 146 Signal is emitted when there is change in hover \a status over \a boxset.
147 147 */
148 148 /*!
149 149 \qmlsignal BoxPlotSeries::onCountChanged();
150 150 Signal is emitted when there is change in count of box-and-whiskers items in the series.
151 151 */
152 152 /*!
153 153 \qmlsignal BoxPlotSeries::onBoxsetsAdded()
154 154 Signal is emitted when new box-and-whiskers sets are added to the series.
155 155 */
156 156 /*!
157 157 \qmlsignal BoxPlotSeries::boxsetsRemoved()
158 158 Signal is emitted when new box-and-whiskers sets are removed from the series.
159 159 */
160 160
161 161 /*!
162 162 Constructs empty QBoxPlotSeries.
163 163 QBoxPlotSeries is QObject which is a child of a \a parent.
164 164 */
165 165 QBoxPlotSeries::QBoxPlotSeries(QObject *parent)
166 166 : QAbstractSeries(*new QBoxPlotSeriesPrivate(this), parent)
167 167 {
168 168 }
169 169
170 170 /*!
171 171 Destructor. Removes series from chart.
172 172 */
173 173 QBoxPlotSeries::~QBoxPlotSeries()
174 174 {
175 175 Q_D(QBoxPlotSeries);
176 176 if (d->m_chart)
177 177 d->m_chart->removeSeries(this);
178 178 }
179 179
180 180 /*!
181 181 Adds a single box and whiskers set to series. Takes ownership of the \a set. If the set is null or is already in series, it won't be appended.
182 182 Returns true, if appending succeeded.
183 183 */
184 184 bool QBoxPlotSeries::append(QBoxSet *set)
185 185 {
186 186 Q_D(QBoxPlotSeries);
187 187
188 188 bool success = d->append(set);
189 189 if (success) {
190 190 QList<QBoxSet *> sets;
191 191 sets.append(set);
192 192 set->setParent(this);
193 193 emit boxsetsAdded(sets);
194 194 emit countChanged();
195 195 }
196 196 return success;
197 197 }
198 198
199 199 /*!
200 200 Removes boxset from the series. Deletes the \a set and returns true if successful.
201 201 */
202 202 bool QBoxPlotSeries::remove(QBoxSet *set)
203 203 {
204 204 Q_D(QBoxPlotSeries);
205 205 bool success = d->remove(set);
206 206 if (success) {
207 207 QList<QBoxSet *> sets;
208 208 sets.append(set);
209 209 set->setParent(0);
210 210 emit boxsetsRemoved(sets);
211 211 emit countChanged();
212 212 delete set;
213 213 set = 0;
214 214 }
215 215 return success;
216 216 }
217 217
218 218 /*!
219 219 Takes a single \a set from the series. Does not delete the boxset object.
220 220
221 221 NOTE: The series remains as the boxset's parent object. You must set the
222 222 parent object to take full ownership.
223 223
224 224 Returns true if take was successful.
225 225 */
226 226 bool QBoxPlotSeries::take(QBoxSet *set)
227 227 {
228 228 Q_D(QBoxPlotSeries);
229 229
230 230 bool success = d->remove(set);
231 231 if (success) {
232 232 QList<QBoxSet *> sets;
233 233 sets.append(set);
234 234 emit boxsetsRemoved(sets);
235 235 emit countChanged();
236 236 }
237 237 return success;
238 238 }
239 239
240 240 /*!
241 241 Adds a list of boxsets to series. Takes ownership of the \a sets.
242 242 Returns true, if all sets were appended successfully. If any of the sets is null or is already appended to series,
243 243 nothing is appended and function returns false. If any of the sets is in list more than once, nothing is appended
244 244 and function returns false.
245 245 */
246 246 bool QBoxPlotSeries::append(QList<QBoxSet *> sets)
247 247 {
248 248 Q_D(QBoxPlotSeries);
249 249 bool success = d->append(sets);
250 250 if (success) {
251 251 emit boxsetsAdded(sets);
252 252 emit countChanged();
253 253 }
254 254 return success;
255 255 }
256 256
257 257 /*!
258 258 Insert a box-and-whiskers set to the series at \a index postion. Takes ownership of the \a set. If the set is null or
259 259 is already in series, it won't be appended. Returns true, if inserting succeeded.
260 260
261 261 */
262 262 bool QBoxPlotSeries::insert(int index, QBoxSet *set)
263 263 {
264 264 Q_D(QBoxPlotSeries);
265 265 bool success = d->insert(index, set);
266 266 if (success) {
267 267 QList<QBoxSet *> sets;
268 268 sets.append(set);
269 269 emit boxsetsAdded(sets);
270 270 emit countChanged();
271 271 }
272 272 return success;
273 273 }
274 274
275 275 /*!
276 276 Removes all boxsets from the series. Deletes removed sets.
277 277 */
278 278 void QBoxPlotSeries::clear()
279 279 {
280 280 Q_D(QBoxPlotSeries);
281 281 QList<QBoxSet *> sets = boxSets();
282 282 bool success = d->remove(sets);
283 283 if (success) {
284 284 emit boxsetsRemoved(sets);
285 285 emit countChanged();
286 286 foreach (QBoxSet *set, sets)
287 287 delete set;
288 288 }
289 289 }
290 290
291 291 /*!
292 292 Returns number of sets in series.
293 293 */
294 294 int QBoxPlotSeries::count() const
295 295 {
296 296 Q_D(const QBoxPlotSeries);
297 297 return d->m_boxSets.count();
298 298 }
299 299
300 300 /*!
301 301 Returns a list of sets in series. Keeps ownership of sets.
302 302 */
303 303 QList<QBoxSet *> QBoxPlotSeries::boxSets() const
304 304 {
305 305 Q_D(const QBoxPlotSeries);
306 306 return d->m_boxSets;
307 307 }
308 308
309 309 /*
310 310 Returns QAbstractSeries::SeriesTypeBoxPlot.
311 311 */
312 312 QAbstractSeries::SeriesType QBoxPlotSeries::type() const
313 313 {
314 314 return QAbstractSeries::SeriesTypeBoxPlot;
315 315 }
316 316
317 317 /*!
318 318 Sets brush for the series. Box-and-whiskers items are drawn using \a brush
319 319 */
320 320 void QBoxPlotSeries::setBrush(const QBrush &brush)
321 321 {
322 322 Q_D(QBoxPlotSeries);
323 323
324 324 if (d->m_brush != brush) {
325 325 d->m_brush = brush;
326 326 emit d->updated();
327 327 }
328 328 }
329 329
330 330 /*!
331 331 Returns brush of the series.
332 332 */
333 333 QBrush QBoxPlotSeries::brush() const
334 334 {
335 335 Q_D(const QBoxPlotSeries);
336 336
337 337 return d->m_brush;
338 338 }
339 339
340 340 /*!
341 341 Sets pen for the series. Box-and-whiskers items are drawn using \a pen
342 342 */
343 343 void QBoxPlotSeries::setPen(const QPen &pen)
344 344 {
345 345 Q_D(QBoxPlotSeries);
346 346
347 347 if (d->m_pen != pen) {
348 348 d->m_pen = pen;
349 349 emit d->updated();
350 350 }
351 351 }
352 352
353 353 /*!
354 354 Returns the pen of this series.
355 355 */
356 356 QPen QBoxPlotSeries::pen() const
357 357 {
358 358 Q_D(const QBoxPlotSeries);
359 359
360 360 return d->m_pen;
361 361 }
362 362
363 363 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
364 364
365 365 QBoxPlotSeriesPrivate::QBoxPlotSeriesPrivate(QBoxPlotSeries *q)
366 366 : QAbstractSeriesPrivate(q),
367 367 m_pen(QChartPrivate::defaultPen()),
368 368 m_brush(QChartPrivate::defaultBrush())
369 369 {
370 370 }
371 371
372 372 QBoxPlotSeriesPrivate::~QBoxPlotSeriesPrivate()
373 373 {
374 374 disconnect(this, 0, 0, 0);
375 375 }
376 376
377 377 void QBoxPlotSeriesPrivate::initializeDomain()
378 378 {
379 379 qreal minX(domain()->minX());
380 380 qreal minY(domain()->minY());
381 381 qreal maxX(domain()->maxX());
382 382 qreal maxY(domain()->maxY());
383 383
384 384 qreal x = m_boxSets.count();
385 minX = qMin(minX, - (qreal)0.5);
385 minX = qMin(minX, qreal(-0.5));
386 386 minY = qMin(minY, min());
387 maxX = qMax(maxX, x - (qreal)0.5);
387 maxX = qMax(maxX, x - qreal(0.5));
388 388 maxY = qMax(maxY, max());
389 389
390 390 domain()->setRange(minX, maxX, minY, maxY);
391 391 }
392 392
393 393 void QBoxPlotSeriesPrivate::initializeAxes()
394 394 {
395 395 foreach (QAbstractAxis* axis, m_axes) {
396 396 if (axis->type() == QAbstractAxis::AxisTypeBarCategory) {
397 397 if (axis->orientation() == Qt::Horizontal)
398 398 populateCategories(qobject_cast<QBarCategoryAxis *>(axis));
399 399 }
400 400 }
401 401 }
402 402
403 403 QAbstractAxis::AxisType QBoxPlotSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
404 404 {
405 405 if (orientation == Qt::Horizontal)
406 406 return QAbstractAxis::AxisTypeBarCategory;
407 407
408 408 return QAbstractAxis::AxisTypeValue;
409 409 }
410 410
411 411 QAbstractAxis* QBoxPlotSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
412 412 {
413 413 Q_UNUSED(orientation);
414 414
415 415 return 0;
416 416 }
417 417
418 418 void QBoxPlotSeriesPrivate::populateCategories(QBarCategoryAxis *axis)
419 419 {
420 420 QStringList categories;
421 421 if (axis->categories().isEmpty()) {
422 422 for (int i(1); i < m_boxSets.count() + 1; i++) {
423 423 QBoxSet *set = m_boxSets.at(i - 1);
424 424 if (set->label().isEmpty())
425 425 categories << QString::number(i);
426 426 else
427 427 categories << set->label();
428 428 }
429 429 axis->append(categories);
430 430 }
431 431 }
432 432
433 433 void QBoxPlotSeriesPrivate::initializeGraphics(QGraphicsItem *parent)
434 434 {
435 435 Q_Q(QBoxPlotSeries);
436 436
437 437 BoxPlotChartItem *boxPlot = new BoxPlotChartItem(q, parent);
438 438 m_item.reset(boxPlot);
439 439 QAbstractSeriesPrivate::initializeGraphics(parent);
440 440
441 441 if (m_chart) {
442 442 connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), this, SLOT(handleSeriesChange(QAbstractSeries*)) );
443 443 connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), this, SLOT(handleSeriesRemove(QAbstractSeries*)) );
444 444
445 445 QList<QAbstractSeries *> serieses = m_chart->series();
446 446
447 447 // Tries to find this series from the Chart's list of series and deduce the index
448 448 int index = 0;
449 449 foreach (QAbstractSeries *s, serieses) {
450 450 if (s->type() == QAbstractSeries::SeriesTypeBoxPlot) {
451 451 if (q == static_cast<QBoxPlotSeries *>(s)) {
452 452 boxPlot->m_seriesIndex = index;
453 453 m_index = index;
454 454 }
455 455 index++;
456 456 }
457 457 }
458 458 boxPlot->m_seriesCount = index;
459 459 }
460 460
461 461 // Make BoxPlotChartItem to instantiate box & whisker items
462 462 boxPlot->handleDataStructureChanged();
463 463 }
464 464
465 465 void QBoxPlotSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced)
466 466 {
467 467 Q_Q(QBoxPlotSeries);
468 468
469 469 const QList<QGradient> gradients = theme->seriesGradients();
470 470
471 471 if (forced || QChartPrivate::defaultBrush() == m_brush) {
472 472 QColor brushColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.5);
473 473 q->setBrush(brushColor);
474 474 }
475 475
476 476 if (forced || QChartPrivate::defaultPen() == m_pen) {
477 477 QPen pen = theme->outlinePen();
478 478 pen.setCosmetic(true);
479 479 q->setPen(pen);
480 480 }
481 481 }
482 482
483 483 void QBoxPlotSeriesPrivate::initializeAnimations(QChart::AnimationOptions options)
484 484 {
485 485 BoxPlotChartItem *item = static_cast<BoxPlotChartItem *>(m_item.data());
486 486 Q_ASSERT(item);
487 487 if (options.testFlag(QChart::SeriesAnimations))
488 488 item->setAnimation(new BoxPlotAnimation(item));
489 489 else
490 490 item->setAnimation((BoxPlotAnimation *)0);
491 491 QAbstractSeriesPrivate::initializeAnimations(options);
492 492 }
493 493
494 494 QList<QLegendMarker*> QBoxPlotSeriesPrivate::createLegendMarkers(QLegend *legend)
495 495 {
496 496 Q_Q(QBoxPlotSeries);
497 497 QList<QLegendMarker *> list;
498 498 return list << new QBoxPlotLegendMarker(q, legend);
499 499 }
500 500
501 501 void QBoxPlotSeriesPrivate::handleSeriesRemove(QAbstractSeries *series)
502 502 {
503 503 Q_Q(QBoxPlotSeries);
504 504
505 505 QBoxPlotSeries *removedSeries = static_cast<QBoxPlotSeries *>(series);
506 506 QObject::disconnect(m_chart->d_ptr->m_dataset, 0, removedSeries->d_func(), 0);
507 507
508 508 // Test if series removed is me, then don't do anything
509 509 if (q != removedSeries) {
510 510 BoxPlotChartItem *item = static_cast<BoxPlotChartItem *>(m_item.data());
511 511 if (item) {
512 512 item->m_seriesCount = item->m_seriesCount - 1;
513 513 if (removedSeries->d_func()->m_index < m_index) {
514 514 m_index--;
515 515 item->m_seriesIndex = m_index;
516 516 }
517 517
518 518 item->handleDataStructureChanged();
519 519 }
520 520 }
521 521 }
522 522
523 523 void QBoxPlotSeriesPrivate::handleSeriesChange(QAbstractSeries *series)
524 524 {
525 525 Q_UNUSED(series);
526 526
527 527 Q_Q(QBoxPlotSeries);
528 528
529 529 BoxPlotChartItem *boxPlot = static_cast<BoxPlotChartItem *>(m_item.data());
530 530
531 531 if (m_chart) {
532 532 QList<QAbstractSeries *> serieses = m_chart->series();
533 533
534 534 // Tries to find this series from the Chart's list of series and deduce the index
535 535 int index = 0;
536 536 foreach (QAbstractSeries *s, serieses) {
537 537 if (s->type() == QAbstractSeries::SeriesTypeBoxPlot) {
538 538 if (q == static_cast<QBoxPlotSeries *>(s)) {
539 539 boxPlot->m_seriesIndex = index;
540 540 m_index = index;
541 541 }
542 542 index++;
543 543 }
544 544 }
545 545 boxPlot->m_seriesCount = index;
546 546 }
547 547
548 548 boxPlot->handleDataStructureChanged();
549 549 }
550 550
551 551 bool QBoxPlotSeriesPrivate::append(QBoxSet *set)
552 552 {
553 553 if ((m_boxSets.contains(set)) || (set == 0))
554 554 return false; // Fail if set is already in list or set is null.
555 555
556 556 m_boxSets.append(set);
557 557 QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
558 558 QObject::connect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
559 559 QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
560 560
561 561 emit restructuredBoxes(); // this notifies boxplotchartitem
562 562 return true;
563 563 }
564 564
565 565 bool QBoxPlotSeriesPrivate::remove(QBoxSet *set)
566 566 {
567 567 if (!m_boxSets.contains(set))
568 568 return false; // Fail if set is not in list
569 569
570 570 m_boxSets.removeOne(set);
571 571 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
572 572 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
573 573 QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
574 574
575 575 emit restructuredBoxes(); // this notifies boxplotchartitem
576 576 return true;
577 577 }
578 578
579 579 bool QBoxPlotSeriesPrivate::append(QList<QBoxSet *> sets)
580 580 {
581 581 foreach (QBoxSet *set, sets) {
582 582 if ((set == 0) || (m_boxSets.contains(set)))
583 583 return false; // Fail if any of the sets is null or is already appended.
584 584 if (sets.count(set) != 1)
585 585 return false; // Also fail if same set is more than once in given list.
586 586 }
587 587
588 588 foreach (QBoxSet *set, sets) {
589 589 m_boxSets.append(set);
590 590 QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
591 591 QObject::connect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
592 592 QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
593 593 }
594 594
595 595 emit restructuredBoxes(); // this notifies boxplotchartitem
596 596 return true;
597 597 }
598 598
599 599 bool QBoxPlotSeriesPrivate::remove(QList<QBoxSet *> sets)
600 600 {
601 601 if (sets.count() == 0)
602 602 return false;
603 603
604 604 foreach (QBoxSet *set, sets) {
605 605 if ((set == 0) || (!m_boxSets.contains(set)))
606 606 return false; // Fail if any of the sets is null or is not in series
607 607 if (sets.count(set) != 1)
608 608 return false; // Also fail if same set is more than once in given list.
609 609 }
610 610
611 611 foreach (QBoxSet *set, sets) {
612 612 m_boxSets.removeOne(set);
613 613 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
614 614 QObject::disconnect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
615 615 QObject::disconnect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
616 616 }
617 617
618 618 emit restructuredBoxes(); // this notifies boxplotchartitem
619 619
620 620 return true;
621 621 }
622 622
623 623 bool QBoxPlotSeriesPrivate::insert(int index, QBoxSet *set)
624 624 {
625 625 if ((m_boxSets.contains(set)) || (set == 0))
626 626 return false; // Fail if set is already in list or set is null.
627 627
628 628 m_boxSets.insert(index, set);
629 629 QObject::connect(set->d_ptr.data(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout()));
630 630 QObject::connect(set->d_ptr.data(), SIGNAL(updatedBox()), this, SIGNAL(updatedBoxes()));
631 631 QObject::connect(set->d_ptr.data(), SIGNAL(restructuredBox()), this, SIGNAL(restructuredBoxes()));
632 632
633 633 emit restructuredBoxes(); // this notifies boxplotchartitem
634 634 return true;
635 635 }
636 636
637 637 QBoxSet *QBoxPlotSeriesPrivate::boxSetAt(int index)
638 638 {
639 639 return m_boxSets.at(index);
640 640 }
641 641
642 642 qreal QBoxPlotSeriesPrivate::min()
643 643 {
644 644 if (m_boxSets.count() <= 0)
645 645 return 0;
646 646
647 647 qreal min = m_boxSets.at(0)->at(0);
648 648
649 649 foreach (QBoxSet *set, m_boxSets) {
650 650 for (int i = 0; i < 5; i++) {
651 651 if (set->at(i) < min)
652 652 min = set->at(i);
653 653 }
654 654 }
655 655
656 656 return min;
657 657 }
658 658
659 659 qreal QBoxPlotSeriesPrivate::max()
660 660 {
661 661 if (m_boxSets.count() <= 0)
662 662 return 0;
663 663
664 664 qreal max = m_boxSets.at(0)->at(0);
665 665
666 666 foreach (QBoxSet *set, m_boxSets) {
667 667 for (int i = 0; i < 5; i++) {
668 668 if (set->at(i) > max)
669 669 max = set->at(i);
670 670 }
671 671 }
672 672
673 673 return max;
674 674 }
675 675
676 676 #include "moc_qboxplotseries.cpp"
677 677 #include "moc_qboxplotseries_p.cpp"
678 678
679 679 QTCOMMERCIALCHART_END_NAMESPACE
680 680
@@ -1,416 +1,419
1 1 /****************************************************************************
2 2 **
3 3 ** Copyright (C) 2013 Digia Plc
4 4 ** All rights reserved.
5 5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 6 **
7 7 ** This file is part of the Qt Commercial Charts Add-on.
8 8 **
9 9 ** $QT_BEGIN_LICENSE$
10 10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 11 ** accordance with the Qt Commercial License Agreement provided with the
12 12 ** Software or, alternatively, in accordance with the terms contained in
13 13 ** a written agreement between you and Digia.
14 14 **
15 15 ** If you have questions regarding the use of this file, please use
16 16 ** contact form at http://qt.digia.com
17 17 ** $QT_END_LICENSE$
18 18 **
19 19 ****************************************************************************/
20 20
21 21 #include "mainwidget.h"
22 22 #include "customtablemodel.h"
23 23 #include <QVBoxPlotModelMapper>
24 24 #include <QTableView>
25 25 #include <QHeaderView>
26 26 #include <QChartView>
27 27 #include <QBoxPlotSeries>
28 28 #include <QBoxSet>
29 29 #include <QLegend>
30 30 #include <QBarCategoryAxis>
31 31 #include <QBrush>
32 32 #include <QColor>
33 33 #include <QPushButton>
34 34 #include <QComboBox>
35 35 #include <QSpinBox>
36 36 #include <QCheckBox>
37 37 #include <QGridLayout>
38 38 #include <QHBoxLayout>
39 39 #include <QLabel>
40 40 #include <QSpacerItem>
41 41 #include <QMessageBox>
42 42 #include <cmath>
43 43 #include <QDebug>
44 44 #include <QStandardItemModel>
45 45 #include <QBarCategoryAxis>
46
46 #include <QLogValueAxis>
47 47
48 48 QTCOMMERCIALCHART_USE_NAMESPACE
49 49
50 50 QString addCategories[] = {"Jul", "Aug", "Sep", "Nov", "Dec"};
51 static const int maxAddCategories = 5;
51 52
52 53 MainWidget::MainWidget(QWidget *parent) :
53 54 QWidget(parent),
54 55 m_chart(0),
56 m_axis(0),
55 57 rowPos(0),
56 58 nSeries(0),
57 59 nNewBoxes(0)
58 60 {
59 61 m_chart = new QChart();
60 62
61 63 // Grid layout for the controls for configuring the chart widget
62 64 QGridLayout *grid = new QGridLayout();
63 65
64 66 // Create add a series button
65 67 QPushButton *addSeriesButton = new QPushButton("Add a series");
66 68 connect(addSeriesButton, SIGNAL(clicked()), this, SLOT(addSeries()));
67 69 grid->addWidget(addSeriesButton, rowPos++, 1);
68 70
69 71 // Create remove a series button
70 72 QPushButton *removeSeriesButton = new QPushButton("Remove a series");
71 73 connect(removeSeriesButton, SIGNAL(clicked()), this, SLOT(removeSeries()));
72 74 grid->addWidget(removeSeriesButton, rowPos++, 1);
73 75
74 76 // Create add a single box button
75 77 QPushButton *addBoxButton = new QPushButton("Add a box");
76 78 connect(addBoxButton, SIGNAL(clicked()), this, SLOT(addBox()));
77 79 grid->addWidget(addBoxButton, rowPos++, 1);
78 80
79 81 // Create insert a box button
80 82 QPushButton *insertBoxButton = new QPushButton("Insert a box");
81 83 connect(insertBoxButton, SIGNAL(clicked()), this, SLOT(insertBox()));
82 84 grid->addWidget(insertBoxButton, rowPos++, 1);
83 85
84 86 // Create add a single box button
85 87 QPushButton *removeBoxButton = new QPushButton("Remove a box");
86 88 connect(removeBoxButton, SIGNAL(clicked()), this, SLOT(removeBox()));
87 89 grid->addWidget(removeBoxButton, rowPos++, 1);
88 90
89 91 // Create clear button
90 92 QPushButton *clearButton = new QPushButton("Clear");
91 93 connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
92 94 grid->addWidget(clearButton, rowPos++, 1);
93 95
94 96 // Create clear button
95 97 QPushButton *clearBoxButton = new QPushButton("ClearBox");
96 98 connect(clearBoxButton, SIGNAL(clicked()), this, SLOT(clearBox()));
97 99 grid->addWidget(clearBoxButton, rowPos++, 1);
98 100
99 101
100 102 // Create set brush button
101 103 QPushButton *setBrushButton = new QPushButton("Set brush");
102 104 connect(setBrushButton, SIGNAL(clicked()), this, SLOT(setBrush()));
103 105 grid->addWidget(setBrushButton, rowPos++, 1);
104 106
105 107 initThemeCombo(grid);
106 108 initCheckboxes(grid);
107 109
108 110 m_model = new CustomTableModel;
109 111 QTableView *tableView = new QTableView;
110 112 tableView->setModel(m_model);
111 113 tableView->setMaximumWidth(200);
112 114 grid->addWidget(tableView, rowPos++, 0, 3, 2, Qt::AlignLeft);
113 115 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
114 116 tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
115 117 tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
116 118 #else
117 119 tableView->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
118 120 tableView->verticalHeader()->setResizeMode(QHeaderView::Stretch);
119 121 #endif
120 122
121 123 // add row with empty label to make all the other rows static
122 124 grid->addWidget(new QLabel(""), grid->rowCount(), 0);
123 125 grid->setRowStretch(grid->rowCount() - 1, 1);
124 126
125 127 // Create chart view with the chart
126 128 m_chartView = new QChartView(m_chart, this);
127 //m_chartView->setRubberBand(QChartView::HorizonalRubberBand);
128 129
129 130 // Another grid layout as a main layout
130 131 QGridLayout *mainLayout = new QGridLayout();
131 132 mainLayout->addLayout(grid, 0, 0);
132 133 mainLayout->addWidget(m_chartView, 0, 1, 3, 1);
133 134 setLayout(mainLayout);
134 135
135 136 legendToggled(false);
136 137 animationToggled(false);
137 138 }
138 139
139 140 // Combo box for selecting theme
140 141 void MainWidget::initThemeCombo(QGridLayout *grid)
141 142 {
142 143 QComboBox *chartTheme = new QComboBox();
143 144 chartTheme->addItem("Default");
144 145 chartTheme->addItem("Light");
145 146 chartTheme->addItem("Blue Cerulean");
146 147 chartTheme->addItem("Dark");
147 148 chartTheme->addItem("Brown Sand");
148 149 chartTheme->addItem("Blue NCS");
149 150 chartTheme->addItem("High Contrast");
150 151 chartTheme->addItem("Blue Icy");
151 152 connect(chartTheme, SIGNAL(currentIndexChanged(int)),
152 153 this, SLOT(changeChartTheme(int)));
153 154 grid->addWidget(new QLabel("Chart theme:"), rowPos, 0);
154 155 grid->addWidget(chartTheme, rowPos++, 1);
155 156 }
156 157
157 158 // Different check boxes for customizing chart
158 159 void MainWidget::initCheckboxes(QGridLayout *grid)
159 160 {
160 161 QCheckBox *animationCheckBox = new QCheckBox("Animation");
161 162 connect(animationCheckBox, SIGNAL(toggled(bool)), this, SLOT(animationToggled(bool)));
162 163 animationCheckBox->setChecked(false);
163 164 grid->addWidget(animationCheckBox, rowPos++, 0);
164 165
165 166 QCheckBox *legendCheckBox = new QCheckBox("Legend");
166 167 connect(legendCheckBox, SIGNAL(toggled(bool)), this, SLOT(legendToggled(bool)));
167 168 legendCheckBox->setChecked(false);
168 169 grid->addWidget(legendCheckBox, rowPos++, 0);
169 170
170 171 QCheckBox *titleCheckBox = new QCheckBox("Title");
171 172 connect(titleCheckBox, SIGNAL(toggled(bool)), this, SLOT(titleToggled(bool)));
172 173 titleCheckBox->setChecked(false);
173 174 grid->addWidget(titleCheckBox, rowPos++, 0);
174 175
175 176 QCheckBox *modelMapperCheckBox = new QCheckBox("Use model mapper");
176 177 connect(modelMapperCheckBox, SIGNAL(toggled(bool)), this, SLOT(modelMapperToggled(bool)));
177 178 modelMapperCheckBox->setChecked(false);
178 179 grid->addWidget(modelMapperCheckBox, rowPos++, 0);
179 180
180 181 }
181 182
182 183 void MainWidget::addSeries()
183 184 {
184 185 qDebug() << "BoxPlotTester::MainWidget::addSeries()";
185 186
186 187 if (nSeries > 9)
187 188 return;
188 189
189 190 // Initial data
190 //![1]
191 191 QBoxSet *set0 = new QBoxSet();
192 192 QBoxSet *set1 = new QBoxSet();
193 193 QBoxSet *set2 = new QBoxSet();
194 194 QBoxSet *set3 = new QBoxSet();
195 195 QBoxSet *set4 = new QBoxSet();
196 196 QBoxSet *set5 = new QBoxSet();
197 197
198 198 // low bot med top upp
199 *set0 << 1 << 2 << 4.4 << 13 << 15;
199 *set0 << -1 << 2 << 4 << 13 << 15;
200 200 *set1 << 5 << 6 << 7.5 << 8 << 12;
201 201 *set2 << 3 << 5 << 5.7 << 8 << 9;
202 202 *set3 << 5 << 6 << 6.8 << 7 << 8;
203 203 *set4 << 4 << 5 << 5.2 << 6 << 7;
204 204 *set5 << 4 << 7 << 8.2 << 9 << 10;
205 205
206 206 m_series[nSeries] = new QBoxPlotSeries();
207 207 m_series[nSeries]->append(set0);
208 208 m_series[nSeries]->append(set1);
209 209 m_series[nSeries]->append(set2);
210 210 m_series[nSeries]->append(set3);
211 211 m_series[nSeries]->append(set4);
212 212 m_series[nSeries]->append(set5);
213 213 m_series[nSeries]->setName("Box & Whiskers");
214 214
215 215 connect(m_series[nSeries], SIGNAL(clicked(QBoxSet*)), this, SLOT(boxClicked(QBoxSet*)));
216 216 connect(m_series[nSeries], SIGNAL(hovered(bool, QBoxSet*)), this, SLOT(boxHovered(bool, QBoxSet*)));
217 217 connect(set1, SIGNAL(clicked()), this, SLOT(singleBoxClicked()));
218 218 connect(set2, SIGNAL(hovered(bool)), this, SLOT(singleBoxHovered(bool)));
219 219
220 220 m_chart->addSeries(m_series[nSeries]);
221 221
222 if (nSeries == 0) {
222 if (!m_axis) {
223 223 QStringList categories;
224 224 categories << "Jan" << "Feb" << "Mar" << "Apr" << "May" << "Jun";
225 225 m_axis = new QBarCategoryAxis();
226 226 m_axis->append(categories);
227 227 m_chart->createDefaultAxes();
228 m_chart->setAxisX(m_axis, m_series[nSeries]);
229 228 }
229 m_chart->setAxisX(m_axis, m_series[nSeries]);
230 230
231 231 nSeries++;
232 232 }
233 233
234 234 void MainWidget::removeSeries()
235 235 {
236 236 qDebug() << "BoxPlotTester::MainWidget::removeSeries()";
237 237
238 238 if (nSeries > 0) {
239 239 nSeries--;
240 240 m_chart->removeSeries(m_series[nSeries]);
241 241 delete m_series[nSeries];
242 242 } else {
243 243 qDebug() << "Create a series first";
244 244 }
245 245 }
246 246
247 247 void MainWidget::addBox()
248 248 {
249 249 qDebug() << "BoxPlotTester::MainWidget::addBox()";
250 250
251 if (nSeries > 0) {
251 if (nSeries > 0 && nNewBoxes < maxAddCategories) {
252 252 QBoxSet *newSet = new QBoxSet();
253 253 newSet->setValue(QBoxSet::LowerExtreme, 5.0);
254 254 newSet->setValue(QBoxSet::LowerQuartile, 6.0);
255 255 newSet->setValue(QBoxSet::Median, 6.8);
256 256 newSet->setValue(QBoxSet::UpperQuartile, 7.0);
257 257 newSet->setValue(QBoxSet::UpperExtreme, 8.0);
258 258
259 for (int i = 0; i < nSeries; i++)
260 m_series[i]->append(newSet);
259 m_series[0]->append(newSet);
261 260
262 261 m_axis->append(addCategories[nNewBoxes]);
263 262
264 263 nNewBoxes++;
265 264 }
266 265 }
267 266
268 267 void MainWidget::insertBox()
269 268 {
270 269 qDebug() << "BoxPlotTester::MainWidget::insertBox()";
271 270
272 271 if (nSeries > 0) {
273 272 QBoxSet *newSet = new QBoxSet();
274 273 *newSet << 2 << 6 << 6.8 << 7 << 10;
275 274
276 275 for (int i = 0; i < nSeries; i++)
277 276 m_series[i]->insert(1, newSet);
278 277
279 278 m_axis->append(addCategories[nNewBoxes]);
280 279
281 280 nNewBoxes++;
282 281 }
283 282 }
284 283
285 284 void MainWidget::removeBox()
286 285 {
287 286 qDebug() << "BoxPlotTester::MainWidget::removeBox";
288 287
289 288 if (nSeries > 0) {
290 289 for (int i = 0; i < nSeries; i++) {
291 QList<QBoxSet *> sets = m_series[i]->boxSets();
292 m_series[i]->remove(sets.at(m_series[i]->count() - 3));
290 qDebug() << "m_series[i]->count() = " << m_series[i]->count();
291 if (m_series[i]->count() > 3) {
292 QList<QBoxSet *> sets = m_series[i]->boxSets();
293 m_series[i]->remove(sets.at(m_series[i]->count() - 3));
294 }
293 295 }
294 296
295 m_axis->remove(m_axis->at(1));
297 if (m_axis->count() > 3)
298 m_axis->remove(m_axis->at(1));
296 299 } else {
297 300 qDebug() << "Create a series first";
298 301 }
299 302 }
300 303
301 304 void MainWidget::clear()
302 305 {
303 306 qDebug() << "BoxPlotTester::MainWidget::clear";
304 307
305 308 if (nSeries > 0)
306 309 m_series[0]->clear();
307 310 else
308 311 qDebug() << "Create a series first";
309 312 }
310 313
311 314 void MainWidget::clearBox()
312 315 {
313 316 qDebug() << "BoxPlotTester::MainWidget::clearBox";
314 317
315 318 if (nSeries > 0) {
316 319 QList<QBoxSet *> sets = m_series[0]->boxSets();
317 320 sets.at(1)->clear();
318 321 } else {
319 322 qDebug() << "Create a series first";
320 323 }
321 324 }
322 325
323 326 void MainWidget::setBrush()
324 327 {
325 328 qDebug() << "BoxPlotTester::MainWidget::setBrush";
326 329
327 330 if (nSeries > 0) {
328 331 QList<QBoxSet *> sets = m_series[0]->boxSets();
329 332 sets.at(1)->setBrush(QBrush(QColor(Qt::yellow)));
330 333 } else {
331 334 qDebug() << "Create a series first";
332 335 }
333 336 }
334 337
335 338 void MainWidget::animationToggled(bool enabled)
336 339 {
337 340 qDebug() << "BoxPlotTester::Animation toggled to " << enabled;
338 341 if (enabled)
339 342 m_chart->setAnimationOptions(QChart::SeriesAnimations);
340 343 else
341 344 m_chart->setAnimationOptions(QChart::NoAnimation);
342 345 }
343 346
344 347 void MainWidget::legendToggled(bool enabled)
345 348 {
346 349 qDebug() << "BoxPlotTester::Legend toggled to " << enabled;
347 350 m_chart->legend()->setVisible(enabled);
348 351 if (enabled)
349 352 m_chart->legend()->setAlignment(Qt::AlignBottom);
350 353 }
351 354
352 355 void MainWidget::titleToggled(bool enabled)
353 356 {
354 357 qDebug() << "BoxPlotTester::Title toggled to " << enabled;
355 358 if (enabled)
356 359 m_chart->setTitle("Simple boxplotchart example");
357 360 else
358 361 m_chart->setTitle("");
359 362 }
360 363
361 364 void MainWidget::modelMapperToggled(bool enabled)
362 365 {
363 366 if (enabled) {
364 367 m_series[nSeries] = new QBoxPlotSeries();
365 368
366 369 int first = 0;
367 370 int count = 5;
368 371 QVBoxPlotModelMapper *mapper = new QVBoxPlotModelMapper(this);
369 372 mapper->setFirstBoxSetColumn(0);
370 373 mapper->setLastBoxSetColumn(5);
371 374 mapper->setFirstRow(first);
372 375 mapper->setRowCount(count);
373 376 mapper->setSeries(m_series[nSeries]);
374 377 mapper->setModel(m_model);
375 378 m_chart->addSeries(m_series[nSeries]);
376 379
377 380 nSeries++;
378 381 } else {
379 382 removeSeries();
380 383 }
381 384 }
382 385
383 386 void MainWidget::changeChartTheme(int themeIndex)
384 387 {
385 388 qDebug() << "BoxPlotTester::changeChartTheme: " << themeIndex;
386 389 if (themeIndex == 0)
387 390 m_chart->setTheme(QChart::ChartThemeLight);
388 391 else
389 392 m_chart->setTheme((QChart::ChartTheme) (themeIndex - 1));
390 393 }
391 394
392 395 void MainWidget::boxClicked(QBoxSet *set)
393 396 {
394 397 qDebug() << "boxClicked, median = " << set->at(QBoxSet::Median);
395 398 }
396 399
397 400 void MainWidget::boxHovered(bool state, QBoxSet *set)
398 401 {
399 402 if (state)
400 403 qDebug() << "box median " << set->at(QBoxSet::Median) << " hover started";
401 404 else
402 405 qDebug() << "box median " << set->at(QBoxSet::Median) << " hover ended";
403 406 }
404 407
405 408 void MainWidget::singleBoxClicked()
406 409 {
407 410 qDebug() << "singleBoxClicked";
408 411 }
409 412
410 413 void MainWidget::singleBoxHovered(bool state)
411 414 {
412 415 if (state)
413 416 qDebug() << "single box hover started";
414 417 else
415 418 qDebug() << "single box hover ended";
416 419 }
General Comments 0
You need to be logged in to leave comments. Login now