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 | |
10 | QT_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 | |
301 | DeclarativeBoxSet::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 | |
309 | QVariantList 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 | |
317 | void 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 | |
325 | QString DeclarativeBoxSet::brushFilename() const |
326 | { |
327 | return m_brushFilename; |
328 | } |
329 | |
330 | void 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 | |
343 | void 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 | |
355 | DeclarativeBoxPlotSeries::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 | |
371 | void DeclarativeBoxPlotSeries::classBegin() |
372 | { |
373 | } |
374 | |
375 | void 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 | |
389 | QQmlListProperty<QObject> DeclarativeBoxPlotSeries::seriesChildren() |
390 | { |
391 | return QQmlListProperty<QObject>(this, 0, &DeclarativeBoxPlotSeries::appendSeriesChildren ,0,0,0); |
392 | } |
393 | |
394 | void 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 | |
401 | DeclarativeBoxSet *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 | |
410 | DeclarativeBoxSet *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 | |
420 | void DeclarativeBoxPlotSeries::onHovered(bool status, QBoxSet *boxset) |
421 | { |
422 | emit hovered(status, boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
423 | } |
424 | |
425 | void DeclarativeBoxPlotSeries::onClicked(QBoxSet *boxset) |
426 | { |
427 | emit clicked(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
428 | } |
429 | |
430 | void DeclarativeBoxPlotSeries::onPressed(QBoxSet *boxset) |
431 | { |
432 | emit pressed(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
433 | } |
434 | |
435 | void DeclarativeBoxPlotSeries::onReleased(QBoxSet *boxset) |
436 | { |
437 | emit released(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
438 | } |
439 | |
440 | void DeclarativeBoxPlotSeries::onDoubleClicked(QBoxSet *boxset) |
441 | { |
442 | emit doubleClicked(boxset: qobject_cast<DeclarativeBoxSet *>(object: boxset)); |
443 | } |
444 | |
445 | QString DeclarativeBoxPlotSeries::brushFilename() const |
446 | { |
447 | return m_brushFilename; |
448 | } |
449 | |
450 | void 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 | |
463 | void 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 | |
473 | QT_END_NAMESPACE |
474 | |
475 | #include "moc_declarativeboxplotseries_p.cpp" |
476 | |