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 | |
327 | DeclarativeBoxSet::DeclarativeBoxSet(const QString label, QObject *parent) |
328 | : QBoxSet(label, parent) |
329 | { |
330 | connect(sender: this, SIGNAL(valuesChanged()), receiver: this, SIGNAL(changedValues())); |
331 | connect(sender: this, SIGNAL(valueChanged(int)), receiver: this, SIGNAL(changedValue(int))); |
332 | connect(sender: this, SIGNAL(brushChanged()), receiver: this, SLOT(handleBrushChanged())); |
333 | } |
334 | |
335 | QVariantList DeclarativeBoxSet::values() |
336 | { |
337 | QVariantList values; |
338 | for (int i(0); i < 5; i++) |
339 | values.append(t: QVariant(QBoxSet::at(index: i))); |
340 | return values; |
341 | } |
342 | |
343 | void DeclarativeBoxSet::setValues(QVariantList values) |
344 | { |
345 | for (int i(0); i < values.count(); i++) { |
346 | if (values.at(i).canConvert(targetTypeId: QVariant::Double)) |
347 | QBoxSet::append(value: values[i].toDouble()); |
348 | } |
349 | } |
350 | |
351 | QString DeclarativeBoxSet::brushFilename() const |
352 | { |
353 | return m_brushFilename; |
354 | } |
355 | |
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 | |
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(brushFilename: QString("" )); |
376 | } |
377 | } |
378 | |
379 | // ===================================================== |
380 | |
381 | DeclarativeBoxPlotSeries::DeclarativeBoxPlotSeries(QQuickItem *parent) : |
382 | QBoxPlotSeries(parent), |
383 | m_axes(new DeclarativeAxes(this)) |
384 | { |
385 | connect(sender: m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), receiver: this, SIGNAL(axisXChanged(QAbstractAxis*))); |
386 | connect(sender: m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), receiver: this, SIGNAL(axisYChanged(QAbstractAxis*))); |
387 | connect(sender: m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), receiver: this, SIGNAL(axisXTopChanged(QAbstractAxis*))); |
388 | connect(sender: m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), receiver: this, SIGNAL(axisYRightChanged(QAbstractAxis*))); |
389 | connect(sender: this, SIGNAL(hovered(bool, QBoxSet*)), receiver: this, SLOT(onHovered(bool, QBoxSet*))); |
390 | connect(sender: this, SIGNAL(clicked(QBoxSet*)), receiver: this, SLOT(onClicked(QBoxSet*))); |
391 | connect(sender: this, SIGNAL(brushChanged()), receiver: this, SLOT(handleBrushChanged())); |
392 | connect(sender: this, SIGNAL(pressed(QBoxSet*)), receiver: this, SLOT(onPressed(QBoxSet*))); |
393 | connect(sender: this, SIGNAL(released(QBoxSet*)), receiver: this, SLOT(onReleased(QBoxSet*))); |
394 | connect(sender: this, SIGNAL(doubleClicked(QBoxSet*)), receiver: this, SLOT(onDoubleClicked(QBoxSet*))); |
395 | } |
396 | |
397 | void DeclarativeBoxPlotSeries::classBegin() |
398 | { |
399 | } |
400 | |
401 | void DeclarativeBoxPlotSeries::componentComplete() |
402 | { |
403 | foreach (QObject *child, children()) { |
404 | if (qobject_cast<DeclarativeBoxSet *>(object: child)) { |
405 | QBoxPlotSeries::append(box: qobject_cast<DeclarativeBoxSet *>(object: child)); |
406 | } else if (qobject_cast<QVBoxPlotModelMapper *>(object: child)) { |
407 | QVBoxPlotModelMapper *mapper = qobject_cast<QVBoxPlotModelMapper *>(object: child); |
408 | mapper->setSeries(this); |
409 | } else if (QHBoxPlotModelMapper *mapper = qobject_cast<QHBoxPlotModelMapper *>(object: child)) { |
410 | mapper->setSeries(this); |
411 | } |
412 | } |
413 | } |
414 | |
415 | QQmlListProperty<QObject> DeclarativeBoxPlotSeries::seriesChildren() |
416 | { |
417 | return QQmlListProperty<QObject>(this, 0, &DeclarativeBoxPlotSeries::appendSeriesChildren ,0,0,0); |
418 | } |
419 | |
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 | |
427 | DeclarativeBoxSet *DeclarativeBoxPlotSeries::at(int index) |
428 | { |
429 | QList<QBoxSet *> setList = boxSets(); |
430 | if (index >= 0 && index < setList.count()) |
431 | return qobject_cast<DeclarativeBoxSet *>(object: setList[index]); |
432 | |
433 | return 0; |
434 | } |
435 | |
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, box: barset)) |
441 | return barset; |
442 | delete barset; |
443 | return 0; |
444 | } |
445 | |
446 | void DeclarativeBoxPlotSeries::onHovered(bool status, QBoxSet *boxset) |
447 | { |
448 | emit hovered(status, boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
449 | } |
450 | |
451 | void DeclarativeBoxPlotSeries::onClicked(QBoxSet *boxset) |
452 | { |
453 | emit clicked(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
454 | } |
455 | |
456 | void DeclarativeBoxPlotSeries::onPressed(QBoxSet *boxset) |
457 | { |
458 | emit pressed(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
459 | } |
460 | |
461 | void DeclarativeBoxPlotSeries::onReleased(QBoxSet *boxset) |
462 | { |
463 | emit released(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
464 | } |
465 | |
466 | void DeclarativeBoxPlotSeries::onDoubleClicked(QBoxSet *boxset) |
467 | { |
468 | emit doubleClicked(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
469 | } |
470 | |
471 | QString DeclarativeBoxPlotSeries::brushFilename() const |
472 | { |
473 | return m_brushFilename; |
474 | } |
475 | |
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 | |
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(brushFilename: QString("" )); |
496 | } |
497 | } |
498 | |
499 | QT_CHARTS_END_NAMESPACE |
500 | |
501 | #include "moc_declarativeboxplotseries_p.cpp" |
502 | |