1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <private/boxplotchartitem_p.h>
31#include <private/qboxplotseries_p.h>
32#include <private/bar_p.h>
33#include <private/qboxset_p.h>
34#include <private/qabstractbarseries_p.h>
35#include <QtCharts/QBoxSet>
36#include <private/boxwhiskers_p.h>
37#include <QtGui/QPainter>
38
39QT_CHARTS_BEGIN_NAMESPACE
40
41BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item) :
42 ChartItem(series->d_func(), item),
43 m_series(series),
44 m_animation(0)
45{
46 setAcceptedMouseButtons({});
47 connect(sender: series, SIGNAL(boxsetsRemoved(QList<QBoxSet *>)), receiver: this, SLOT(handleBoxsetRemove(QList<QBoxSet *>)));
48 connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged()));
49 connect(sender: series, SIGNAL(opacityChanged()), receiver: this, SLOT(handleOpacityChanged()));
50 connect(sender: series->d_func(), SIGNAL(restructuredBoxes()), receiver: this, SLOT(handleDataStructureChanged()));
51 connect(sender: series->d_func(), SIGNAL(updatedLayout()), receiver: this, SLOT(handleLayoutChanged()));
52 connect(sender: series->d_func(), SIGNAL(updatedBoxes()), receiver: this, SLOT(handleUpdatedBars()));
53 connect(sender: series->d_func(), SIGNAL(updated()), receiver: this, SLOT(handleUpdatedBars()));
54 // QBoxPlotSeriesPrivate calls handleDataStructureChanged(), don't do it here
55 setZValue(ChartPresenter::BoxPlotSeriesZValue);
56}
57
58BoxPlotChartItem::~BoxPlotChartItem()
59{
60}
61
62void BoxPlotChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
63{
64 Q_UNUSED(painter);
65 Q_UNUSED(option);
66 Q_UNUSED(widget);
67}
68
69void BoxPlotChartItem::setAnimation(BoxPlotAnimation *animation)
70{
71 m_animation = animation;
72 if (m_animation) {
73 foreach (BoxWhiskers *item, m_boxTable.values())
74 m_animation->addBox(box: item);
75 handleDomainUpdated();
76 }
77}
78
79void BoxPlotChartItem::handleSeriesVisibleChanged()
80{
81 setVisible(m_series->isVisible());
82}
83
84void BoxPlotChartItem::handleOpacityChanged()
85{
86 setOpacity(m_series->opacity());
87}
88
89void BoxPlotChartItem::handleDataStructureChanged()
90{
91 int setCount = m_series->count();
92
93 for (int s = 0; s < setCount; s++) {
94 QBoxSet *set = m_series->d_func()->boxSetAt(index: s);
95
96 BoxWhiskers *box = m_boxTable.value(akey: set);
97 if (!box) {
98 // Item is not yet created, make a box and add it to hash table
99 box = new BoxWhiskers(set, domain(), this);
100 m_boxTable.insert(akey: set, avalue: box);
101 connect(sender: box, SIGNAL(clicked(QBoxSet *)), receiver: m_series, SIGNAL(clicked(QBoxSet *)));
102 connect(sender: box, SIGNAL(hovered(bool, QBoxSet *)), receiver: m_series, SIGNAL(hovered(bool, QBoxSet *)));
103 connect(sender: box, SIGNAL(pressed(QBoxSet *)), receiver: m_series, SIGNAL(pressed(QBoxSet *)));
104 connect(sender: box, SIGNAL(released(QBoxSet *)), receiver: m_series, SIGNAL(released(QBoxSet *)));
105 connect(sender: box, SIGNAL(doubleClicked(QBoxSet *)),
106 receiver: m_series, SIGNAL(doubleClicked(QBoxSet *)));
107 connect(sender: box, SIGNAL(clicked(QBoxSet *)), receiver: set, SIGNAL(clicked()));
108 connect(sender: box, SIGNAL(hovered(bool, QBoxSet *)), receiver: set, SIGNAL(hovered(bool)));
109 connect(sender: box, SIGNAL(pressed(QBoxSet *)), receiver: set, SIGNAL(pressed()));
110 connect(sender: box, SIGNAL(released(QBoxSet *)), receiver: set, SIGNAL(released()));
111 connect(sender: box, SIGNAL(doubleClicked(QBoxSet *)), receiver: set, SIGNAL(doubleClicked()));
112
113 // Set the decorative issues for the newly created box
114 // so that the brush and pen already defined for the set are kept.
115 if (set->brush() == Qt::NoBrush)
116 box->setBrush(m_series->brush());
117 else
118 box->setBrush(set->brush());
119 if (set->pen() == Qt::NoPen)
120 box->setPen(m_series->pen());
121 else
122 box->setPen(set->pen());
123 box->setBoxOutlined(m_series->boxOutlineVisible());
124 box->setBoxWidth(m_series->boxWidth());
125 }
126 updateBoxGeometry(box, index: s);
127
128 box->updateGeometry(domain: domain());
129
130 if (m_animation)
131 m_animation->addBox(box);
132 }
133
134 handleDomainUpdated();
135}
136
137void BoxPlotChartItem::handleUpdatedBars()
138{
139 foreach (BoxWhiskers *item, m_boxTable.values()) {
140 item->setBrush(m_series->brush());
141 item->setPen(m_series->pen());
142 item->setBoxOutlined(m_series->boxOutlineVisible());
143 item->setBoxWidth(m_series->boxWidth());
144 }
145 // Override with QBoxSet specific settings
146 foreach (QBoxSet *set, m_boxTable.keys()) {
147 if (set->brush().style() != Qt::NoBrush)
148 m_boxTable.value(akey: set)->setBrush(set->brush());
149 if (set->pen().style() != Qt::NoPen)
150 m_boxTable.value(akey: set)->setPen(set->pen());
151 }
152}
153
154void BoxPlotChartItem::handleBoxsetRemove(QList<QBoxSet*> barSets)
155{
156 foreach (QBoxSet *set, barSets) {
157 BoxWhiskers *boxItem = m_boxTable.value(akey: set);
158 m_boxTable.remove(akey: set);
159 delete boxItem;
160 }
161}
162
163void BoxPlotChartItem::handleDomainUpdated()
164{
165 if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0))
166 return;
167
168 // Set my bounding rect to same as domain size. Add one pixel at the top (-1.0) and the bottom as 0.0 would
169 // snip a bit off from the whisker at the grid line
170 m_boundingRect.setRect(ax: 0.0, ay: -1.0, aaw: domain()->size().width(), aah: domain()->size().height() + 1.0);
171
172 foreach (BoxWhiskers *item, m_boxTable.values()) {
173 item->updateGeometry(domain: domain());
174
175 // If the animation is set, start the animation for each BoxWhisker item
176 if (m_animation)
177 presenter()->startAnimation(animation: m_animation->boxAnimation(box: item));
178 }
179}
180
181void BoxPlotChartItem::handleLayoutChanged()
182{
183 foreach (BoxWhiskers *item, m_boxTable.values()) {
184 if (m_animation)
185 m_animation->setAnimationStart(item);
186
187 item->setBoxWidth(m_series->boxWidth());
188
189 bool dirty = updateBoxGeometry(box: item, index: item->m_data.m_index);
190 if (dirty && m_animation)
191 presenter()->startAnimation(animation: m_animation->boxChangeAnimation(box: item));
192 else
193 item->updateGeometry(domain: domain());
194 }
195}
196
197QRectF BoxPlotChartItem::boundingRect() const
198{
199 return m_boundingRect;
200}
201
202void BoxPlotChartItem::initializeLayout()
203{
204}
205
206QVector<QRectF> BoxPlotChartItem::calculateLayout()
207{
208 return QVector<QRectF>();
209}
210
211bool BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index)
212{
213 bool changed = false;
214
215 QBoxSet *set = m_series->d_func()->boxSetAt(index);
216 BoxWhiskersData &data = box->m_data;
217
218 if ((data.m_lowerExtreme != set->at(index: 0)) || (data.m_lowerQuartile != set->at(index: 1)) ||
219 (data.m_median != set->at(index: 2)) || (data.m_upperQuartile != set->at(index: 3)) || (data.m_upperExtreme != set->at(index: 4))) {
220 changed = true;
221 }
222
223 data.m_lowerExtreme = set->at(index: 0);
224 data.m_lowerQuartile = set->at(index: 1);
225 data.m_median = set->at(index: 2);
226 data.m_upperQuartile = set->at(index: 3);
227 data.m_upperExtreme = set->at(index: 4);
228 data.m_index = index;
229 data.m_boxItems = m_series->count();
230
231 data.m_maxX = domain()->maxX();
232 data.m_minX = domain()->minX();
233 data.m_maxY = domain()->maxY();
234 data.m_minY = domain()->minY();
235
236 data.m_seriesIndex = m_seriesIndex;
237 data.m_seriesCount = m_seriesCount;
238
239 return changed;
240}
241
242QT_CHARTS_END_NAMESPACE
243
244#include "moc_boxplotchartitem_p.cpp"
245

source code of qtcharts/src/charts/boxplotchart/boxplotchartitem.cpp