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