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 "declarativebarseries_p.h"
31 #include "declarativeboxplotseries_p.h"
32 #include <QtCharts/QBoxSet>
33 #include <QtCharts/QHBoxPlotModelMapper>
34 #include <QtCharts/QVBoxPlotModelMapper>
35 
36 QT_CHARTS_BEGIN_NAMESPACE
37 
38 /*!
39     \qmltype BoxSet
40     \instantiates QBoxSet
41     \inqmlmodule QtCharts
42 
43     \brief Represents one item in a box-and-whiskers chart.
44 
45     A box-and-whiskers item is a graphical representation of a range and three median values
46     that is constructed from five different values. There are two ways to specify the values.
47     The first one is by using a constructor or the append() method. The values have to be
48     specified in the following order: lower extreme, lower quartile, median, upper quartile,
49     and upper extreme.
50 
51     The second way is to create an empty BoxSet instance and specify the values using the
52     setValue() method.
53 
54     \sa BoxPlotSeries
55 */
56 
57 /*!
58     \qmlproperty list BoxSet::values
59     The values of the box-and-whiskers item. The following enumerations can be
60     used as indexes when accessing the list of values:
61 
62     \value BoxSet.LowerExtreme The smallest value of the box-and-whiskers item.
63     \value BoxSet.LowerQuartile The median value of the lower half of the box-and-whiskers item.
64     \value BoxSet.Median The median value of the box-and-whiskers item.
65     \value BoxSet.UpperQuartile The median value of the upper half of the box-and-whiskers item.
66     \value BoxSet.UpperExtreme The largest value of the box-and-whiskers item.
67 
68     \sa at(), setValue()
69 */
70 /*!
71     \qmlproperty string BoxSet::label
72     The label of the category of the box-and-whiskers item.
73 */
74 /*!
75     \qmlproperty int BoxSet::count
76     The number of values of the box-and-whiskers item.
77 */
78 
79 /*!
80     \qmlproperty string BoxSet::brushFilename
81     The name of the file used as a brush for the box-and-whiskers item.
82 */
83 
84 /*!
85     \qmlmethod void BoxSet::at(int index)
86     Returns the value in the position specified by \a index.
87 */
88 /*!
89     \qmlmethod void BoxSet::append(qreal value)
90     Appends the new value specified by \a value to the end of the box-and-whiskers item.
91 */
92 /*!
93     \qmlmethod void BoxSet::clear()
94     Sets all the values of the box-and-whiskers item to 0.
95 */
96 /*!
97     \qmlmethod void BoxSet::setValue(int index, qreal value)
98     Sets the value specified by \a value in the position specified by \a index.
99 */
100 /*!
101     \qmlsignal BoxSet::clicked()
102     This signal is emitted when the user clicks a box-and-whiskers item in the chart.
103 
104     The corresponding signal handler is \c onClicked().
105 */
106 /*!
107     \qmlsignal BoxSet::pressed()
108     This signal is emitted when the user clicks a box-and-whiskers item in the chart
109     and holds down the mouse button.
110 
111     The corresponding signal handler is \c onPressed.
112 */
113 /*!
114     \qmlsignal BoxSet::released()
115     This signal is emitted when the user releases the mouse press on a box-and-whiskers item.
116 
117     The corresponding signal handler is \c onReleased().
118 */
119 /*!
120     \qmlsignal BoxSet::doubleClicked()
121     This signal is emitted when the user double-clicks a box-and-whiskers item.
122 
123     The corresponding signal handler is \c onDoubleClicked().
124 */
125 /*!
126     \qmlsignal BoxSet::hovered(bool status)
127     This signal is emitted when a mouse is hovered over a box-and-whiskers item in a chart.
128     When the mouse moves over the item, \a status turns \c true, and when the mouse moves
129     away again, it turns \c false.
130 
131     The corresponding signal handler is \c onHovered().
132 */
133 /*!
134     \qmlsignal BoxSet::valuesChanged()
135     This signal is emitted when multiple values of the box-and-whiskers item change.
136 
137     The corresponding signal handler is \c onValuesChanged().
138 */
139 /*!
140     \qmlsignal BoxSet::valueChanged(int index)
141     This signal is emitted when the value of the box-and-whiskers item specified by \a index
142     changes.
143 
144     The corresponding signal handler is \c onValueChanged().
145 */
146 /*!
147     \qmlsignal BoxSet::cleared()
148     This signal is emitted when all the values of the box-and-whiskers item are set to 0.
149 
150     The corresponding signal handler is \c onCleared().
151 */
152 
153 /*!
154     \qmltype BoxPlotSeries
155     \instantiates QBoxPlotSeries
156     \inqmlmodule QtCharts
157 
158     \inherits AbstractSeries
159 
160     \brief Presents data in box-and-whiskers charts.
161 
162     A box plot series acts as a container for box-and-whiskers items. Items from multiple series
163     are grouped into categories according to their index value.
164 
165     The BarCategoryAxis class is used to add the categories to the chart's axis. Category labels
166     have to be unique. If the same category label is defined for several box-and-whiskers items,
167     only the first one is drawn.
168 
169     The following QML code snippet shows how to create a simple box-and-whiskers chart:
170     \code
171     import QtQuick 2.0
172     import QtCharts 2.0
173 
174     ChartView {
175         title: "Box Plot series"
176         width: 400
177         height: 300
178         theme: ChartView.ChartThemeBrownSand
179         legend.alignment: Qt.AlignBottom
180 
181         BoxPlotSeries {
182             id: plotSeries
183             name: "Income"
184             BoxSet { label: "Jan"; values: [3, 4, 5.1, 6.2, 8.5] }
185             BoxSet { label: "Feb"; values: [5, 6, 7.5, 8.6, 11.8] }
186             BoxSet { label: "Mar"; values: [3.2, 5, 5.7, 8, 9.2] }
187             BoxSet { label: "Apr"; values: [3.8, 5, 6.4, 7, 8] }
188             BoxSet { label: "May"; values: [4, 5, 5.2, 6, 7] }
189         }
190     }
191     \endcode
192 
193     \beginfloatleft
194     \image examples_qmlboxplot.png
195     \endfloat
196     \clearfloat
197 
198     \sa BoxSet, BarCategoryAxis
199 */
200 
201 /*!
202     \qmlmethod BoxPlotSeries::at(int index)
203     Returns the box-and-whiskers item in the position specified by \a index.
204 */
205 
206 /*!
207     \qmlmethod BoxPlotSeries::append(string label, VariantList values)
208     Appends a new box-and-whiskers item with the label specified by \a label and the values
209     specified by \a values to the series.
210  */
211 /*!
212     \qmlmethod BoxPlotSeries::append(BoxSet box)
213     Appends the box-and-whiskers item specified by \a box to the series.
214 */
215 /*!
216     \qmlmethod BoxPlotSeries::insert(int index, string label, VariantList values)
217     Inserts a new box-and-whiskers item with the label specified by \a label and the values
218     specified by \a values to the series at the position specified by \a index.
219 */
220 /*!
221     \qmlmethod BoxPlotSeries::remove(QBoxSet boxset)
222     Removes the box-and-whiskers item specified by \a boxset from the series.
223 */
224 /*!
225     \qmlmethod BoxPlotSeries::clear()
226     Removes all box-and-whiskers items from the series and permanently deletes them.
227 */
228 /*!
229     \qmlsignal BoxPlotSeries::clicked(BoxSet boxset);
230     This signal is emitted when the user clicks the box-and-whiskers item specified by
231     \a boxset in the chart.
232 
233     The corresponding signal handler is \c onClicked().
234 */
235 /*!
236     \qmlsignal BoxPlotSeries::hovered(bool status, BoxSet boxset);
237     This signal is emitted when a mouse is hovered over the box-and-whiskers item specified by
238     \a boxset in the chart. When the mouse moves over the item, \a status turns \c true, and
239     when the mouse moves away again, it turns \c false.
240 
241     The corresponding signal handler is \c onHovered().
242 */
243 /*!
244     \qmlsignal BoxPlotSeries::pressed(BoxSet boxset)
245     This signal is emitted when the user presses the \a boxset on the chart.
246 
247     The corresponding signal handler is \c onPressed.
248 */
249 /*!
250     \qmlsignal BoxPlotSeries::released(BoxSet boxset)
251     This signal is emitted when the user releases the mouse press on the box-and-whiskers
252     item specified by \a boxset in the chart.
253 
254     The corresponding signal handler is \c onReleased().
255 */
256 /*!
257     \qmlsignal BoxPlotSeries::doubleClicked(BoxSet boxset)
258     This signal is emitted when the user double-clicks the box-and-whiskers item specified by
259     \a boxset in the chart.
260 
261     The corresponding signal handler is \c onDoubleClicked().
262 */
263 /*!
264     \qmlsignal BoxPlotSeries::boxsetsAdded(list sets)
265     This signal is emitted when the box-and-whiskers items specified by \a sets
266     are added to the series.
267 
268      The corresponding signal handler is \c onBoxsetsAdded().
269 */
270 /*!
271     \qmlsignal BoxPlotSeries::boxsetsRemoved(list sets)
272     This signal is emitted when the box-and-whiskers items specified by \a sets
273     are removed from the series.
274 
275      The corresponding signal handler is \c onBoxsetsRemoved().
276  */
277 /*!
278     \qmlproperty AbstractAxis BoxPlotSeries::axisX
279     The x-axis used for the series. If you leave both axisX and axisXTop undefined, a
280     BarCategoryAxis is created for the series.
281     \sa axisXTop
282 */
283 /*!
284     \qmlproperty AbstractAxis BoxPlotSeries::axisY
285     The y-axis used for the series. If you leave both axisY and axisYRight undefined, a
286     ValueAxis is created for the series.
287     \sa axisYRight
288 */
289 /*!
290     \qmlproperty AbstractAxis BoxPlotSeries::axisXTop
291     The x-axis used for the series, drawn on top of the chart view.
292 
293     \note You can only provide either axisX or axisXTop, but not both.
294     \sa axisX
295 
296     \sa axisX
297 */
298 /*!
299     \qmlproperty AbstractAxis BoxPlotSeries::axisYRight
300     The y-axis used for the series, drawn to the right on the chart view.
301 
302     \note You can only provide either axisY or axisYRight, but not both.
303     \sa axisY
304 */
305 /*!
306     \qmlproperty bool BoxPlotSeries::boxOutlineVisible
307     The visibility of the box outline.
308 */
309 /*!
310     \qmlproperty real BoxPlotSeries::boxWidth
311     \brief The width of the box-and-whiskers item. The value indicates the relative
312     width of the item within its category. The value can be between 0.0 and 1.0. Negative values
313     are replaced with 0.0 and values greater than 1.0 are replaced with 1.0.
314 */
315 
316 /*!
317     \qmlproperty string BoxPlotSeries::brushFilename
318     The name of the file used as a brush for the series.
319 */
320 
321 /*!
322     \qmlproperty int BoxPlotSeries::count
323     The number of box-and-whiskers items in a box plot series.
324 */
325 
326 
DeclarativeBoxSet(const QString label,QObject * parent)327 DeclarativeBoxSet::DeclarativeBoxSet(const QString label, QObject *parent)
328     : QBoxSet(label, parent)
329 {
330     connect(this, SIGNAL(valuesChanged()), this, SIGNAL(changedValues()));
331     connect(this, SIGNAL(valueChanged(int)), this, SIGNAL(changedValue(int)));
332     connect(this, SIGNAL(brushChanged()), this, SLOT(handleBrushChanged()));
333 }
334 
values()335 QVariantList DeclarativeBoxSet::values()
336 {
337     QVariantList values;
338     for (int i(0); i < 5; i++)
339         values.append(QVariant(QBoxSet::at(i)));
340     return values;
341 }
342 
setValues(QVariantList values)343 void DeclarativeBoxSet::setValues(QVariantList values)
344 {
345     for (int i(0); i < values.count(); i++) {
346         if (values.at(i).canConvert(QVariant::Double))
347             QBoxSet::append(values[i].toDouble());
348     }
349 }
350 
brushFilename() const351 QString DeclarativeBoxSet::brushFilename() const
352 {
353     return m_brushFilename;
354 }
355 
setBrushFilename(const QString & brushFilename)356 void DeclarativeBoxSet::setBrushFilename(const QString &brushFilename)
357 {
358     QImage brushImage(brushFilename);
359     if (QBoxSet::brush().textureImage() != brushImage) {
360         QBrush brush = QBoxSet::brush();
361         brush.setTextureImage(brushImage);
362         QBoxSet::setBrush(brush);
363         m_brushFilename = brushFilename;
364         m_brushImage = brushImage;
365         emit brushFilenameChanged(brushFilename);
366     }
367 }
368 
handleBrushChanged()369 void DeclarativeBoxSet::handleBrushChanged()
370 {
371     // If the texture image of the brush has changed along the brush
372     // the brush file name needs to be cleared.
373     if (!m_brushFilename.isEmpty() && QBoxSet::brush().textureImage() != m_brushImage) {
374         m_brushFilename.clear();
375         emit brushFilenameChanged(QString(""));
376     }
377 }
378 
379 // =====================================================
380 
DeclarativeBoxPlotSeries(QQuickItem * parent)381 DeclarativeBoxPlotSeries::DeclarativeBoxPlotSeries(QQuickItem *parent) :
382     QBoxPlotSeries(parent),
383     m_axes(new DeclarativeAxes(this))
384 {
385     connect(m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), this, SIGNAL(axisXChanged(QAbstractAxis*)));
386     connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), this, SIGNAL(axisYChanged(QAbstractAxis*)));
387     connect(m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), this, SIGNAL(axisXTopChanged(QAbstractAxis*)));
388     connect(m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), this, SIGNAL(axisYRightChanged(QAbstractAxis*)));
389     connect(this, SIGNAL(hovered(bool, QBoxSet*)), this, SLOT(onHovered(bool, QBoxSet*)));
390     connect(this, SIGNAL(clicked(QBoxSet*)), this, SLOT(onClicked(QBoxSet*)));
391     connect(this, SIGNAL(brushChanged()), this, SLOT(handleBrushChanged()));
392     connect(this, SIGNAL(pressed(QBoxSet*)), this, SLOT(onPressed(QBoxSet*)));
393     connect(this, SIGNAL(released(QBoxSet*)), this, SLOT(onReleased(QBoxSet*)));
394     connect(this, SIGNAL(doubleClicked(QBoxSet*)), this, SLOT(onDoubleClicked(QBoxSet*)));
395 }
396 
classBegin()397 void DeclarativeBoxPlotSeries::classBegin()
398 {
399 }
400 
componentComplete()401 void DeclarativeBoxPlotSeries::componentComplete()
402 {
403     foreach (QObject *child, children()) {
404         if (qobject_cast<DeclarativeBoxSet *>(child)) {
405             QBoxPlotSeries::append(qobject_cast<DeclarativeBoxSet *>(child));
406         } else if (qobject_cast<QVBoxPlotModelMapper *>(child)) {
407             QVBoxPlotModelMapper *mapper = qobject_cast<QVBoxPlotModelMapper *>(child);
408             mapper->setSeries(this);
409         } else if (QHBoxPlotModelMapper *mapper = qobject_cast<QHBoxPlotModelMapper *>(child)) {
410             mapper->setSeries(this);
411         }
412     }
413 }
414 
seriesChildren()415 QQmlListProperty<QObject> DeclarativeBoxPlotSeries::seriesChildren()
416 {
417     return QQmlListProperty<QObject>(this, 0, &DeclarativeBoxPlotSeries::appendSeriesChildren ,0,0,0);
418 }
419 
appendSeriesChildren(QQmlListProperty<QObject> * list,QObject * element)420 void DeclarativeBoxPlotSeries::appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element)
421 {
422     // Empty implementation; the children are parsed in componentComplete instead
423     Q_UNUSED(list);
424     Q_UNUSED(element);
425 }
426 
at(int index)427 DeclarativeBoxSet *DeclarativeBoxPlotSeries::at(int index)
428 {
429     QList<QBoxSet *> setList = boxSets();
430     if (index >= 0 && index < setList.count())
431         return qobject_cast<DeclarativeBoxSet *>(setList[index]);
432 
433     return 0;
434 }
435 
insert(int index,const QString label,QVariantList values)436 DeclarativeBoxSet *DeclarativeBoxPlotSeries::insert(int index, const QString label, QVariantList values)
437 {
438     DeclarativeBoxSet *barset = new DeclarativeBoxSet(label, this);
439     barset->setValues(values);
440     if (QBoxPlotSeries::insert(index, barset))
441         return barset;
442     delete barset;
443     return 0;
444 }
445 
onHovered(bool status,QBoxSet * boxset)446 void DeclarativeBoxPlotSeries::onHovered(bool status, QBoxSet *boxset)
447 {
448     emit hovered(status, qobject_cast<DeclarativeBoxSet *>(boxset));
449 }
450 
onClicked(QBoxSet * boxset)451 void DeclarativeBoxPlotSeries::onClicked(QBoxSet *boxset)
452 {
453     emit clicked(qobject_cast<DeclarativeBoxSet *>(boxset));
454 }
455 
onPressed(QBoxSet * boxset)456 void DeclarativeBoxPlotSeries::onPressed(QBoxSet *boxset)
457 {
458     emit pressed(qobject_cast<DeclarativeBoxSet *>(boxset));
459 }
460 
onReleased(QBoxSet * boxset)461 void DeclarativeBoxPlotSeries::onReleased(QBoxSet *boxset)
462 {
463     emit released(qobject_cast<DeclarativeBoxSet *>(boxset));
464 }
465 
onDoubleClicked(QBoxSet * boxset)466 void DeclarativeBoxPlotSeries::onDoubleClicked(QBoxSet *boxset)
467 {
468     emit doubleClicked(qobject_cast<DeclarativeBoxSet *>(boxset));
469 }
470 
brushFilename() const471 QString DeclarativeBoxPlotSeries::brushFilename() const
472 {
473     return m_brushFilename;
474 }
475 
setBrushFilename(const QString & brushFilename)476 void DeclarativeBoxPlotSeries::setBrushFilename(const QString &brushFilename)
477 {
478     QImage brushImage(brushFilename);
479     if (QBoxPlotSeries::brush().textureImage() != brushImage) {
480         QBrush brush = QBoxPlotSeries::brush();
481         brush.setTextureImage(brushImage);
482         QBoxPlotSeries::setBrush(brush);
483         m_brushFilename = brushFilename;
484         m_brushImage = brushImage;
485         emit brushFilenameChanged(brushFilename);
486     }
487 }
488 
handleBrushChanged()489 void DeclarativeBoxPlotSeries::handleBrushChanged()
490 {
491     // If the texture image of the brush has changed along the brush
492     // the brush file name needs to be cleared.
493     if (!m_brushFilename.isEmpty() && QBoxPlotSeries::brush().textureImage() != m_brushImage) {
494         m_brushFilename.clear();
495         emit brushFilenameChanged(QString(""));
496     }
497 }
498 
499 QT_CHARTS_END_NAMESPACE
500 
501 #include "moc_declarativeboxplotseries_p.cpp"
502