1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtCharts/QBoxPlotSeries> |
5 | #include <private/qboxplotseries_p.h> |
6 | #include <QtCharts/QBoxPlotLegendMarker> |
7 | #include <QtCharts/QBarCategoryAxis> |
8 | #include <private/boxplotchartitem_p.h> |
9 | #include <private/chartdataset_p.h> |
10 | #include <private/charttheme_p.h> |
11 | #include <QtCharts/QValueAxis> |
12 | #include <private/charttheme_p.h> |
13 | #include <private/boxplotanimation_p.h> |
14 | #include <private/qchart_p.h> |
15 | #include <QtCharts/QBoxSet> |
16 | #include <private/qboxset_p.h> |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | /*! |
21 | \class QBoxPlotSeries |
22 | \inmodule QtCharts |
23 | \brief The QBoxPlotSeries class presents data in box-and-whiskers charts. |
24 | |
25 | A box plot series acts as a container for box-and-whiskers items. Items from multiple series |
26 | are grouped into categories according to their index value. |
27 | |
28 | The QBarCategoryAxis class is used to add the categories to the chart's axis. Category labels |
29 | have to be unique. If the same category label is defined for several box-and-whiskers items, |
30 | only the first one is drawn. |
31 | |
32 | See the \l {Charts with Widgets Gallery} to learn how to create a |
33 | box-and-whiskers chart. |
34 | \image examples_boxplotchart.png |
35 | |
36 | \sa QBoxSet, QBarCategoryAxis |
37 | */ |
38 | /*! |
39 | \fn QBoxPlotSeries::boxsetsAdded(const QList<QBoxSet *> &sets) |
40 | This signal is emitted when the list of box-and-whiskers items specified by \a sets |
41 | is added to the series. |
42 | */ |
43 | /*! |
44 | \fn QBoxPlotSeries::boxsetsRemoved(const QList<QBoxSet *> &sets) |
45 | This signal is emitted when the list of box-and-whiskers items specified by \a sets |
46 | is removed from the series. |
47 | */ |
48 | /*! |
49 | \fn QBoxPlotSeries::clicked(QBoxSet *boxset) |
50 | This signal is emitted when the user clicks the box-and-whiskers item specified by |
51 | \a boxset in the chart. |
52 | */ |
53 | /*! |
54 | \fn QBoxPlotSeries::pressed(QBoxSet *boxset) |
55 | This signal is emitted when the user clicks the box-and-whiskers item specified by |
56 | \a boxset in the chart and holds down the mouse button. |
57 | */ |
58 | /*! |
59 | \fn QBoxPlotSeries::released(QBoxSet *boxset) |
60 | This signal is emitted when the user releases the mouse press on the box-and-whiskers |
61 | item specified by \a boxset in the chart. |
62 | */ |
63 | /*! |
64 | \fn QBoxPlotSeries::doubleClicked(QBoxSet *boxset) |
65 | This signal is emitted when the user double-clicks the box-and-whiskers item specified by |
66 | \a boxset in the chart. |
67 | */ |
68 | /*! |
69 | \fn QBoxPlotSeries::hovered(bool status, QBoxSet *boxset) |
70 | This signal is emitted when a mouse is hovered over the box-and-whiskers item specified by |
71 | \a boxset in the chart. When the mouse moves over the item, \a status turns \c true, and |
72 | when the mouse moves away again, it turns \c false. |
73 | */ |
74 | /*! |
75 | \fn QBoxPlotSeries::countChanged() |
76 | This signal is emitted when the number of box-and-whiskers items in the series changes. |
77 | */ |
78 | /*! |
79 | \property QBoxPlotSeries::boxOutlineVisible |
80 | \brief The visibility of the box outline. |
81 | */ |
82 | /*! |
83 | \property QBoxPlotSeries::boxWidth |
84 | \brief The width of the box-and-whiskers item. The value indicates the relative |
85 | width of the item within its category. The value can be between 0.0 and 1.0. Negative values |
86 | are replaced with 0.0 and values greater than 1.0 are replaced with 1.0. |
87 | */ |
88 | /*! |
89 | \property QBoxPlotSeries::pen |
90 | \brief The pen used to draw the lines of the box-and-whiskers items. |
91 | */ |
92 | /*! |
93 | \property QBoxPlotSeries::brush |
94 | \brief The brush used to fill the boxes of the box-and-whiskers items. |
95 | */ |
96 | /*! |
97 | \property QBoxPlotSeries::count |
98 | \brief The number of box-and-whiskers items in a box plot series. |
99 | */ |
100 | |
101 | /*! |
102 | \fn void QBoxPlotSeries::boxOutlineVisibilityChanged() |
103 | This signal is emitted when the box outline visibility changes. |
104 | */ |
105 | |
106 | /*! |
107 | \fn void QBoxPlotSeries::boxWidthChanged() |
108 | This signal is emitted when the width of the box-and-whiskers item changes. |
109 | */ |
110 | /*! |
111 | \fn void QBoxPlotSeries::penChanged() |
112 | This signal is emitted when the pen used to draw the lines of the box-and-whiskers |
113 | items changes. |
114 | */ |
115 | /*! |
116 | \fn void QBoxPlotSeries::brushChanged() |
117 | This signal is emitted when the brush used to fill the boxes of the box-and-whiskers |
118 | items changes. |
119 | */ |
120 | /*! |
121 | \fn virtual SeriesType QBoxPlotSeries::type() const |
122 | Returns the type of the series. |
123 | \sa QAbstractSeries, SeriesType |
124 | */ |
125 | |
126 | /*! |
127 | Constructs an empty box plot series that is a QObject and a child of \a parent. |
128 | */ |
129 | QBoxPlotSeries::QBoxPlotSeries(QObject *parent) |
130 | : QAbstractSeries(*new QBoxPlotSeriesPrivate(this), parent) |
131 | { |
132 | } |
133 | |
134 | /*! |
135 | Removes the series from the chart. |
136 | */ |
137 | QBoxPlotSeries::~QBoxPlotSeries() |
138 | { |
139 | Q_D(QBoxPlotSeries); |
140 | if (d->m_chart) |
141 | d->m_chart->removeSeries(series: this); |
142 | } |
143 | |
144 | /*! |
145 | Adds a single box-and-whiskers item specified by \a set to the series and takes ownership of |
146 | it. If the item is null or it already belongs to the series, it will not be appended. |
147 | Returns \c true if appending succeeded. |
148 | */ |
149 | bool QBoxPlotSeries::append(QBoxSet *set) |
150 | { |
151 | Q_D(QBoxPlotSeries); |
152 | |
153 | bool success = d->append(set); |
154 | if (success) { |
155 | QList<QBoxSet *> sets; |
156 | sets.append(t: set); |
157 | set->setParent(this); |
158 | emit boxsetsAdded(sets); |
159 | emit countChanged(); |
160 | } |
161 | return success; |
162 | } |
163 | |
164 | /*! |
165 | Removes the box-and-whiskers item specified by \a set from the series and permanently |
166 | deletes it if the removal succeeds. Returns \c true if the item was removed. |
167 | */ |
168 | bool QBoxPlotSeries::remove(QBoxSet *set) |
169 | { |
170 | Q_D(QBoxPlotSeries); |
171 | bool success = d->remove(set); |
172 | if (success) { |
173 | QList<QBoxSet *> sets; |
174 | sets.append(t: set); |
175 | set->setParent(0); |
176 | emit boxsetsRemoved(sets); |
177 | emit countChanged(); |
178 | delete set; |
179 | set = 0; |
180 | } |
181 | return success; |
182 | } |
183 | |
184 | /*! |
185 | Takes the box-and-whiskers item specified by \a set from the series. Does not delete the |
186 | item. |
187 | |
188 | \note The series remains the item's parent object. You must set the parent object to take |
189 | full ownership. |
190 | |
191 | Returns \c true if the take operation succeeds. |
192 | |
193 | */ |
194 | bool QBoxPlotSeries::take(QBoxSet *set) |
195 | { |
196 | Q_D(QBoxPlotSeries); |
197 | |
198 | bool success = d->remove(set); |
199 | if (success) { |
200 | QList<QBoxSet *> sets; |
201 | sets.append(t: set); |
202 | emit boxsetsRemoved(sets); |
203 | emit countChanged(); |
204 | } |
205 | return success; |
206 | } |
207 | |
208 | /*! |
209 | Adds a list of box-and-whiskers items specified by \a sets to the series and takes ownership of |
210 | them. If the list is null or the items already belong to the series, it will not be appended. |
211 | Returns \c true if appending succeeded. |
212 | */ |
213 | bool QBoxPlotSeries::append(const QList<QBoxSet *> &sets) |
214 | { |
215 | Q_D(QBoxPlotSeries); |
216 | bool success = d->append(sets); |
217 | if (success) { |
218 | emit boxsetsAdded(sets); |
219 | emit countChanged(); |
220 | } |
221 | return success; |
222 | } |
223 | |
224 | /*! |
225 | Inserts a box-and-whiskers item specified by \a set to a series at the position specified by |
226 | \a index and takes ownership of the item. If the item is null or already belongs to the series, |
227 | it will not be appended. Returns \c true if inserting succeeds. |
228 | */ |
229 | bool QBoxPlotSeries::insert(int index, QBoxSet *set) |
230 | { |
231 | Q_D(QBoxPlotSeries); |
232 | bool success = d->insert(index, set); |
233 | if (success) { |
234 | QList<QBoxSet *> sets; |
235 | sets.append(t: set); |
236 | emit boxsetsAdded(sets); |
237 | emit countChanged(); |
238 | } |
239 | return success; |
240 | } |
241 | |
242 | /*! |
243 | Removes all box-and-whiskers items from the series and permanently deletes them. |
244 | */ |
245 | void QBoxPlotSeries::clear() |
246 | { |
247 | Q_D(QBoxPlotSeries); |
248 | QList<QBoxSet *> sets = boxSets(); |
249 | bool success = d->remove(sets); |
250 | if (success) { |
251 | emit boxsetsRemoved(sets); |
252 | emit countChanged(); |
253 | foreach (QBoxSet *set, sets) |
254 | delete set; |
255 | } |
256 | } |
257 | |
258 | /*! |
259 | Returns the number of box-and-whiskers items in a box plot series. |
260 | */ |
261 | int QBoxPlotSeries::count() const |
262 | { |
263 | Q_D(const QBoxPlotSeries); |
264 | return d->m_boxSets.size(); |
265 | } |
266 | |
267 | /*! |
268 | Returns a list of box-and-whiskers items in a box plot series. Keeps the ownership of the items. |
269 | */ |
270 | QList<QBoxSet *> QBoxPlotSeries::boxSets() const |
271 | { |
272 | Q_D(const QBoxPlotSeries); |
273 | return d->m_boxSets; |
274 | } |
275 | |
276 | /* |
277 | Returns QAbstractSeries::SeriesTypeBoxPlot. |
278 | */ |
279 | QAbstractSeries::SeriesType QBoxPlotSeries::type() const |
280 | { |
281 | return QAbstractSeries::SeriesTypeBoxPlot; |
282 | } |
283 | |
284 | void QBoxPlotSeries::setBoxOutlineVisible(bool visible) |
285 | { |
286 | Q_D(QBoxPlotSeries); |
287 | |
288 | if (d->m_boxOutlineVisible != visible) { |
289 | d->m_boxOutlineVisible = visible; |
290 | emit d->updated(); |
291 | emit boxOutlineVisibilityChanged(); |
292 | } |
293 | } |
294 | |
295 | bool QBoxPlotSeries::boxOutlineVisible() |
296 | { |
297 | Q_D(QBoxPlotSeries); |
298 | |
299 | return d->m_boxOutlineVisible; |
300 | } |
301 | |
302 | void QBoxPlotSeries::setBoxWidth(qreal width) |
303 | { |
304 | Q_D(QBoxPlotSeries); |
305 | |
306 | if (width != d->m_boxWidth) { |
307 | if (width < 0.0) |
308 | width = 0.0; |
309 | if (width > 1.0) |
310 | width = 1.0; |
311 | d->m_boxWidth = width; |
312 | emit d->updatedLayout(); |
313 | emit boxWidthChanged(); |
314 | } |
315 | } |
316 | |
317 | qreal QBoxPlotSeries::boxWidth() |
318 | { |
319 | Q_D(QBoxPlotSeries); |
320 | |
321 | return d->m_boxWidth; |
322 | } |
323 | |
324 | void QBoxPlotSeries::setBrush(const QBrush &brush) |
325 | { |
326 | Q_D(QBoxPlotSeries); |
327 | |
328 | if (d->m_brush != brush) { |
329 | d->m_brush = brush; |
330 | emit d->updated(); |
331 | emit brushChanged(); |
332 | } |
333 | } |
334 | |
335 | QBrush QBoxPlotSeries::brush() const |
336 | { |
337 | Q_D(const QBoxPlotSeries); |
338 | |
339 | return d->m_brush; |
340 | } |
341 | |
342 | void QBoxPlotSeries::setPen(const QPen &pen) |
343 | { |
344 | Q_D(QBoxPlotSeries); |
345 | |
346 | if (d->m_pen != pen) { |
347 | d->m_pen = pen; |
348 | emit d->updated(); |
349 | emit penChanged(); |
350 | } |
351 | } |
352 | |
353 | QPen QBoxPlotSeries::pen() const |
354 | { |
355 | Q_D(const QBoxPlotSeries); |
356 | |
357 | return d->m_pen; |
358 | } |
359 | |
360 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
361 | |
362 | QBoxPlotSeriesPrivate::QBoxPlotSeriesPrivate(QBoxPlotSeries *q) |
363 | : QAbstractSeriesPrivate(q), |
364 | m_pen(QChartPrivate::defaultPen()), |
365 | m_brush(QChartPrivate::defaultBrush()), |
366 | m_boxOutlineVisible(true), |
367 | m_boxWidth(0.5) |
368 | { |
369 | } |
370 | |
371 | QBoxPlotSeriesPrivate::~QBoxPlotSeriesPrivate() |
372 | { |
373 | disconnect(sender: this, signal: 0, receiver: 0, member: 0); |
374 | } |
375 | |
376 | void QBoxPlotSeriesPrivate::initializeDomain() |
377 | { |
378 | qreal minX(domain()->minX()); |
379 | qreal minY(domain()->minY()); |
380 | qreal maxX(domain()->maxX()); |
381 | qreal maxY(domain()->maxY()); |
382 | |
383 | qreal x = m_boxSets.size(); |
384 | minX = qMin(a: minX, b: qreal(-0.5)); |
385 | minY = qMin(a: minY, b: min()); |
386 | maxX = qMax(a: maxX, b: x - qreal(0.5)); |
387 | maxY = qMax(a: maxY, b: max()); |
388 | |
389 | domain()->setRange(minX, maxX, minY, maxY); |
390 | } |
391 | |
392 | void QBoxPlotSeriesPrivate::initializeAxes() |
393 | { |
394 | foreach (QAbstractAxis* axis, m_axes) { |
395 | if (axis->type() == QAbstractAxis::AxisTypeBarCategory) { |
396 | if (axis->orientation() == Qt::Horizontal) |
397 | populateCategories(axis: qobject_cast<QBarCategoryAxis *>(object: axis)); |
398 | } |
399 | } |
400 | } |
401 | |
402 | QAbstractAxis::AxisType QBoxPlotSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const |
403 | { |
404 | if (orientation == Qt::Horizontal) |
405 | return QAbstractAxis::AxisTypeBarCategory; |
406 | |
407 | return QAbstractAxis::AxisTypeValue; |
408 | } |
409 | |
410 | QAbstractAxis* QBoxPlotSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const |
411 | { |
412 | if (defaultAxisType(orientation) == QAbstractAxis::AxisTypeBarCategory) |
413 | return new QBarCategoryAxis; |
414 | else |
415 | return new QValueAxis; |
416 | } |
417 | |
418 | void QBoxPlotSeriesPrivate::populateCategories(QBarCategoryAxis *axis) |
419 | { |
420 | QStringList categories; |
421 | if (axis->categories().isEmpty()) { |
422 | for (int i(1); i < m_boxSets.size() + 1; i++) { |
423 | QBoxSet *set = m_boxSets.at(i: i - 1); |
424 | if (set->label().isEmpty()) |
425 | categories << presenter()->numberToString(value: i); |
426 | else |
427 | categories << set->label(); |
428 | } |
429 | axis->append(categories); |
430 | } |
431 | } |
432 | |
433 | void QBoxPlotSeriesPrivate::initializeGraphics(QGraphicsItem *parent) |
434 | { |
435 | Q_Q(QBoxPlotSeries); |
436 | |
437 | BoxPlotChartItem *boxPlot = new BoxPlotChartItem(q, parent); |
438 | m_item.reset(p: boxPlot); |
439 | QAbstractSeriesPrivate::initializeGraphics(parent); |
440 | |
441 | if (m_chart) { |
442 | connect(sender: m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries*)), receiver: this, SLOT(handleSeriesChange(QAbstractSeries*))); |
443 | connect(sender: m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries*)), receiver: this, SLOT(handleSeriesRemove(QAbstractSeries*))); |
444 | |
445 | QList<QAbstractSeries *> serieses = m_chart->series(); |
446 | |
447 | // Tries to find this series from the Chart's list of series and deduce the index |
448 | int index = 0; |
449 | foreach (QAbstractSeries *s, serieses) { |
450 | if (s->type() == QAbstractSeries::SeriesTypeBoxPlot) { |
451 | if (q == static_cast<QBoxPlotSeries *>(s)) { |
452 | boxPlot->m_seriesIndex = index; |
453 | m_index = index; |
454 | } |
455 | index++; |
456 | } |
457 | } |
458 | boxPlot->m_seriesCount = index; |
459 | } |
460 | |
461 | // Make BoxPlotChartItem to instantiate box & whisker items |
462 | boxPlot->handleDataStructureChanged(); |
463 | } |
464 | |
465 | void QBoxPlotSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced) |
466 | { |
467 | Q_Q(QBoxPlotSeries); |
468 | |
469 | const QList<QGradient> gradients = theme->seriesGradients(); |
470 | |
471 | if (forced || QChartPrivate::defaultBrush() == m_brush) { |
472 | QColor brushColor = ChartThemeManager::colorAt(gradient: gradients.at(i: index % gradients.size()), pos: 0.5); |
473 | q->setBrush(brushColor); |
474 | } |
475 | |
476 | if (forced || QChartPrivate::defaultPen() == m_pen) { |
477 | QPen pen = theme->outlinePen(); |
478 | pen.setCosmetic(true); |
479 | q->setPen(pen); |
480 | } |
481 | } |
482 | |
483 | void QBoxPlotSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, int duration, |
484 | QEasingCurve &curve) |
485 | { |
486 | BoxPlotChartItem *item = static_cast<BoxPlotChartItem *>(m_item.get()); |
487 | Q_ASSERT(item); |
488 | if (item->animation()) |
489 | item->animation()->stopAndDestroyLater(); |
490 | |
491 | if (options.testFlag(flag: QChart::SeriesAnimations)) |
492 | m_animation = new BoxPlotAnimation(item, duration, curve); |
493 | else |
494 | m_animation = 0; |
495 | item->setAnimation(m_animation); |
496 | |
497 | QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); |
498 | |
499 | // Make BoxPlotChartItem to instantiate box & whisker items |
500 | item->handleDataStructureChanged(); |
501 | } |
502 | |
503 | QList<QLegendMarker*> QBoxPlotSeriesPrivate::createLegendMarkers(QLegend *legend) |
504 | { |
505 | Q_Q(QBoxPlotSeries); |
506 | QList<QLegendMarker *> list; |
507 | return list << new QBoxPlotLegendMarker(q, legend); |
508 | } |
509 | |
510 | void QBoxPlotSeriesPrivate::handleSeriesRemove(QAbstractSeries *series) |
511 | { |
512 | Q_Q(QBoxPlotSeries); |
513 | |
514 | QBoxPlotSeries *removedSeries = static_cast<QBoxPlotSeries *>(series); |
515 | |
516 | if (q == removedSeries) { |
517 | if (m_animation) |
518 | m_animation->stopAll(); |
519 | QObject::disconnect(sender: m_chart->d_ptr->m_dataset, signal: 0, receiver: this, member: 0); |
520 | } |
521 | |
522 | // Test if series removed is me, then don't do anything |
523 | if (q != removedSeries) { |
524 | BoxPlotChartItem *item = static_cast<BoxPlotChartItem *>(m_item.get()); |
525 | if (item) { |
526 | item->m_seriesCount = item->m_seriesCount - 1; |
527 | if (removedSeries->d_func()->m_index < m_index) { |
528 | m_index--; |
529 | item->m_seriesIndex = m_index; |
530 | } |
531 | |
532 | item->handleDataStructureChanged(); |
533 | } |
534 | } |
535 | } |
536 | |
537 | void QBoxPlotSeriesPrivate::handleSeriesChange(QAbstractSeries *series) |
538 | { |
539 | Q_UNUSED(series); |
540 | |
541 | Q_Q(QBoxPlotSeries); |
542 | |
543 | BoxPlotChartItem *boxPlot = static_cast<BoxPlotChartItem *>(m_item.get()); |
544 | |
545 | if (m_chart) { |
546 | QList<QAbstractSeries *> serieses = m_chart->series(); |
547 | |
548 | // Tries to find this series from the Chart's list of series and deduce the index |
549 | int index = 0; |
550 | foreach (QAbstractSeries *s, serieses) { |
551 | if (s->type() == QAbstractSeries::SeriesTypeBoxPlot) { |
552 | if (q == static_cast<QBoxPlotSeries *>(s)) { |
553 | boxPlot->m_seriesIndex = index; |
554 | m_index = index; |
555 | } |
556 | index++; |
557 | } |
558 | } |
559 | boxPlot->m_seriesCount = index; |
560 | } |
561 | |
562 | boxPlot->handleDataStructureChanged(); |
563 | } |
564 | |
565 | bool QBoxPlotSeriesPrivate::append(QBoxSet *set) |
566 | { |
567 | if (m_boxSets.contains(t: set) || (set == 0) || set->d_ptr->m_series) |
568 | return false; // Fail if set is already in list or set is null. |
569 | |
570 | m_boxSets.append(t: set); |
571 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(updatedLayout()), receiver: this, SIGNAL(updatedLayout())); |
572 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(updatedBox()), receiver: this, SIGNAL(updatedBoxes())); |
573 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(restructuredBox()), receiver: this, SIGNAL(restructuredBoxes())); |
574 | set->d_ptr->m_series = this; |
575 | |
576 | emit restructuredBoxes(); // this notifies boxplotchartitem |
577 | return true; |
578 | } |
579 | |
580 | bool QBoxPlotSeriesPrivate::remove(QBoxSet *set) |
581 | { |
582 | if (!m_boxSets.contains(t: set)) |
583 | return false; // Fail if set is not in list |
584 | |
585 | set->d_ptr->m_series = 0; |
586 | m_boxSets.removeOne(t: set); |
587 | QObject::disconnect(sender: set->d_ptr.data(), SIGNAL(updatedLayout()), receiver: this, SIGNAL(updatedLayout())); |
588 | QObject::disconnect(sender: set->d_ptr.data(), SIGNAL(updatedBox()), receiver: this, SIGNAL(updatedBoxes())); |
589 | QObject::disconnect(sender: set->d_ptr.data(), SIGNAL(restructuredBox()), receiver: this, SIGNAL(restructuredBoxes())); |
590 | |
591 | emit restructuredBoxes(); // this notifies boxplotchartitem |
592 | return true; |
593 | } |
594 | |
595 | bool QBoxPlotSeriesPrivate::append(const QList<QBoxSet *> &sets) |
596 | { |
597 | for (auto *set : sets) { |
598 | if ((set == 0) || m_boxSets.contains(t: set) || set->d_ptr->m_series) |
599 | return false; // Fail if any of the sets is null or is already appended. |
600 | if (sets.count(t: set) != 1) |
601 | return false; // Also fail if same set is more than once in given list. |
602 | } |
603 | |
604 | for (auto *set : sets) { |
605 | m_boxSets.append(t: set); |
606 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(updatedLayout()), receiver: this, SIGNAL(updatedLayout())); |
607 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(updatedBox()), receiver: this, SIGNAL(updatedBoxes())); |
608 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(restructuredBox()), receiver: this, SIGNAL(restructuredBoxes())); |
609 | set->d_ptr->m_series = this; |
610 | } |
611 | |
612 | emit restructuredBoxes(); // this notifies boxplotchartitem |
613 | return true; |
614 | } |
615 | |
616 | bool QBoxPlotSeriesPrivate::remove(const QList<QBoxSet *> &sets) |
617 | { |
618 | if (sets.size() == 0) |
619 | return false; |
620 | |
621 | for (auto *set : sets) { |
622 | if ((set == 0) || (!m_boxSets.contains(t: set))) |
623 | return false; // Fail if any of the sets is null or is not in series |
624 | if (sets.count(t: set) != 1) |
625 | return false; // Also fail if same set is more than once in given list. |
626 | } |
627 | |
628 | for (auto *set : sets) { |
629 | set->d_ptr->m_series = 0; |
630 | m_boxSets.removeOne(t: set); |
631 | QObject::disconnect(sender: set->d_ptr.data(), SIGNAL(updatedLayout()), receiver: this, SIGNAL(updatedLayout())); |
632 | QObject::disconnect(sender: set->d_ptr.data(), SIGNAL(updatedBox()), receiver: this, SIGNAL(updatedBoxes())); |
633 | QObject::disconnect(sender: set->d_ptr.data(), SIGNAL(restructuredBox()), receiver: this, SIGNAL(restructuredBoxes())); |
634 | } |
635 | |
636 | emit restructuredBoxes(); // this notifies boxplotchartitem |
637 | |
638 | return true; |
639 | } |
640 | |
641 | bool QBoxPlotSeriesPrivate::insert(int index, QBoxSet *set) |
642 | { |
643 | if ((m_boxSets.contains(t: set)) || (set == 0) || set->d_ptr->m_series) |
644 | return false; // Fail if set is already in list or set is null. |
645 | |
646 | m_boxSets.insert(i: index, t: set); |
647 | set->d_ptr->m_series = this; |
648 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(updatedLayout()), receiver: this, SIGNAL(updatedLayout())); |
649 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(updatedBox()), receiver: this, SIGNAL(updatedBoxes())); |
650 | QObject::connect(sender: set->d_ptr.data(), SIGNAL(restructuredBox()), receiver: this, SIGNAL(restructuredBoxes())); |
651 | |
652 | emit restructuredBoxes(); // this notifies boxplotchartitem |
653 | return true; |
654 | } |
655 | |
656 | QBoxSet *QBoxPlotSeriesPrivate::boxSetAt(int index) |
657 | { |
658 | return m_boxSets.at(i: index); |
659 | } |
660 | |
661 | qreal QBoxPlotSeriesPrivate::min() |
662 | { |
663 | if (m_boxSets.size() <= 0) |
664 | return 0; |
665 | |
666 | qreal min = m_boxSets.at(i: 0)->at(index: 0); |
667 | |
668 | foreach (QBoxSet *set, m_boxSets) { |
669 | for (int i = 0; i < 5; i++) { |
670 | if (set->at(index: i) < min) |
671 | min = set->at(index: i); |
672 | } |
673 | } |
674 | |
675 | return min; |
676 | } |
677 | |
678 | qreal QBoxPlotSeriesPrivate::max() |
679 | { |
680 | if (m_boxSets.size() <= 0) |
681 | return 0; |
682 | |
683 | qreal max = m_boxSets.at(i: 0)->at(index: 0); |
684 | |
685 | foreach (QBoxSet *set, m_boxSets) { |
686 | for (int i = 0; i < 5; i++) { |
687 | if (set->at(index: i) > max) |
688 | max = set->at(index: i); |
689 | } |
690 | } |
691 | |
692 | return max; |
693 | } |
694 | |
695 | QT_END_NAMESPACE |
696 | |
697 | #include "moc_qboxplotseries.cpp" |
698 | #include "moc_qboxplotseries_p.cpp" |
699 | |