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 
39 QT_CHARTS_BEGIN_NAMESPACE
40 
BoxPlotChartItem(QBoxPlotSeries * series,QGraphicsItem * item)41 BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item) :
42     ChartItem(series->d_func(), item),
43     m_series(series),
44     m_animation(0)
45 {
46     setAcceptedMouseButtons({});
47     connect(series, SIGNAL(boxsetsRemoved(QList<QBoxSet *>)), this, SLOT(handleBoxsetRemove(QList<QBoxSet *>)));
48     connect(series, SIGNAL(visibleChanged()), this, SLOT(handleSeriesVisibleChanged()));
49     connect(series, SIGNAL(opacityChanged()), this, SLOT(handleOpacityChanged()));
50     connect(series->d_func(), SIGNAL(restructuredBoxes()), this, SLOT(handleDataStructureChanged()));
51     connect(series->d_func(), SIGNAL(updatedLayout()), this, SLOT(handleLayoutChanged()));
52     connect(series->d_func(), SIGNAL(updatedBoxes()), this, SLOT(handleUpdatedBars()));
53     connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdatedBars()));
54     // QBoxPlotSeriesPrivate calls handleDataStructureChanged(), don't do it here
55     setZValue(ChartPresenter::BoxPlotSeriesZValue);
56 }
57 
~BoxPlotChartItem()58 BoxPlotChartItem::~BoxPlotChartItem()
59 {
60 }
61 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)62 void BoxPlotChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
63 {
64     Q_UNUSED(painter);
65     Q_UNUSED(option);
66     Q_UNUSED(widget);
67 }
68 
setAnimation(BoxPlotAnimation * animation)69 void BoxPlotChartItem::setAnimation(BoxPlotAnimation *animation)
70 {
71     m_animation = animation;
72     if (m_animation) {
73         foreach (BoxWhiskers *item, m_boxTable.values())
74             m_animation->addBox(item);
75         handleDomainUpdated();
76     }
77 }
78 
handleSeriesVisibleChanged()79 void BoxPlotChartItem::handleSeriesVisibleChanged()
80 {
81     setVisible(m_series->isVisible());
82 }
83 
handleOpacityChanged()84 void BoxPlotChartItem::handleOpacityChanged()
85 {
86     setOpacity(m_series->opacity());
87 }
88 
handleDataStructureChanged()89 void 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(s);
95 
96         BoxWhiskers *box = m_boxTable.value(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(set, box);
101             connect(box, SIGNAL(clicked(QBoxSet *)), m_series, SIGNAL(clicked(QBoxSet *)));
102             connect(box, SIGNAL(hovered(bool, QBoxSet *)), m_series, SIGNAL(hovered(bool, QBoxSet *)));
103             connect(box, SIGNAL(pressed(QBoxSet *)), m_series, SIGNAL(pressed(QBoxSet *)));
104             connect(box, SIGNAL(released(QBoxSet *)), m_series, SIGNAL(released(QBoxSet *)));
105             connect(box, SIGNAL(doubleClicked(QBoxSet *)),
106                     m_series, SIGNAL(doubleClicked(QBoxSet *)));
107             connect(box, SIGNAL(clicked(QBoxSet *)), set, SIGNAL(clicked()));
108             connect(box, SIGNAL(hovered(bool, QBoxSet *)), set, SIGNAL(hovered(bool)));
109             connect(box, SIGNAL(pressed(QBoxSet *)), set, SIGNAL(pressed()));
110             connect(box, SIGNAL(released(QBoxSet *)), set, SIGNAL(released()));
111             connect(box, SIGNAL(doubleClicked(QBoxSet *)), 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, s);
127 
128         box->updateGeometry(domain());
129 
130         if (m_animation)
131             m_animation->addBox(box);
132     }
133 
134     handleDomainUpdated();
135 }
136 
handleUpdatedBars()137 void 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(set)->setBrush(set->brush());
149         if (set->pen().style() != Qt::NoPen)
150             m_boxTable.value(set)->setPen(set->pen());
151     }
152 }
153 
handleBoxsetRemove(QList<QBoxSet * > barSets)154 void BoxPlotChartItem::handleBoxsetRemove(QList<QBoxSet*> barSets)
155 {
156     foreach (QBoxSet *set, barSets) {
157         BoxWhiskers *boxItem = m_boxTable.value(set);
158         m_boxTable.remove(set);
159         delete boxItem;
160     }
161 }
162 
handleDomainUpdated()163 void 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(0.0, -1.0, domain()->size().width(), domain()->size().height() + 1.0);
171 
172     foreach (BoxWhiskers *item, m_boxTable.values()) {
173         item->updateGeometry(domain());
174 
175         // If the animation is set, start the animation for each BoxWhisker item
176         if (m_animation)
177             presenter()->startAnimation(m_animation->boxAnimation(item));
178     }
179 }
180 
handleLayoutChanged()181 void 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(item, item->m_data.m_index);
190         if (dirty && m_animation)
191             presenter()->startAnimation(m_animation->boxChangeAnimation(item));
192         else
193             item->updateGeometry(domain());
194     }
195 }
196 
boundingRect() const197 QRectF BoxPlotChartItem::boundingRect() const
198 {
199     return m_boundingRect;
200 }
201 
initializeLayout()202 void BoxPlotChartItem::initializeLayout()
203 {
204 }
205 
calculateLayout()206 QVector<QRectF> BoxPlotChartItem::calculateLayout()
207 {
208     return QVector<QRectF>();
209 }
210 
updateBoxGeometry(BoxWhiskers * box,int index)211 bool 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(0)) || (data.m_lowerQuartile != set->at(1)) ||
219         (data.m_median != set->at(2)) || (data.m_upperQuartile != set->at(3)) || (data.m_upperExtreme != set->at(4))) {
220         changed = true;
221     }
222 
223     data.m_lowerExtreme = set->at(0);
224     data.m_lowerQuartile = set->at(1);
225     data.m_median = set->at(2);
226     data.m_upperQuartile = set->at(3);
227     data.m_upperExtreme = set->at(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 
242 QT_CHARTS_END_NAMESPACE
243 
244 #include "moc_boxplotchartitem_p.cpp"
245