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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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