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

source code of qtcharts/src/chartsqml2/declarativeboxplotseries.cpp