diff options
Diffstat (limited to 'src/charts/candlestickchart/qcandlestickmodelmapper.cpp')
-rw-r--r-- | src/charts/candlestickchart/qcandlestickmodelmapper.cpp | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/src/charts/candlestickchart/qcandlestickmodelmapper.cpp b/src/charts/candlestickchart/qcandlestickmodelmapper.cpp new file mode 100644 index 00000000..a66e82ab --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickmodelmapper.cpp @@ -0,0 +1,706 @@ +/**************************************************************************** +** +** 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/QCandlestickModelMapper> +#include <QtCharts/QCandlestickSeries> +#include <QtCharts/QCandlestickSet> +#include <QtCore/QAbstractItemModel> +#include <private/qcandlestickmodelmapper_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +/*! + \class QCandlestickModelMapper + \since 5.8 + \inmodule Qt Charts + \brief Abstract model mapper class for candlestick series. + + Model mappers allow the use of a QAbstractItemModel-derived model as a data source for a chart + series, creating a connection between a QCandlestickSeries and the model object. A model mapper + maintains an equal size across all \l {QCandlestickSet} {QCandlestickSets}. + + \note The model used must support adding and removing rows/columns and modifying the data of the + cells. +*/ + +/*! + \property QCandlestickModelMapper::model + \brief Defines the model that is used by the mapper. +*/ + +/*! + \property QCandlestickModelMapper::series + \brief Defines the QCandlestickSeries object that is used by the mapper. + + \note All data in the series is discarded when it is set to the mapper. When a new series is + specified, the old series is disconnected (preserving its data). +*/ + +/*! + \fn Qt::Orientation QCandlestickModelMapper::orientation() const + Returns the orientation that is used when QCandlestickModelMapper accesses the model. This + determines whether the consecutive values of the set are read from rows (Qt::Horizontal) or from + columns (Qt::Vertical). +*/ + +/*! + \fn void QCandlestickModelMapper::modelReplaced() + \brief Emitted when the model, to which the mapper is connected, has changed. + \sa model +*/ + +/*! + \fn void QCandlestickModelMapper::seriesReplaced() + \brief Emitted when the series to which mapper is connected to has changed. + \sa series +*/ + +/*! + Constructs a model mapper object as a child of \a parent. +*/ +QCandlestickModelMapper::QCandlestickModelMapper(QObject *parent) + : QObject(parent), + d_ptr(new QCandlestickModelMapperPrivate(this)) +{ +} + +void QCandlestickModelMapper::setModel(QAbstractItemModel *model) +{ + Q_D(QCandlestickModelMapper); + + if (d->m_model == model) + return; + + if (d->m_model) + disconnect(d->m_model, 0, d, 0); + + d->m_model = model; + emit modelReplaced(); + + if (!d->m_model) + return; + + d->initializeCandlestickFromModel(); + // connect signals from the model + connect(d->m_model, SIGNAL(modelReset()), d, SLOT(initializeCandlestickFromModel())); + connect(d->m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), + d, SLOT(modelDataUpdated(QModelIndex, QModelIndex))); + connect(d->m_model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), + d, SLOT(modelHeaderDataUpdated(Qt::Orientation, int, int))); + connect(d->m_model, SIGNAL(rowsInserted(QModelIndex, int, int)), + d, SLOT(modelRowsInserted(QModelIndex, int, int))); + connect(d->m_model, SIGNAL(rowsRemoved(QModelIndex, int, int)), + d, SLOT(modelRowsRemoved(QModelIndex, int, int))); + connect(d->m_model, SIGNAL(columnsInserted(QModelIndex, int, int)), + d, SLOT(modelColumnsInserted(QModelIndex, int, int))); + connect(d->m_model, SIGNAL(columnsRemoved(QModelIndex, int, int)), + d, SLOT(modelColumnsRemoved(QModelIndex, int, int))); + connect(d->m_model, SIGNAL(destroyed()), d, SLOT(modelDestroyed())); +} + +QAbstractItemModel *QCandlestickModelMapper::model() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_model; +} + +void QCandlestickModelMapper::setSeries(QCandlestickSeries *series) +{ + Q_D(QCandlestickModelMapper); + + if (d->m_series == series) + return; + + if (d->m_series) + disconnect(d->m_series, 0, d, 0); + + d->m_series = series; + emit seriesReplaced(); + + if (!d->m_series) + return; + + d->initializeCandlestickFromModel(); + // connect the signals from the series + connect(d->m_series, SIGNAL(candlestickSetsAdded(QList<QCandlestickSet *>)), + d, SLOT(candlestickSetsAdded(QList<QCandlestickSet *>))); + connect(d->m_series, SIGNAL(candlestickSetsRemoved(QList<QCandlestickSet*>)), + d, SLOT(candlestickSetsRemoved(QList<QCandlestickSet *>))); + connect(d->m_series, SIGNAL(destroyed()), d, SLOT(seriesDestroyed())); +} + +QCandlestickSeries *QCandlestickModelMapper::series() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_series; +} + +/*! + Sets the row/column of the model that contains the \a timestamp values of the sets in the + series. Default value is -1 (invalid mapping). +*/ +void QCandlestickModelMapper::setTimestamp(int timestamp) +{ + Q_D(QCandlestickModelMapper); + + timestamp = qMax(timestamp, -1); + + if (d->m_timestamp == timestamp) + return; + + d->m_timestamp = timestamp; + emit d->timestampChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the row/column of the model that contains the timestamp values of the sets in the + series. Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::timestamp() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_timestamp; +} + +/*! + Sets the row/column of the model that contains the \a open values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +void QCandlestickModelMapper::setOpen(int open) +{ + Q_D(QCandlestickModelMapper); + + open = qMax(open, -1); + + if (d->m_open == open) + return; + + d->m_open = open; + emit d->openChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the row/column of the model that contains the open values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::open() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_open; +} + +/*! + Sets the row/column of the model that contains the \a high values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +void QCandlestickModelMapper::setHigh(int high) +{ + Q_D(QCandlestickModelMapper); + + high = qMax(high, -1); + + if (d->m_high == high) + return; + + d->m_high = high; + emit d->highChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the row/column of the model that contains the high values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::high() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_high; +} + +/*! + Sets the row/column of the model that contains the \a low values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +void QCandlestickModelMapper::setLow(int low) +{ + Q_D(QCandlestickModelMapper); + + low = qMax(low, -1); + + if (d->m_low == low) + return; + + d->m_low = low; + emit d->lowChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the row/column of the model that contains the low values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::low() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_low; +} + +/*! + Sets the row/column of the model that contains the \a close values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +void QCandlestickModelMapper::setClose(int close) +{ + Q_D(QCandlestickModelMapper); + + close = qMax(close, -1); + + if (d->m_close == close) + return; + + d->m_close = close; + emit d->closeChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the row/column of the model that contains the close values of the sets in the series. + Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::close() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_close; +} + +/*! + Sets the section of the model that is used as the data source for the first candlestick set. + Parameter \a firstCandlestickSetSection specifies the section of the model. Default value is -1. +*/ +void QCandlestickModelMapper::setFirstCandlestickSetSection(int firstCandlestickSetSection) +{ + Q_D(QCandlestickModelMapper); + + firstCandlestickSetSection = qMax(firstCandlestickSetSection, -1); + + if (d->m_firstCandlestickSetSection == firstCandlestickSetSection) + return; + + d->m_firstCandlestickSetSection = firstCandlestickSetSection; + emit d->firstCandlestickSetSectionChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the section of the model that is used as the data source for the first candlestick set. + Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::firstCandlestickSetSection() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_firstCandlestickSetSection; +} + +/*! + Sets the section of the model that is used as the data source for the last candlestick set. + Parameter \a lastCandlestickSetSection specifies the section of the model. Default value is -1. +*/ +void QCandlestickModelMapper::setLastCandlestickSetSection(int lastCandlestickSetSection) +{ + Q_D(QCandlestickModelMapper); + + lastCandlestickSetSection = qMax(lastCandlestickSetSection, -1); + + if (d->m_lastCandlestickSetSection == lastCandlestickSetSection) + return; + + d->m_lastCandlestickSetSection = lastCandlestickSetSection; + emit d->lastCandlestickSetSectionChanged(); + d->initializeCandlestickFromModel(); +} + +/*! + Returns the section of the model that is used as the data source for the last candlestick set. + Default value is -1 (invalid mapping). +*/ +int QCandlestickModelMapper::lastCandlestickSetSection() const +{ + Q_D(const QCandlestickModelMapper); + + return d->m_lastCandlestickSetSection; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +QCandlestickModelMapperPrivate::QCandlestickModelMapperPrivate(QCandlestickModelMapper *q) + : QObject(q), + m_model(nullptr), + m_series(nullptr), + m_timestamp(-1), + m_open(-1), + m_high(-1), + m_low(-1), + m_close(-1), + m_firstCandlestickSetSection(-1), + m_lastCandlestickSetSection(-1), + m_modelSignalsBlock(false), + m_seriesSignalsBlock(false), + q_ptr(q) +{ +} + +void QCandlestickModelMapperPrivate::initializeCandlestickFromModel() +{ + if (!m_model || !m_series) + return; + + blockSeriesSignals(); + // clear current content + m_series->clear(); + m_candlestickSets.clear(); + + // create the initial candlestick sets + QList<QCandlestickSet *> candlestickSets; + for (int i = m_firstCandlestickSetSection; i <= m_lastCandlestickSetSection; ++i) { + QModelIndex timestampIndex = candlestickModelIndex(i, m_timestamp); + QModelIndex openIndex = candlestickModelIndex(i, m_open); + QModelIndex highIndex = candlestickModelIndex(i, m_high); + QModelIndex lowIndex = candlestickModelIndex(i, m_low); + QModelIndex closeIndex = candlestickModelIndex(i, m_close); + if (timestampIndex.isValid() + && openIndex.isValid() + && highIndex.isValid() + && lowIndex.isValid() + && closeIndex.isValid()) { + QCandlestickSet *set = new QCandlestickSet(); + set->setTimestamp(m_model->data(timestampIndex, Qt::DisplayRole).toReal()); + set->setOpen(m_model->data(openIndex, Qt::DisplayRole).toReal()); + set->setHigh(m_model->data(highIndex, Qt::DisplayRole).toReal()); + set->setLow(m_model->data(lowIndex, Qt::DisplayRole).toReal()); + set->setClose(m_model->data(closeIndex, Qt::DisplayRole).toReal()); + + connect(set, SIGNAL(timestampChanged()), this, SLOT(candlestickSetChanged())); + connect(set, SIGNAL(openChanged()), this, SLOT(candlestickSetChanged())); + connect(set, SIGNAL(highChanged()), this, SLOT(candlestickSetChanged())); + connect(set, SIGNAL(lowChanged()), this, SLOT(candlestickSetChanged())); + connect(set, SIGNAL(closeChanged()), this, SLOT(candlestickSetChanged())); + + candlestickSets.append(set); + } else { + break; + } + } + m_series->append(candlestickSets); + m_candlestickSets.append(candlestickSets); + blockSeriesSignals(false); +} + +void QCandlestickModelMapperPrivate::modelDataUpdated(QModelIndex topLeft, QModelIndex bottomRight) +{ + Q_Q(QCandlestickModelMapper); + + if (!m_model || !m_series) + return; + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + QModelIndex index; + for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { + for (int column = topLeft.column(); column <= bottomRight.column(); ++column) { + index = topLeft.sibling(row, column); + QCandlestickSet *set = candlestickSet(index); + if (set) { + int pos = (q->orientation() == Qt::Vertical) ? row : column; + if (pos == m_timestamp) + set->setTimestamp(m_model->data(index).toReal()); + else if (pos == m_open) + set->setOpen(m_model->data(index).toReal()); + else if (pos == m_high) + set->setHigh(m_model->data(index).toReal()); + else if (pos == m_low) + set->setLow(m_model->data(index).toReal()); + else if (pos == m_close) + set->setClose(m_model->data(index).toReal()); + } + } + } + blockSeriesSignals(false); +} + +void QCandlestickModelMapperPrivate::modelHeaderDataUpdated(Qt::Orientation orientation, int first, + int last) +{ + Q_UNUSED(orientation); + Q_UNUSED(first); + Q_UNUSED(last); +} + +void QCandlestickModelMapperPrivate::modelRowsInserted(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + + Q_Q(QCandlestickModelMapper); + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (q->orientation() == Qt::Vertical) + insertData(start, end); + else if (start <= m_firstCandlestickSetSection || start <= m_lastCandlestickSetSection) + initializeCandlestickFromModel(); // if the changes affect the map - reinitialize + blockSeriesSignals(false); +} + +void QCandlestickModelMapperPrivate::modelRowsRemoved(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + + Q_Q(QCandlestickModelMapper); + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (q->orientation() == Qt::Vertical) + removeData(start, end); + else if (start <= m_firstCandlestickSetSection || start <= m_lastCandlestickSetSection) + initializeCandlestickFromModel(); // if the changes affect the map - reinitialize + blockSeriesSignals(false); +} + +void QCandlestickModelMapperPrivate::modelColumnsInserted(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + + Q_Q(QCandlestickModelMapper); + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (q->orientation() == Qt::Horizontal) + insertData(start, end); + else if (start <= m_firstCandlestickSetSection || start <= m_lastCandlestickSetSection) + initializeCandlestickFromModel(); // if the changes affect the map - reinitialize + blockSeriesSignals(false); +} + +void QCandlestickModelMapperPrivate::modelColumnsRemoved(QModelIndex parent, int start, int end) +{ + Q_UNUSED(parent) + + Q_Q(QCandlestickModelMapper); + + if (m_modelSignalsBlock) + return; + + blockSeriesSignals(); + if (q->orientation() == Qt::Horizontal) + removeData(start, end); + else if (start <= m_firstCandlestickSetSection || start <= m_lastCandlestickSetSection) + initializeCandlestickFromModel(); // if the changes affect the map - reinitialize + blockSeriesSignals(false); +} + +void QCandlestickModelMapperPrivate::modelDestroyed() +{ + m_model = 0; +} + +void QCandlestickModelMapperPrivate::candlestickSetsAdded(const QList<QCandlestickSet *> &sets) +{ + Q_Q(QCandlestickModelMapper); + + if (m_seriesSignalsBlock) + return; + + if (sets.isEmpty()) + return; + + int firstIndex = m_series->candlestickSets().indexOf(sets.at(0)); + if (firstIndex == -1) + return; + + m_lastCandlestickSetSection += sets.count(); + + blockModelSignals(); + if (q->orientation() == Qt::Vertical) + m_model->insertColumns(firstIndex + m_firstCandlestickSetSection, sets.count()); + else + m_model->insertRows(firstIndex + m_firstCandlestickSetSection, sets.count()); + + for (int i = 0; i < sets.count(); ++i) { + int section = i + firstIndex + m_firstCandlestickSetSection; + m_model->setData(candlestickModelIndex(section, m_timestamp), sets.at(i)->timestamp()); + m_model->setData(candlestickModelIndex(section, m_open), sets.at(i)->open()); + m_model->setData(candlestickModelIndex(section, m_high), sets.at(i)->high()); + m_model->setData(candlestickModelIndex(section, m_low), sets.at(i)->low()); + m_model->setData(candlestickModelIndex(section, m_close), sets.at(i)->close()); + } + blockModelSignals(false); + initializeCandlestickFromModel(); +} + +void QCandlestickModelMapperPrivate::candlestickSetsRemoved(const QList<QCandlestickSet *> &sets) +{ + Q_Q(QCandlestickModelMapper); + + if (m_seriesSignalsBlock) + return; + + if (sets.isEmpty()) + return; + + int firstIndex = m_candlestickSets.indexOf(sets.at(0)); + if (firstIndex == -1) + return; + + m_lastCandlestickSetSection -= sets.count(); + + for (int i = firstIndex + sets.count() - 1; i >= firstIndex; --i) + m_candlestickSets.removeAt(i); + + blockModelSignals(); + if (q->orientation() == Qt::Vertical) + m_model->removeColumns(firstIndex + m_firstCandlestickSetSection, sets.count()); + else + m_model->removeRows(firstIndex + m_firstCandlestickSetSection, sets.count()); + blockModelSignals(false); + initializeCandlestickFromModel(); +} + +void QCandlestickModelMapperPrivate::candlestickSetChanged() +{ + if (m_seriesSignalsBlock) + return; + + QCandlestickSet *set = qobject_cast<QCandlestickSet *>(QObject::sender()); + if (!set) + return; + + int section = m_series->candlestickSets().indexOf(set); + if (section < 0) + return; + + section += m_firstCandlestickSetSection; + + blockModelSignals(); + m_model->setData(candlestickModelIndex(section, m_timestamp), set->timestamp()); + m_model->setData(candlestickModelIndex(section, m_open), set->open()); + m_model->setData(candlestickModelIndex(section, m_high), set->high()); + m_model->setData(candlestickModelIndex(section, m_low), set->low()); + m_model->setData(candlestickModelIndex(section, m_close), set->close()); + blockModelSignals(false); +} + +void QCandlestickModelMapperPrivate::seriesDestroyed() +{ + m_series = 0; +} + +QCandlestickSet *QCandlestickModelMapperPrivate::candlestickSet(QModelIndex index) +{ + Q_Q(QCandlestickModelMapper); + + if (!index.isValid()) + return 0; + + int section = (q->orientation() == Qt::Vertical) ? index.column() : index.row(); + int pos = (q->orientation() == Qt::Vertical) ? index.row() : index.column(); + + if (section < m_firstCandlestickSetSection || section > m_lastCandlestickSetSection) + return 0; // This part of model has not been mapped to any candlestick set. + + if (pos != m_timestamp && pos != m_open && pos != m_high && pos != m_low && pos != m_close) + return 0; // This part of model has not been mapped to any candlestick set. + + return m_series->candlestickSets().at(section - m_firstCandlestickSetSection); +} + +QModelIndex QCandlestickModelMapperPrivate::candlestickModelIndex(int section, int pos) +{ + Q_Q(QCandlestickModelMapper); + + if (section < m_firstCandlestickSetSection || section > m_lastCandlestickSetSection) + return QModelIndex(); // invalid + + if (pos != m_timestamp && pos != m_open && pos != m_high && pos != m_low && pos != m_close) + return QModelIndex(); // invalid + + if (q->orientation() == Qt::Vertical) + return m_model->index(pos, section); + else + return m_model->index(section, pos); +} + +void QCandlestickModelMapperPrivate::insertData(int start, int end) +{ + Q_UNUSED(start) + Q_UNUSED(end) + + // Currently candlestickchart needs to be fully recalculated when change is made. + initializeCandlestickFromModel(); +} + +void QCandlestickModelMapperPrivate::removeData(int start, int end) +{ + Q_UNUSED(start) + Q_UNUSED(end) + + // Currently candlestickchart needs to be fully recalculated when change is made. + initializeCandlestickFromModel(); +} + +void QCandlestickModelMapperPrivate::blockModelSignals(bool block) +{ + m_modelSignalsBlock = block; +} + +void QCandlestickModelMapperPrivate::blockSeriesSignals(bool block) +{ + m_seriesSignalsBlock = block; +} + +#include "moc_qcandlestickmodelmapper.cpp" +#include "moc_qcandlestickmodelmapper_p.cpp" + +QT_CHARTS_END_NAMESPACE |