1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <private/boxplotchartitem_p.h>
5#include <private/qboxplotseries_p.h>
6#include <private/bar_p.h>
7#include <private/qboxset_p.h>
8#include <private/qabstractbarseries_p.h>
9#include <QtCharts/QBoxSet>
10#include <private/boxwhiskers_p.h>
11#include <QtGui/QPainter>
12
13QT_BEGIN_NAMESPACE
14
15BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item) :
16 ChartItem(series->d_func(), item),
17 m_series(series),
18 m_animation(0)
19{
20 setAcceptedMouseButtons({});
21 connect(sender: series, SIGNAL(boxsetsRemoved(QList<QBoxSet *>)), receiver: this, SLOT(handleBoxsetRemove(QList<QBoxSet *>)));
22 connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged()));
23 connect(sender: series, SIGNAL(opacityChanged()), receiver: this, SLOT(handleOpacityChanged()));
24 connect(sender: series->d_func(), SIGNAL(restructuredBoxes()), receiver: this, SLOT(handleDataStructureChanged()));
25 connect(sender: series->d_func(), SIGNAL(updatedLayout()), receiver: this, SLOT(handleLayoutChanged()));
26 connect(sender: series->d_func(), SIGNAL(updatedBoxes()), receiver: this, SLOT(handleUpdatedBars()));
27 connect(sender: series->d_func(), SIGNAL(updated()), receiver: this, SLOT(handleUpdatedBars()));
28 // QBoxPlotSeriesPrivate calls handleDataStructureChanged(), don't do it here
29 setZValue(ChartPresenter::BoxPlotSeriesZValue);
30}
31
32BoxPlotChartItem::~BoxPlotChartItem()
33{
34}
35
36void BoxPlotChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
37{
38 Q_UNUSED(painter);
39 Q_UNUSED(option);
40 Q_UNUSED(widget);
41}
42
43void BoxPlotChartItem::setAnimation(BoxPlotAnimation *animation)
44{
45 m_animation = animation;
46 if (m_animation) {
47 foreach (BoxWhiskers *item, m_boxTable.values())
48 m_animation->addBox(box: item);
49 handleDomainUpdated();
50 }
51}
52
53void BoxPlotChartItem::handleSeriesVisibleChanged()
54{
55 setVisible(m_series->isVisible());
56}
57
58void BoxPlotChartItem::handleOpacityChanged()
59{
60 setOpacity(m_series->opacity());
61}
62
63void BoxPlotChartItem::handleDataStructureChanged()
64{
65 int setCount = m_series->count();
66
67 for (int s = 0; s < setCount; s++) {
68 QBoxSet *set = m_series->d_func()->boxSetAt(index: s);
69
70 BoxWhiskers *box = m_boxTable.value(key: set);
71 if (!box) {
72 // Item is not yet created, make a box and add it to hash table
73 box = new BoxWhiskers(set, domain(), this);
74 m_boxTable.insert(key: set, value: box);
75 connect(sender: box, SIGNAL(clicked(QBoxSet *)), receiver: m_series, SIGNAL(clicked(QBoxSet *)));
76 connect(sender: box, SIGNAL(hovered(bool, QBoxSet *)), receiver: m_series, SIGNAL(hovered(bool, QBoxSet *)));
77 connect(sender: box, SIGNAL(pressed(QBoxSet *)), receiver: m_series, SIGNAL(pressed(QBoxSet *)));
78 connect(sender: box, SIGNAL(released(QBoxSet *)), receiver: m_series, SIGNAL(released(QBoxSet *)));
79 connect(sender: box, SIGNAL(doubleClicked(QBoxSet *)),
80 receiver: m_series, SIGNAL(doubleClicked(QBoxSet *)));
81 connect(sender: box, SIGNAL(clicked(QBoxSet *)), receiver: set, SIGNAL(clicked()));
82 connect(sender: box, SIGNAL(hovered(bool, QBoxSet *)), receiver: set, SIGNAL(hovered(bool)));
83 connect(sender: box, SIGNAL(pressed(QBoxSet *)), receiver: set, SIGNAL(pressed()));
84 connect(sender: box, SIGNAL(released(QBoxSet *)), receiver: set, SIGNAL(released()));
85 connect(sender: box, SIGNAL(doubleClicked(QBoxSet *)), receiver: set, SIGNAL(doubleClicked()));
86
87 // Set the decorative issues for the newly created box
88 // so that the brush and pen already defined for the set are kept.
89 if (set->brush() == Qt::NoBrush)
90 box->setBrush(m_series->brush());
91 else
92 box->setBrush(set->brush());
93 if (set->pen() == Qt::NoPen)
94 box->setPen(m_series->pen());
95 else
96 box->setPen(set->pen());
97 box->setBoxOutlined(m_series->boxOutlineVisible());
98 box->setBoxWidth(m_series->boxWidth());
99 }
100 updateBoxGeometry(box, index: s);
101
102 box->updateGeometry(domain: domain());
103
104 if (m_animation)
105 m_animation->addBox(box);
106 }
107
108 handleDomainUpdated();
109}
110
111void BoxPlotChartItem::handleUpdatedBars()
112{
113 foreach (BoxWhiskers *item, m_boxTable.values()) {
114 item->setBrush(m_series->brush());
115 item->setPen(m_series->pen());
116 item->setBoxOutlined(m_series->boxOutlineVisible());
117 item->setBoxWidth(m_series->boxWidth());
118 }
119 // Override with QBoxSet specific settings
120 foreach (QBoxSet *set, m_boxTable.keys()) {
121 if (set->brush().style() != Qt::NoBrush)
122 m_boxTable.value(key: set)->setBrush(set->brush());
123 if (set->pen().style() != Qt::NoPen)
124 m_boxTable.value(key: set)->setPen(set->pen());
125 }
126}
127
128void BoxPlotChartItem::handleBoxsetRemove(const QList<QBoxSet *> &barSets)
129{
130 for (auto *set : barSets) {
131 BoxWhiskers *boxItem = m_boxTable.value(key: set);
132 m_boxTable.remove(key: set);
133 delete boxItem;
134 }
135}
136
137void BoxPlotChartItem::handleDomainUpdated()
138{
139 if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0))
140 return;
141
142 // Set my bounding rect to same as domain size. Add one pixel at the top (-1.0) and the bottom as 0.0 would
143 // snip a bit off from the whisker at the grid line
144 m_boundingRect.setRect(ax: 0.0, ay: -1.0, aaw: domain()->size().width(), aah: domain()->size().height() + 1.0);
145
146 foreach (BoxWhiskers *item, m_boxTable.values()) {
147 item->updateGeometry(domain: domain());
148
149 // If the animation is set, start the animation for each BoxWhisker item
150 if (m_animation)
151 presenter()->startAnimation(animation: m_animation->boxAnimation(box: item));
152 }
153}
154
155void BoxPlotChartItem::handleLayoutChanged()
156{
157 foreach (BoxWhiskers *item, m_boxTable.values()) {
158 if (m_animation)
159 m_animation->setAnimationStart(item);
160
161 item->setBoxWidth(m_series->boxWidth());
162
163 bool dirty = updateBoxGeometry(box: item, index: item->m_data.m_index);
164 if (dirty && m_animation)
165 presenter()->startAnimation(animation: m_animation->boxChangeAnimation(box: item));
166 else
167 item->updateGeometry(domain: domain());
168 }
169}
170
171QRectF BoxPlotChartItem::boundingRect() const
172{
173 return m_boundingRect;
174}
175
176void BoxPlotChartItem::initializeLayout()
177{
178}
179
180QList<QRectF> BoxPlotChartItem::calculateLayout()
181{
182 return QList<QRectF>();
183}
184
185bool BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index)
186{
187 bool changed = false;
188
189 QBoxSet *set = m_series->d_func()->boxSetAt(index);
190 BoxWhiskersData &data = box->m_data;
191
192 if ((data.m_lowerExtreme != set->at(index: 0)) || (data.m_lowerQuartile != set->at(index: 1)) ||
193 (data.m_median != set->at(index: 2)) || (data.m_upperQuartile != set->at(index: 3)) || (data.m_upperExtreme != set->at(index: 4))) {
194 changed = true;
195 }
196
197 data.m_lowerExtreme = set->at(index: 0);
198 data.m_lowerQuartile = set->at(index: 1);
199 data.m_median = set->at(index: 2);
200 data.m_upperQuartile = set->at(index: 3);
201 data.m_upperExtreme = set->at(index: 4);
202 data.m_index = index;
203 data.m_boxItems = m_series->count();
204
205 data.m_maxX = domain()->maxX();
206 data.m_minX = domain()->minX();
207 data.m_maxY = domain()->maxY();
208 data.m_minY = domain()->minY();
209
210 data.m_seriesIndex = m_seriesIndex;
211 data.m_seriesCount = m_seriesCount;
212
213 return changed;
214}
215
216QT_END_NAMESPACE
217
218#include "moc_boxplotchartitem_p.cpp"
219

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