| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Charts module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtCharts/QCandlestickSeries> |
| #include <QtCharts/QCandlestickSet> |
| #include <private/candlestickchartitem_p.h> |
| #include <private/candlestick_p.h> |
| #include <private/candlestickdata_p.h> |
| #include <private/qcandlestickseries_p.h> |
| #include <private/candlestickanimation_p.h> |
| |
| QT_CHARTS_BEGIN_NAMESPACE |
| |
| CandlestickChartItem::CandlestickChartItem(QCandlestickSeries *series, QGraphicsItem *item) |
| : ChartItem(series->d_func(), item), |
| m_series(series), |
| m_seriesIndex(0), |
| m_seriesCount(0), |
| m_timePeriod(0.0), |
| m_animation(nullptr) |
| { |
| setAcceptedMouseButtons(0); |
| connect(series, SIGNAL(candlestickSetsAdded(QList<QCandlestickSet *>)), |
| this, SLOT(handleCandlestickSetsAdd(QList<QCandlestickSet *>))); |
| connect(series, SIGNAL(candlestickSetsRemoved(QList<QCandlestickSet *>)), |
| this, SLOT(handleCandlestickSetsRemove(QList<QCandlestickSet *>))); |
| |
| connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleCandlesticksUpdated())); |
| connect(series->d_func(), SIGNAL(updatedLayout()), this, SLOT(handleLayoutUpdated())); |
| connect(series->d_func(), SIGNAL(updatedCandlesticks()), |
| this, SLOT(handleCandlesticksUpdated())); |
| |
| setZValue(ChartPresenter::CandlestickSeriesZValue); |
| |
| handleCandlestickSetsAdd(m_series->sets()); |
| } |
| |
| CandlestickChartItem::~CandlestickChartItem() |
| { |
| } |
| |
| void CandlestickChartItem::setAnimation(CandlestickAnimation *animation) |
| { |
| m_animation = animation; |
| |
| if (m_animation) { |
| foreach (Candlestick *item, m_candlesticks.values()) |
| m_animation->addCandlestick(item); |
| |
| handleDomainUpdated(); |
| } |
| } |
| |
| QRectF CandlestickChartItem::boundingRect() const |
| { |
| return m_boundingRect; |
| } |
| |
| void CandlestickChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, |
| QWidget *widget) |
| { |
| Q_UNUSED(painter); |
| Q_UNUSED(option); |
| Q_UNUSED(widget); |
| } |
| |
| void CandlestickChartItem::handleDomainUpdated() |
| { |
| if ((domain()->size().width() <= 0) || (domain()->size().height() <= 0)) |
| return; |
| |
| // Set bounding rectangle to same as domain size. Add one pixel at the top (-1.0) and the bottom |
| // as 0.0 would snip a bit off from the wick at the grid line. |
| m_boundingRect.setRect(0.0, -1.0, domain()->size().width(), domain()->size().height() + 1.0); |
| |
| foreach (Candlestick *item, m_candlesticks.values()) { |
| item->updateGeometry(domain()); |
| |
| if (m_animation) |
| presenter()->startAnimation(m_animation->candlestickAnimation(item)); |
| } |
| } |
| |
| void CandlestickChartItem::handleLayoutUpdated() |
| { |
| bool timestampChanged = false; |
| foreach (QCandlestickSet *set, m_candlesticks.keys()) { |
| qreal oldTimestamp = m_candlesticks.value(set)->m_data.m_timestamp; |
| qreal newTimestamp = set->timestamp(); |
| if (Q_UNLIKELY(oldTimestamp != newTimestamp)) { |
| removeTimestamp(oldTimestamp); |
| addTimestamp(newTimestamp); |
| timestampChanged = true; |
| } |
| } |
| if (timestampChanged) |
| updateTimePeriod(); |
| |
| foreach (Candlestick *item, m_candlesticks.values()) { |
| if (m_animation) |
| m_animation->setAnimationStart(item); |
| |
| item->setTimePeriod(m_timePeriod); |
| item->setMaximumColumnWidth(m_series->maximumColumnWidth()); |
| item->setMinimumColumnWidth(m_series->minimumColumnWidth()); |
| item->setBodyWidth(m_series->bodyWidth()); |
| item->setCapsWidth(m_series->capsWidth()); |
| |
| bool dirty = updateCandlestickGeometry(item, item->m_data.m_index); |
| if (dirty && m_animation) |
| presenter()->startAnimation(m_animation->candlestickChangeAnimation(item)); |
| else |
| item->updateGeometry(domain()); |
| } |
| } |
| |
| void CandlestickChartItem::handleCandlesticksUpdated() |
| { |
| foreach (QCandlestickSet *set, m_candlesticks.keys()) |
| updateCandlestickAppearance(m_candlesticks.value(set), set); |
| } |
| |
| void CandlestickChartItem::handleCandlestickSeriesChange() |
| { |
| int seriesIndex = 0; |
| int seriesCount = 0; |
| |
| int index = 0; |
| foreach (QAbstractSeries *series, m_series->chart()->series()) { |
| if (series->type() == QAbstractSeries::SeriesTypeCandlestick) { |
| if (m_series == static_cast<QCandlestickSeries *>(series)) |
| seriesIndex = index; |
| index++; |
| } |
| } |
| seriesCount = index; |
| |
| bool changed; |
| if ((m_seriesIndex != seriesIndex) || (m_seriesCount != seriesCount)) |
| changed = true; |
| else |
| changed = false; |
| |
| if (changed) { |
| m_seriesIndex = seriesIndex; |
| m_seriesCount = seriesCount; |
| handleDataStructureChanged(); |
| } |
| } |
| |
| void CandlestickChartItem::handleCandlestickSetsAdd(const QList<QCandlestickSet *> &sets) |
| { |
| foreach (QCandlestickSet *set, sets) { |
| Candlestick *item = m_candlesticks.value(set, 0); |
| if (item) { |
| qWarning() << "There is already a candlestick for this set in the hash"; |
| continue; |
| } |
| |
| item = new Candlestick(set, domain(), this); |
| m_candlesticks.insert(set, item); |
| addTimestamp(set->timestamp()); |
| |
| connect(item, SIGNAL(clicked(QCandlestickSet *)), |
| m_series, SIGNAL(clicked(QCandlestickSet *))); |
| connect(item, SIGNAL(hovered(bool, QCandlestickSet *)), |
| m_series, SIGNAL(hovered(bool, QCandlestickSet *))); |
| connect(item, SIGNAL(pressed(QCandlestickSet *)), |
| m_series, SIGNAL(pressed(QCandlestickSet *))); |
| connect(item, SIGNAL(released(QCandlestickSet *)), |
| m_series, SIGNAL(released(QCandlestickSet *))); |
| connect(item, SIGNAL(doubleClicked(QCandlestickSet *)), |
| m_series, SIGNAL(doubleClicked(QCandlestickSet *))); |
| connect(item, SIGNAL(clicked(QCandlestickSet *)), set, SIGNAL(clicked())); |
| connect(item, SIGNAL(hovered(bool, QCandlestickSet *)), set, SIGNAL(hovered(bool))); |
| connect(item, SIGNAL(pressed(QCandlestickSet *)), set, SIGNAL(pressed())); |
| connect(item, SIGNAL(released(QCandlestickSet *)), set, SIGNAL(released())); |
| connect(item, SIGNAL(doubleClicked(QCandlestickSet *)), set, SIGNAL(doubleClicked())); |
| } |
| |
| handleDataStructureChanged(); |
| } |
| |
| void CandlestickChartItem::handleCandlestickSetsRemove(const QList<QCandlestickSet *> &sets) |
| { |
| foreach (QCandlestickSet *set, sets) { |
| Candlestick *item = m_candlesticks.value(set); |
| |
| m_candlesticks.remove(set); |
| removeTimestamp(set->timestamp()); |
| |
| if (m_animation) { |
| ChartAnimation *animation = m_animation->candlestickAnimation(item); |
| if (animation) { |
| animation->stop(); |
| delete animation; |
| } |
| } |
| |
| delete item; |
| } |
| |
| handleDataStructureChanged(); |
| } |
| |
| void CandlestickChartItem::handleDataStructureChanged() |
| { |
| updateTimePeriod(); |
| |
| for (int i = 0; i < m_series->count(); ++i) { |
| QCandlestickSet *set = m_series->sets().at(i); |
| Candlestick *item = m_candlesticks.value(set); |
| |
| updateCandlestickGeometry(item, i); |
| updateCandlestickAppearance(item, set); |
| |
| item->updateGeometry(domain()); |
| |
| if (m_animation) |
| m_animation->addCandlestick(item); |
| } |
| |
| handleDomainUpdated(); |
| } |
| |
| bool CandlestickChartItem::updateCandlestickGeometry(Candlestick *item, int index) |
| { |
| bool changed = false; |
| |
| QCandlestickSet *set = m_series->sets().at(index); |
| CandlestickData &data = item->m_data; |
| |
| if ((data.m_open != set->open()) |
| || (data.m_high != set->high()) |
| || (data.m_low != set->low()) |
| || (data.m_close != set->close())) { |
| changed = true; |
| } |
| |
| data.m_timestamp = set->timestamp(); |
| data.m_open = set->open(); |
| data.m_high = set->high(); |
| data.m_low = set->low(); |
| data.m_close = set->close(); |
| data.m_index = index; |
| |
| data.m_maxX = domain()->maxX(); |
| data.m_minX = domain()->minX(); |
| data.m_maxY = domain()->maxY(); |
| data.m_minY = domain()->minY(); |
| |
| data.m_series = m_series; |
| data.m_seriesIndex = m_seriesIndex; |
| data.m_seriesCount = m_seriesCount; |
| |
| return changed; |
| } |
| |
| void CandlestickChartItem::updateCandlestickAppearance(Candlestick *item, QCandlestickSet *set) |
| { |
| item->setTimePeriod(m_timePeriod); |
| item->setMaximumColumnWidth(m_series->maximumColumnWidth()); |
| item->setMinimumColumnWidth(m_series->minimumColumnWidth()); |
| item->setBodyWidth(m_series->bodyWidth()); |
| item->setBodyOutlineVisible(m_series->bodyOutlineVisible()); |
| item->setCapsWidth(m_series->capsWidth()); |
| item->setCapsVisible(m_series->capsVisible()); |
| item->setIncreasingColor(m_series->increasingColor()); |
| item->setDecreasingColor(m_series->decreasingColor()); |
| |
| // Set the decorative issues for the candlestick so that |
| // the brush and pen already defined for the set are kept. |
| if (set->brush() == Qt::NoBrush) |
| item->setBrush(m_series->brush()); |
| else |
| item->setBrush(set->brush()); |
| |
| if (set->pen() == Qt::NoPen) |
| item->setPen(m_series->pen()); |
| else |
| item->setPen(set->pen()); |
| } |
| |
| void CandlestickChartItem::addTimestamp(qreal timestamp) |
| { |
| int index = 0; |
| for (int i = m_timestamps.count() - 1; i >= 0; --i) { |
| if (timestamp > m_timestamps.at(i)) { |
| index = i + 1; |
| break; |
| } |
| } |
| m_timestamps.insert(index, timestamp); |
| } |
| |
| void CandlestickChartItem::removeTimestamp(qreal timestamp) |
| { |
| m_timestamps.removeOne(timestamp); |
| } |
| |
| void CandlestickChartItem::updateTimePeriod() |
| { |
| if (m_timestamps.count() == 0) { |
| m_timePeriod = 0; |
| return; |
| } |
| |
| if (m_timestamps.count() == 1) { |
| m_timePeriod = qAbs(domain()->maxX() - domain()->minX()); |
| return; |
| } |
| |
| qreal timePeriod = qAbs(m_timestamps.at(1) - m_timestamps.at(0)); |
| for (int i = 1; i < m_timestamps.count(); ++i) { |
| timePeriod = qMin(timePeriod, qAbs(m_timestamps.at(i) - m_timestamps.at(i - 1))); |
| } |
| m_timePeriod = timePeriod; |
| } |
| |
| QT_CHARTS_END_NAMESPACE |
| |
| #include "moc_candlestickchartitem_p.cpp" |