/**************************************************************************** ** ** 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 #include #include #include #include #include #include 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) { connect(series, SIGNAL(candlestickSetsAdded(QList)), this, SLOT(handleCandlestickSetsAdd(QList))); connect(series, SIGNAL(candlestickSetsRemoved(QList)), this, SLOT(handleCandlestickSetsRemove(QList))); 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->candlestickSets()); } 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(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 &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 &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->candlestickSets().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->candlestickSets().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; } #include "moc_candlestickchartitem_p.cpp" QT_CHARTS_END_NAMESPACE