| 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 <private/boxplotchartitem_p.h> | 
| 7 | #include <private/qboxplotseries_p.h> | 
| 8 | #include <private/bar_p.h> | 
| 9 | #include <private/qboxset_p.h> | 
| 10 | #include <private/qabstractbarseries_p.h> | 
| 11 | #include <QtCharts/QBoxSet> | 
| 12 | #include <private/boxwhiskers_p.h> | 
| 13 | #include <QtGui/QPainter> | 
| 14 |  | 
| 15 | QT_BEGIN_NAMESPACE | 
| 16 |  | 
| 17 | BoxPlotChartItem::BoxPlotChartItem(QBoxPlotSeries *series, QGraphicsItem *item) : | 
| 18 |     ChartItem(series->d_func(), item), | 
| 19 |     m_series(series), | 
| 20 |     m_animation(0) | 
| 21 | { | 
| 22 |     setAcceptedMouseButtons({}); | 
| 23 |     connect(sender: series, SIGNAL(boxsetsRemoved(QList<QBoxSet *>)), receiver: this, SLOT(handleBoxsetRemove(QList<QBoxSet *>))); | 
| 24 |     connect(sender: series, SIGNAL(visibleChanged()), receiver: this, SLOT(handleSeriesVisibleChanged())); | 
| 25 |     connect(sender: series, SIGNAL(opacityChanged()), receiver: this, SLOT(handleOpacityChanged())); | 
| 26 |     connect(sender: series->d_func(), SIGNAL(restructuredBoxes()), receiver: this, SLOT(handleDataStructureChanged())); | 
| 27 |     connect(sender: series->d_func(), SIGNAL(updatedLayout()), receiver: this, SLOT(handleLayoutChanged())); | 
| 28 |     connect(sender: series->d_func(), SIGNAL(updatedBoxes()), receiver: this, SLOT(handleUpdatedBars())); | 
| 29 |     connect(sender: series->d_func(), SIGNAL(updated()), receiver: this, SLOT(handleUpdatedBars())); | 
| 30 |     // QBoxPlotSeriesPrivate calls handleDataStructureChanged(), don't do it here | 
| 31 |     setZValue(ChartPresenter::BoxPlotSeriesZValue); | 
| 32 | } | 
| 33 |  | 
| 34 | BoxPlotChartItem::~BoxPlotChartItem() | 
| 35 | { | 
| 36 | } | 
| 37 |  | 
| 38 | void BoxPlotChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) | 
| 39 | { | 
| 40 |     Q_UNUSED(painter); | 
| 41 |     Q_UNUSED(option); | 
| 42 |     Q_UNUSED(widget); | 
| 43 | } | 
| 44 |  | 
| 45 | void BoxPlotChartItem::setAnimation(BoxPlotAnimation *animation) | 
| 46 | { | 
| 47 |     m_animation = animation; | 
| 48 |     if (m_animation) { | 
| 49 |         foreach (BoxWhiskers *item, m_boxTable.values()) | 
| 50 |             m_animation->addBox(box: item); | 
| 51 |         handleDomainUpdated(); | 
| 52 |     } | 
| 53 | } | 
| 54 |  | 
| 55 | void BoxPlotChartItem::handleSeriesVisibleChanged() | 
| 56 | { | 
| 57 |     setVisible(m_series->isVisible()); | 
| 58 | } | 
| 59 |  | 
| 60 | void BoxPlotChartItem::handleOpacityChanged() | 
| 61 | { | 
| 62 |     setOpacity(m_series->opacity()); | 
| 63 | } | 
| 64 |  | 
| 65 | void BoxPlotChartItem::handleDataStructureChanged() | 
| 66 | { | 
| 67 |     int setCount = m_series->count(); | 
| 68 |  | 
| 69 |     for (int s = 0; s < setCount; s++) { | 
| 70 |         QBoxSet *set = m_series->d_func()->boxSetAt(index: s); | 
| 71 |  | 
| 72 |         BoxWhiskers *box = m_boxTable.value(key: set); | 
| 73 |         if (!box) { | 
| 74 |             // Item is not yet created, make a box and add it to hash table | 
| 75 |             box = new BoxWhiskers(set, domain(), this); | 
| 76 |             m_boxTable.insert(key: set, value: box); | 
| 77 |             connect(sender: box, SIGNAL(clicked(QBoxSet *)), receiver: m_series, SIGNAL(clicked(QBoxSet *))); | 
| 78 |             connect(sender: box, SIGNAL(hovered(bool, QBoxSet *)), receiver: m_series, SIGNAL(hovered(bool, QBoxSet *))); | 
| 79 |             connect(sender: box, SIGNAL(pressed(QBoxSet *)), receiver: m_series, SIGNAL(pressed(QBoxSet *))); | 
| 80 |             connect(sender: box, SIGNAL(released(QBoxSet *)), receiver: m_series, SIGNAL(released(QBoxSet *))); | 
| 81 |             connect(sender: box, SIGNAL(doubleClicked(QBoxSet *)), | 
| 82 |                     receiver: m_series, SIGNAL(doubleClicked(QBoxSet *))); | 
| 83 |             connect(sender: box, SIGNAL(clicked(QBoxSet *)), receiver: set, SIGNAL(clicked())); | 
| 84 |             connect(sender: box, SIGNAL(hovered(bool, QBoxSet *)), receiver: set, SIGNAL(hovered(bool))); | 
| 85 |             connect(sender: box, SIGNAL(pressed(QBoxSet *)), receiver: set, SIGNAL(pressed())); | 
| 86 |             connect(sender: box, SIGNAL(released(QBoxSet *)), receiver: set, SIGNAL(released())); | 
| 87 |             connect(sender: box, SIGNAL(doubleClicked(QBoxSet *)), receiver: set, SIGNAL(doubleClicked())); | 
| 88 |  | 
| 89 |             // Set the decorative issues for the newly created box | 
| 90 |             // so that the brush and pen already defined for the set are kept. | 
| 91 |             if (set->brush() == Qt::NoBrush) | 
| 92 |                 box->setBrush(m_series->brush()); | 
| 93 |             else | 
| 94 |                 box->setBrush(set->brush()); | 
| 95 |             if (set->pen() == Qt::NoPen) | 
| 96 |                 box->setPen(m_series->pen()); | 
| 97 |             else | 
| 98 |                 box->setPen(set->pen()); | 
| 99 |             box->setBoxOutlined(m_series->boxOutlineVisible()); | 
| 100 |             box->setBoxWidth(m_series->boxWidth()); | 
| 101 |         } | 
| 102 |         updateBoxGeometry(box, index: s); | 
| 103 |  | 
| 104 |         box->updateGeometry(domain: domain()); | 
| 105 |  | 
| 106 |         if (m_animation) | 
| 107 |             m_animation->addBox(box); | 
| 108 |     } | 
| 109 |  | 
| 110 |     handleDomainUpdated(); | 
| 111 | } | 
| 112 |  | 
| 113 | void BoxPlotChartItem::handleUpdatedBars() | 
| 114 | { | 
| 115 |     foreach (BoxWhiskers *item, m_boxTable.values()) { | 
| 116 |         item->setBrush(m_series->brush()); | 
| 117 |         item->setPen(m_series->pen()); | 
| 118 |         item->setBoxOutlined(m_series->boxOutlineVisible()); | 
| 119 |         item->setBoxWidth(m_series->boxWidth()); | 
| 120 |     } | 
| 121 |     // Override with QBoxSet specific settings | 
| 122 |     foreach (QBoxSet *set, m_boxTable.keys()) { | 
| 123 |         if (set->brush().style() != Qt::NoBrush) | 
| 124 |             m_boxTable.value(key: set)->setBrush(set->brush()); | 
| 125 |         if (set->pen().style() != Qt::NoPen) | 
| 126 |             m_boxTable.value(key: set)->setPen(set->pen()); | 
| 127 |     } | 
| 128 | } | 
| 129 |  | 
| 130 | void BoxPlotChartItem::handleBoxsetRemove(const QList<QBoxSet *> &barSets) | 
| 131 | { | 
| 132 |     for (auto *set : barSets) { | 
| 133 |         BoxWhiskers *boxItem = m_boxTable.value(key: set); | 
| 134 |         m_boxTable.remove(key: set); | 
| 135 |         delete boxItem; | 
| 136 |     } | 
| 137 | } | 
| 138 |  | 
| 139 | void BoxPlotChartItem::handleDomainUpdated() | 
| 140 | { | 
| 141 |     if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0)) | 
| 142 |         return; | 
| 143 |  | 
| 144 |     // Set my bounding rect to same as domain size. Add one pixel at the top (-1.0) and the bottom as 0.0 would | 
| 145 |     // snip a bit off from the whisker at the grid line | 
| 146 |     m_boundingRect.setRect(ax: 0.0, ay: -1.0, aaw: domain()->size().width(), aah: domain()->size().height() + 1.0); | 
| 147 |  | 
| 148 |     foreach (BoxWhiskers *item, m_boxTable.values()) { | 
| 149 |         item->updateGeometry(domain: domain()); | 
| 150 |  | 
| 151 |         // If the animation is set, start the animation for each BoxWhisker item | 
| 152 |         if (m_animation) | 
| 153 |             presenter()->startAnimation(animation: m_animation->boxAnimation(box: item)); | 
| 154 |     } | 
| 155 | } | 
| 156 |  | 
| 157 | void BoxPlotChartItem::handleLayoutChanged() | 
| 158 | { | 
| 159 |     foreach (BoxWhiskers *item, m_boxTable.values()) { | 
| 160 |         if (m_animation) | 
| 161 |             m_animation->setAnimationStart(item); | 
| 162 |  | 
| 163 |         item->setBoxWidth(m_series->boxWidth()); | 
| 164 |  | 
| 165 |         bool dirty = updateBoxGeometry(box: item, index: item->m_data.m_index); | 
| 166 |         if (dirty && m_animation) | 
| 167 |             presenter()->startAnimation(animation: m_animation->boxChangeAnimation(box: item)); | 
| 168 |         else | 
| 169 |             item->updateGeometry(domain: domain()); | 
| 170 |     } | 
| 171 | } | 
| 172 |  | 
| 173 | QRectF BoxPlotChartItem::boundingRect() const | 
| 174 | { | 
| 175 |     return m_boundingRect; | 
| 176 | } | 
| 177 |  | 
| 178 | void BoxPlotChartItem::initializeLayout() | 
| 179 | { | 
| 180 | } | 
| 181 |  | 
| 182 | QList<QRectF> BoxPlotChartItem::calculateLayout() | 
| 183 | { | 
| 184 |     return QList<QRectF>(); | 
| 185 | } | 
| 186 |  | 
| 187 | bool BoxPlotChartItem::updateBoxGeometry(BoxWhiskers *box, int index) | 
| 188 | { | 
| 189 |     bool changed = false; | 
| 190 |  | 
| 191 |     QBoxSet *set = m_series->d_func()->boxSetAt(index); | 
| 192 |     BoxWhiskersData &data = box->m_data; | 
| 193 |  | 
| 194 |     if ((data.m_lowerExtreme != set->at(index: 0)) || (data.m_lowerQuartile != set->at(index: 1)) || | 
| 195 |         (data.m_median != set->at(index: 2)) || (data.m_upperQuartile != set->at(index: 3)) || (data.m_upperExtreme != set->at(index: 4))) { | 
| 196 |         changed = true; | 
| 197 |     } | 
| 198 |  | 
| 199 |     data.m_lowerExtreme = set->at(index: 0); | 
| 200 |     data.m_lowerQuartile = set->at(index: 1); | 
| 201 |     data.m_median = set->at(index: 2); | 
| 202 |     data.m_upperQuartile = set->at(index: 3); | 
| 203 |     data.m_upperExtreme = set->at(index: 4); | 
| 204 |     data.m_index = index; | 
| 205 |     data.m_boxItems = m_series->count(); | 
| 206 |  | 
| 207 |     data.m_maxX = domain()->maxX(); | 
| 208 |     data.m_minX = domain()->minX(); | 
| 209 |     data.m_maxY = domain()->maxY(); | 
| 210 |     data.m_minY = domain()->minY(); | 
| 211 |  | 
| 212 |     data.m_seriesIndex = m_seriesIndex; | 
| 213 |     data.m_seriesCount = m_seriesCount; | 
| 214 |  | 
| 215 |     return changed; | 
| 216 | } | 
| 217 |  | 
| 218 | QT_END_NAMESPACE | 
| 219 |  | 
| 220 | #include "moc_boxplotchartitem_p.cpp" | 
| 221 |  |