diff options
author | Alexander Mishin <apmishin@yandex.com> | 2016-02-09 21:49:53 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2016-05-25 11:47:43 +0000 |
commit | facc2941efbfb8c9f40e363f0ea881653f6b4393 (patch) | |
tree | ae5ef712e4abbe47d60704fd0978f7533c7edbdd | |
parent | e9a2e2a5e9c023c872e39e39fb2a8b0a31820577 (diff) |
Added candlestick chart type
- added QCandlestickSeries
- added QCandlestickSet
- added QCandlestickLegendMarker
- added model mappers
- added Candlestick, CandlestickChartItem, CandlestickData
- added SeriesTypeCandlestick to SeriesType enum
- added LegendMarkerTypeCandlestick to LegendMarkerType enum
- added candlestick chart example
- added QML candlestick chart example
- added candlestick tester
- added autotests
- added documentation
[ChangeLog][CandlestickChart] Added new chart type:
Candlestick Chart.
Task-number: QTBUG-50544
Change-Id: I17d18dfa23e0ea209bf51ab1e349585b9cb50a8f
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
76 files changed, 9376 insertions, 24 deletions
diff --git a/examples/charts/candlestickchart/acme_data.txt b/examples/charts/candlestickchart/acme_data.txt new file mode 100644 index 00000000..eaac318c --- /dev/null +++ b/examples/charts/candlestickchart/acme_data.txt @@ -0,0 +1,24 @@ +# Acme Ltd Historical Data (July 2015) +# timestamp, open, high, low, close +1435708800000 126.90 126.94 125.99 126.60 +1435795200000 126.69 126.69 126.69 126.69 +1436140800000 124.85 126.23 124.85 126.00 +1436227200000 125.89 126.15 123.77 125.69 +1436313600000 124.64 124.64 122.54 122.54 +1436400000000 123.85 124.06 119.22 120.07 +1436486400000 121.94 123.85 121.21 123.28 +1436745600000 125.03 125.76 124.32 125.66 +1436832000000 126.04 126.37 125.04 125.61 +1436918400000 125.72 127.15 125.58 126.82 +1437004800000 127.74 128.57 127.35 128.51 +1437091200000 129.08 129.62 128.31 129.62 +1437350400000 130.97 132.97 130.70 132.07 +1437436800000 132.85 132.92 130.32 130.75 +1437523200000 121.99 125.50 121.99 125.22 +1437609600000 126.20 127.09 125.06 125.16 +1437696000000 125.32 125.74 123.90 124.50 +1437955200000 123.09 123.61 122.12 122.77 +1438041600000 123.38 123.91 122.55 123.38 +1438128000000 123.15 123.50 122.27 122.99 +1438214400000 122.32 122.57 121.71 122.37 +1438300800000 122.60 122.64 120.91 121.30 diff --git a/examples/charts/candlestickchart/candlestickchart.pro b/examples/charts/candlestickchart/candlestickchart.pro new file mode 100644 index 00000000..9a334bd6 --- /dev/null +++ b/examples/charts/candlestickchart/candlestickchart.pro @@ -0,0 +1,14 @@ +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +TARGET = candlestickchart + +SOURCES += main.cpp \ + candlestickdatareader.cpp + +HEADERS += \ + candlestickdatareader.h + +RESOURCES += \ + candlestickdata.qrc diff --git a/examples/charts/candlestickchart/candlestickdata.qrc b/examples/charts/candlestickchart/candlestickdata.qrc new file mode 100644 index 00000000..e311cadb --- /dev/null +++ b/examples/charts/candlestickchart/candlestickdata.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file alias="acme">acme_data.txt</file> + </qresource> +</RCC> diff --git a/examples/charts/candlestickchart/candlestickdatareader.cpp b/examples/charts/candlestickchart/candlestickdatareader.cpp new file mode 100644 index 00000000..83228efd --- /dev/null +++ b/examples/charts/candlestickchart/candlestickdatareader.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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 "candlestickdatareader.h" + +CandlestickDataReader::CandlestickDataReader(QIODevice *device) + : QTextStream(device) +{ +} + +CandlestickDataReader::~CandlestickDataReader() +{ +} + +void CandlestickDataReader::readFile(QIODevice *device) +{ + QTextStream::setDevice(device); +} + +QCandlestickSet *CandlestickDataReader::readCandlestickSet() +{ + //! [1] + QString line = readLine(); + if (line.startsWith("#") || line.isEmpty()) + return 0; + //! [1] + + //! [2] + QStringList strList = line.split(" ", QString::SkipEmptyParts); + if (strList.count() != 5) + return 0; + //! [2] + + //! [3] + const qreal timestamp = strList.at(0).toDouble(); + const qreal open = strList.at(1).toDouble(); + const qreal high = strList.at(2).toDouble(); + const qreal low = strList.at(3).toDouble(); + const qreal close = strList.at(4).toDouble(); + //! [3] + + //! [4] + QCandlestickSet *candlestickSet = new QCandlestickSet(timestamp); + candlestickSet->setOpen(open); + candlestickSet->setHigh(high); + candlestickSet->setLow(low); + candlestickSet->setClose(close); + //! [4] + + return candlestickSet; +} diff --git a/examples/charts/candlestickchart/candlestickdatareader.h b/examples/charts/candlestickchart/candlestickdatareader.h new file mode 100644 index 00000000..aaa83bf2 --- /dev/null +++ b/examples/charts/candlestickchart/candlestickdatareader.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef CANDLESTICKDATAREADER_H +#define CANDLESTICKDATAREADER_H + +#include <QtCharts/QCandlestickSet> +#include <QtCore/QTextStream> + +QT_CHARTS_USE_NAMESPACE + +class CandlestickDataReader : public QTextStream +{ +public: + explicit CandlestickDataReader(QIODevice *device); + ~CandlestickDataReader(); + + void readFile(QIODevice *device); + QCandlestickSet *readCandlestickSet(); +}; + +#endif // CANDLESTICKDATAREADER_H diff --git a/examples/charts/candlestickchart/main.cpp b/examples/charts/candlestickchart/main.cpp new file mode 100644 index 00000000..243b0f61 --- /dev/null +++ b/examples/charts/candlestickchart/main.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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/QBarCategoryAxis> +#include <QtCharts/QCandlestickSeries> +#include <QtCharts/QChartView> +#include <QtCharts/QValueAxis> +#include <QtCore/QDateTime> +#include <QtWidgets/QApplication> +#include <QtWidgets/QMainWindow> + +#include "candlestickdatareader.h" + +QT_CHARTS_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + //! [1] + QCandlestickSeries *acmeSeries = new QCandlestickSeries(); + acmeSeries->setName("Acme Ltd"); + acmeSeries->setIncreasingColor(QColor(Qt::green)); + acmeSeries->setDecreasingColor(QColor(Qt::red)); + //! [1] + + //! [2] + QFile acmeData(":acme"); + if (!acmeData.open(QIODevice::ReadOnly | QIODevice::Text)) + return 1; + + QStringList categories; + + CandlestickDataReader dataReader(&acmeData); + while (!dataReader.atEnd()) { + QCandlestickSet *set = dataReader.readCandlestickSet(); + if (set) { + acmeSeries->append(set); + categories << QDateTime::fromMSecsSinceEpoch(set->timestamp()).toString("dd"); + } + } + //! [2] + + //! [3] + QChart *chart = new QChart(); + chart->addSeries(acmeSeries); + chart->setTitle("Acme Ltd Historical Data (July 2015)"); + chart->setAnimationOptions(QChart::SeriesAnimations); + //! [3] + + //! [4] + chart->createDefaultAxes(); + + QBarCategoryAxis *axisX = qobject_cast<QBarCategoryAxis *>(chart->axes(Qt::Horizontal).at(0)); + axisX->setCategories(categories); + + QValueAxis *axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).at(0)); + axisY->setMax(axisY->max() * 1.01); + axisY->setMin(axisY->min() * 0.99); + //! [4] + + //! [5] + chart->legend()->setVisible(true); + chart->legend()->setAlignment(Qt::AlignBottom); + //! [5] + + //! [6] + QChartView *chartView = new QChartView(chart); + chartView->setRenderHint(QPainter::Antialiasing); + //! [6] + + //! [7] + QMainWindow window; + window.setCentralWidget(chartView); + window.resize(800, 600); + window.show(); + //! [7] + + return a.exec(); +} diff --git a/examples/charts/charts.pro b/examples/charts/charts.pro index 5caa03c6..704d9cc0 100644 --- a/examples/charts/charts.pro +++ b/examples/charts/charts.pro @@ -14,6 +14,7 @@ SUBDIRS += areachart \ modeldata \ barchart \ boxplotchart \ + candlestickchart \ legend \ barmodelmapper \ lineandbar \ @@ -36,6 +37,7 @@ SUBDIRS += areachart \ qtHaveModule(quick) { SUBDIRS += qmlboxplot \ + qmlcandlestick \ qmlpiechart \ qmlweather \ qmlf1legends \ diff --git a/examples/charts/qmlcandlestick/main.cpp b/examples/charts/qmlcandlestick/main.cpp new file mode 100644 index 00000000..b399e15c --- /dev/null +++ b/examples/charts/qmlcandlestick/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** 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 <QtCore/QDir> +#include <QtQml/QQmlEngine> +#include <QtQuick/QQuickView> +#include <QtWidgets/QApplication> + +int main(int argc, char *argv[]) +{ + // Qt Charts uses Qt Graphics View Framework for drawing, therefore QApplication must be used. + QApplication app(argc, argv); + + QQuickView viewer; + + // The following are needed to make examples run without having to install the module + // in desktop environments. +#ifdef Q_OS_WIN + QString extraImportPath(QStringLiteral("%1/../../../../%2")); +#else + QString extraImportPath(QStringLiteral("%1/../../../%2")); +#endif + viewer.engine()->addImportPath(extraImportPath.arg(QApplication::applicationDirPath(), + QString::fromLatin1("qml"))); + QObject::connect(viewer.engine(), &QQmlEngine::quit, &viewer, &QWindow::close); + + viewer.setTitle(QStringLiteral("QML Candlestick")); + viewer.setSource(QUrl("qrc:/qml/qmlcandlestick/main.qml")); + viewer.setResizeMode(QQuickView::SizeRootObjectToView); + viewer.show(); + + return app.exec(); +} diff --git a/examples/charts/qmlcandlestick/qml/qmlcandlestick/main.qml b/examples/charts/qmlcandlestick/qml/qmlcandlestick/main.qml new file mode 100644 index 00000000..18fca4fe --- /dev/null +++ b/examples/charts/qmlcandlestick/qml/qmlcandlestick/main.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtCharts 2.2 + +ChartView { + title: "Candlestick series" + width: 800 + height: 600 + theme: ChartView.ChartThemeLight + legend.alignment: Qt.AlignBottom + antialiasing: true + + CandlestickSeries { + name: "Acme Ltd." + increasingColor: "green" + decreasingColor: "red" + + CandlestickSet { timestamp: 1435708800000; open: 6.90; high: 6.94; low: 5.99; close: 6.60 } + CandlestickSet { timestamp: 1435795200000; open: 6.69; high: 6.69; low: 6.69; close: 6.69 } + CandlestickSet { timestamp: 1436140800000; open: 4.85; high: 6.23; low: 4.85; close: 6.00 } + CandlestickSet { timestamp: 1436227200000; open: 5.89; high: 6.15; low: 3.77; close: 5.69 } + CandlestickSet { timestamp: 1436313600000; open: 4.64; high: 4.64; low: 2.54; close: 2.54 } + } +} diff --git a/examples/charts/qmlcandlestick/qmlcandlestick.pro b/examples/charts/qmlcandlestick/qmlcandlestick.pro new file mode 100644 index 00000000..58e63037 --- /dev/null +++ b/examples/charts/qmlcandlestick/qmlcandlestick.pro @@ -0,0 +1,10 @@ +!include( ../examples.pri ) { + error( "Couldn't find the examples.pri file!" ) +} + +RESOURCES += resources.qrc + +SOURCES += main.cpp + +OTHER_FILES += \ + qml/qmlcandlestick/main.qml diff --git a/examples/charts/qmlcandlestick/resources.qrc b/examples/charts/qmlcandlestick/resources.qrc new file mode 100644 index 00000000..ccfab44e --- /dev/null +++ b/examples/charts/qmlcandlestick/resources.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>qml/qmlcandlestick/main.qml</file> + </qresource> +</RCC> diff --git a/src/charts/animations/animations.pri b/src/charts/animations/animations.pri index 28b6db1e..9a5c86e8 100644 --- a/src/charts/animations/animations.pri +++ b/src/charts/animations/animations.pri @@ -11,6 +11,8 @@ SOURCES += \ $$PWD/scatteranimation.cpp \ $$PWD/boxplotanimation.cpp \ $$PWD/boxwhiskersanimation.cpp \ + $$PWD/candlestickanimation.cpp \ + $$PWD/candlestickbodywicksanimation.cpp \ $$PWD/chartanimation.cpp PRIVATE_HEADERS += \ @@ -23,4 +25,6 @@ PRIVATE_HEADERS += \ $$PWD/baranimation_p.h \ $$PWD/scatteranimation_p.h \ $$PWD/boxplotanimation_p.h \ - $$PWD/boxwhiskersanimation_p.h + $$PWD/boxwhiskersanimation_p.h \ + $$PWD/candlestickanimation_p.h \ + $$PWD/candlestickbodywicksanimation_p.h diff --git a/src/charts/animations/candlestickanimation.cpp b/src/charts/animations/candlestickanimation.cpp new file mode 100644 index 00000000..5e141943 --- /dev/null +++ b/src/charts/animations/candlestickanimation.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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 <private/candlestick_p.h> +#include <private/candlestickanimation_p.h> +#include <private/candlestickbodywicksanimation_p.h> +#include <private/candlestickchartitem_p.h> +#include <private/candlestickdata_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +CandlestickAnimation::CandlestickAnimation(CandlestickChartItem *item, int duration, + QEasingCurve &curve) + : QObject(item), + m_item(item), + m_animationDuration(duration), + m_animationCurve(curve) +{ +} + +CandlestickAnimation::~CandlestickAnimation() +{ +} + +void CandlestickAnimation::addCandlestick(Candlestick *candlestick) +{ + CandlestickBodyWicksAnimation *animation = m_animations.value(candlestick, 0); + if (!animation) { + animation = new CandlestickBodyWicksAnimation(candlestick, this, m_animationDuration, + m_animationCurve); + m_animations.insert(candlestick, animation); + + qreal median = (candlestick->m_data.m_open + candlestick->m_data.m_close) / 2; + CandlestickData start; + start.m_open = median; + start.m_high = median; + start.m_low = median; + start.m_close = median; + animation->setup(start, candlestick->m_data); + } else { + animation->stop(); + animation->setEndData(candlestick->m_data); + } +} + +ChartAnimation *CandlestickAnimation::candlestickAnimation(Candlestick *candlestick) +{ + CandlestickBodyWicksAnimation *animation = m_animations.value(candlestick, 0); + if (animation) + animation->m_changeAnimation = false; + + return animation; +} + +ChartAnimation *CandlestickAnimation::candlestickChangeAnimation(Candlestick *candlestick) +{ + CandlestickBodyWicksAnimation *animation = m_animations.value(candlestick, 0); + if (animation) { + animation->m_changeAnimation = true; + animation->setEndData(candlestick->m_data); + } + + return animation; +} + +void CandlestickAnimation::setAnimationStart(Candlestick *candlestick) +{ + CandlestickBodyWicksAnimation *animation = m_animations.value(candlestick, 0); + if (animation) + animation->setStartData(candlestick->m_data); +} + +void CandlestickAnimation::stopAll() +{ + foreach (Candlestick *candlestick, m_animations.keys()) { + CandlestickBodyWicksAnimation *animation = m_animations.value(candlestick, 0); + if (animation) + animation->stopAndDestroyLater(); + m_animations.remove(candlestick); + } +} + +void CandlestickAnimation::removeCandlestickAnimation(Candlestick *candlestick) +{ + m_animations.remove(candlestick); +} + +#include "moc_candlestickanimation_p.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/animations/candlestickanimation_p.h b/src/charts/animations/candlestickanimation_p.h new file mode 100644 index 00000000..cb894cd7 --- /dev/null +++ b/src/charts/animations/candlestickanimation_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CANDLESTICKANIMATION_P_H +#define CANDLESTICKANIMATION_P_H + +#include <private/chartanimation_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +class Candlestick; +class CandlestickChartItem; +class CandlestickBodyWicksAnimation; + +class CandlestickAnimation : public QObject +{ + Q_OBJECT + +public: + CandlestickAnimation(CandlestickChartItem *item, int duration, QEasingCurve &curve); + ~CandlestickAnimation(); + + void addCandlestick(Candlestick *candlestick); + ChartAnimation *candlestickAnimation(Candlestick *candlestick); + ChartAnimation *candlestickChangeAnimation(Candlestick *candlestick); + + void setAnimationStart(Candlestick *candlestick); + void stopAll(); + void removeCandlestickAnimation(Candlestick *candlestick); + +protected: + QHash<Candlestick *, CandlestickBodyWicksAnimation *> m_animations; + CandlestickChartItem *m_item; + int m_animationDuration; + QEasingCurve m_animationCurve; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // CANDLESTICKANIMATION_P_H diff --git a/src/charts/animations/candlestickbodywicksanimation.cpp b/src/charts/animations/candlestickbodywicksanimation.cpp new file mode 100644 index 00000000..615f43c3 --- /dev/null +++ b/src/charts/animations/candlestickbodywicksanimation.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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 <private/candlestick_p.h> +#include <private/candlestickanimation_p.h> +#include <private/candlestickbodywicksanimation_p.h> + +Q_DECLARE_METATYPE(QVector<QRectF>) +Q_DECLARE_METATYPE(QT_CHARTS_NAMESPACE::CandlestickData) +Q_DECLARE_METATYPE(qreal) + +QT_CHARTS_BEGIN_NAMESPACE + +CandlestickBodyWicksAnimation::CandlestickBodyWicksAnimation(Candlestick *candlestick, + CandlestickAnimation *animation, + int duration, QEasingCurve &curve) + : ChartAnimation(candlestick), + m_candlestick(candlestick), + m_candlestickAnimation(animation), + m_changeAnimation(false) +{ + setDuration(duration); + setEasingCurve(curve); +} + +CandlestickBodyWicksAnimation::~CandlestickBodyWicksAnimation() +{ + if (m_candlestickAnimation) + m_candlestickAnimation->removeCandlestickAnimation(m_candlestick); +} + +void CandlestickBodyWicksAnimation::setup(const CandlestickData &startData, + const CandlestickData &endData) +{ + setKeyValueAt(0.0, qVariantFromValue(startData)); + setKeyValueAt(1.0, qVariantFromValue(endData)); +} + +void CandlestickBodyWicksAnimation::setStartData(const CandlestickData &startData) +{ + if (state() != QAbstractAnimation::Stopped) + stop(); + + setStartValue(qVariantFromValue(startData)); +} + +void CandlestickBodyWicksAnimation::setEndData(const CandlestickData &endData) +{ + if (state() != QAbstractAnimation::Stopped) + stop(); + + setEndValue(qVariantFromValue(endData)); +} + +void CandlestickBodyWicksAnimation::updateCurrentValue(const QVariant &value) +{ + CandlestickData data = qvariant_cast<CandlestickData>(value); + m_candlestick->setLayout(data); +} + +QVariant CandlestickBodyWicksAnimation::interpolated(const QVariant &from, const QVariant &to, + qreal progress) const +{ + CandlestickData startData = qvariant_cast<CandlestickData>(from); + CandlestickData endData = qvariant_cast<CandlestickData>(to); + CandlestickData result = endData; + + if (m_changeAnimation) { + result.m_open = startData.m_open + progress * (endData.m_open - startData.m_open); + result.m_high = startData.m_high + progress * (endData.m_high - startData.m_high); + result.m_low = startData.m_low + progress * (endData.m_low - startData.m_low); + result.m_close = startData.m_close + progress * (endData.m_close - startData.m_close); + } else { + const qreal median = (endData.m_open + endData.m_close) / 2; + result.m_low = median + progress * (endData.m_low - median); + result.m_close = median + progress * (endData.m_close - median); + result.m_open = median + progress * (endData.m_open - median); + result.m_high = median + progress * (endData.m_high - median); + } + + return qVariantFromValue(result); +} + +#include "moc_candlestickbodywicksanimation_p.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/animations/candlestickbodywicksanimation_p.h b/src/charts/animations/candlestickbodywicksanimation_p.h new file mode 100644 index 00000000..a8d30f04 --- /dev/null +++ b/src/charts/animations/candlestickbodywicksanimation_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CANDLESTICKBODYWICKSANIMATION_P_H +#define CANDLESTICKBODYWICKSANIMATION_P_H + +#include <private/candlestickdata_p.h> +#include <private/chartanimation_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +class Candlestick; +class CandlestickAnimation; + +class CandlestickBodyWicksAnimation : public ChartAnimation +{ + Q_OBJECT + +public: + CandlestickBodyWicksAnimation(Candlestick *candlestick, CandlestickAnimation *animation, + int duration, QEasingCurve &curve); + ~CandlestickBodyWicksAnimation(); + + void setup(const CandlestickData &startData, const CandlestickData &endData); + void setStartData(const CandlestickData &startData); + void setEndData(const CandlestickData &endData); + + // from QVariantAnimation + virtual void updateCurrentValue(const QVariant &value); + virtual QVariant interpolated(const QVariant &from, const QVariant &to, qreal progress) const; + +protected: + Candlestick *m_candlestick; + CandlestickAnimation *m_candlestickAnimation; + bool m_changeAnimation; + + friend class CandlestickAnimation; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // CANDLESTICKBODYWICKSANIMATION_P_H diff --git a/src/charts/barchart/qabstractbarseries.cpp b/src/charts/barchart/qabstractbarseries.cpp index 027ae8d0..844b21e3 100644 --- a/src/charts/barchart/qabstractbarseries.cpp +++ b/src/charts/barchart/qabstractbarseries.cpp @@ -983,6 +983,7 @@ void QAbstractBarSeriesPrivate::initializeAxes() case QAbstractSeries::SeriesTypePercentBar: case QAbstractSeries::SeriesTypeStackedBar: case QAbstractSeries::SeriesTypeBoxPlot: + case QAbstractSeries::SeriesTypeCandlestick: if (axis->orientation() == Qt::Horizontal) populateCategories(qobject_cast<QBarCategoryAxis *>(axis)); break; @@ -1009,6 +1010,7 @@ QAbstractAxis::AxisType QAbstractBarSeriesPrivate::defaultAxisType(Qt::Orientati case QAbstractSeries::SeriesTypePercentBar: case QAbstractSeries::SeriesTypeStackedBar: case QAbstractSeries::SeriesTypeBoxPlot: + case QAbstractSeries::SeriesTypeCandlestick: if (orientation == Qt::Horizontal) return QAbstractAxis::AxisTypeBarCategory; break; diff --git a/src/charts/candlestickchart/candlestick.cpp b/src/charts/candlestickchart/candlestick.cpp new file mode 100644 index 00000000..9e8d8fa0 --- /dev/null +++ b/src/charts/candlestickchart/candlestick.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** 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/QCandlestickSet> +#include <QtGui/QPainter> +#include <private/abstractdomain_p.h> +#include <private/candlestick_p.h> +#include <private/qchart_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +Candlestick::Candlestick(QCandlestickSet *set, AbstractDomain *domain, QGraphicsObject *parent) + : QGraphicsObject(parent), + m_set(set), + m_domain(domain), + m_timePeriod(0.0), + m_maximumColumnWidth(-1.0), // no maximum column width by default + m_minimumColumnWidth(-1.0), // no minimum column width by default + m_bodyWidth(0.5), + m_bodyOutlineVisible(true), + m_capsWidth(0.5), + m_capsVisible(false), + m_brush(QChartPrivate::defaultBrush()), + m_pen(QChartPrivate::defaultPen()), + m_hovering(false), + m_mousePressed(false) +{ + setAcceptHoverEvents(true); + setAcceptedMouseButtons(Qt::MouseButtonMask); + setFlag(QGraphicsObject::ItemIsSelectable); +} + +Candlestick::~Candlestick() +{ + // End hover event, if candlestick is deleted during it. + if (m_hovering) + emit hovered(false, m_set); +} + +void Candlestick::setTimePeriod(qreal timePeriod) +{ + m_timePeriod = timePeriod; +} + +void Candlestick::setMaximumColumnWidth(qreal maximumColumnWidth) +{ + m_maximumColumnWidth = maximumColumnWidth; +} + +void Candlestick::setMinimumColumnWidth(qreal minimumColumnWidth) +{ + m_minimumColumnWidth = minimumColumnWidth; +} + +void Candlestick::setBodyWidth(qreal bodyWidth) +{ + m_bodyWidth = bodyWidth; +} + +void Candlestick::setBodyOutlineVisible(bool bodyOutlineVisible) +{ + m_bodyOutlineVisible = bodyOutlineVisible; +} + +void Candlestick::setCapsWidth(qreal capsWidth) +{ + m_capsWidth = capsWidth; +} + +void Candlestick::setCapsVisible(bool capsVisible) +{ + m_capsVisible = capsVisible; +} + +void Candlestick::setIncreasingColor(const QColor &color) +{ + m_increasingColor = color; + + update(); +} + +void Candlestick::setDecreasingColor(const QColor &color) +{ + m_decreasingColor = color; + + update(); +} + +void Candlestick::setBrush(const QBrush &brush) +{ + m_brush = brush; + + update(); +} + +void Candlestick::setPen(const QPen &pen) +{ + qreal widthDiff = pen.widthF() - m_pen.widthF(); + m_boundingRect.adjust(-widthDiff, -widthDiff, widthDiff, widthDiff); + + m_pen = pen; + + update(); +} + +void Candlestick::setLayout(const CandlestickData &data) +{ + m_data = data; + + updateGeometry(m_domain); + update(); +} + +void Candlestick::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + m_mousePressed = true; + emit pressed(m_set); + QGraphicsItem::mousePressEvent(event); +} + +void Candlestick::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event) + + m_hovering = true; + emit hovered(m_hovering, m_set); +} + +void Candlestick::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event) + + m_hovering = false; + emit hovered(m_hovering, m_set); +} + +void Candlestick::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + emit released(m_set); + if (m_mousePressed) + emit clicked(m_set); + m_mousePressed = false; + QGraphicsItem::mouseReleaseEvent(event); +} + +void Candlestick::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + // For candlestick a pressed signal needs to be explicitly fired for mouseDoubleClickEvent. + emit pressed(m_set); + emit doubleClicked(m_set); + QGraphicsItem::mouseDoubleClickEvent(event); +} + +QRectF Candlestick::boundingRect() const +{ + return m_boundingRect; +} + +void Candlestick::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option) + Q_UNUSED(widget) + + bool increasingTrend = (m_data.m_open < m_data.m_close); + QColor color = increasingTrend ? m_increasingColor : m_decreasingColor; + + QBrush brush(m_brush); + brush.setColor(color); + + painter->save(); + painter->setBrush(brush); + painter->setPen(m_pen); + painter->setClipRect(m_boundingRect); + if (m_capsVisible) + painter->drawPath(m_capsPath); + painter->drawPath(m_wicksPath); + if (!m_bodyOutlineVisible) + painter->setPen(QColor(Qt::transparent)); + painter->drawRect(m_bodyRect); + painter->restore(); +} + +void Candlestick::updateGeometry(AbstractDomain *domain) +{ + m_domain = domain; + + prepareGeometryChange(); + + m_capsPath = QPainterPath(); + m_wicksPath = QPainterPath(); + m_boundingRect = QRectF(); + + if (!m_data.m_series->chart()) + return; + + QList<QAbstractAxis *> axes = m_data.m_series->chart()->axes(Qt::Horizontal, m_data.m_series); + if (axes.isEmpty()) + return; + + QAbstractAxis *axisX = axes.value(0); + if (!axisX) + return; + + qreal columnWidth = 0.0; + qreal columnCenter = 0.0; + switch (axisX->type()) { + case QAbstractAxis::AxisTypeBarCategory: + columnWidth = 1.0 / m_data.m_seriesCount; + columnCenter = m_data.m_index - 0.5 + + m_data.m_seriesIndex * columnWidth + + columnWidth / 2.0; + break; + case QAbstractAxis::AxisTypeDateTime: + case QAbstractAxis::AxisTypeValue: + columnWidth = m_timePeriod; + columnCenter = m_data.m_timestamp; + break; + default: + qWarning() << "Unexpected axis type"; + return; + } + + const qreal bodyWidth = m_bodyWidth * columnWidth; + const qreal bodyLeft = columnCenter - (bodyWidth / 2.0); + const qreal bodyRight = bodyLeft + bodyWidth; + + const qreal upperBody = qMax(m_data.m_open, m_data.m_close); + const qreal lowerBody = qMin(m_data.m_open, m_data.m_close); + const bool upperWickVisible = (m_data.m_high > upperBody); + const bool lowerWickVisible = (m_data.m_low < lowerBody); + + QPointF geometryPoint; + bool validData; + + // upper extreme + geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyLeft, m_data.m_high), validData); + if (!validData) + return; + const qreal geometryUpperExtreme = geometryPoint.y(); + // upper body + geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyLeft, upperBody), validData); + if (!validData) + return; + const qreal geometryBodyLeft = geometryPoint.x(); + const qreal geometryUpperBody = geometryPoint.y(); + // lower body + geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyRight, lowerBody), validData); + if (!validData) + return; + const qreal geometryBodyRight = geometryPoint.x(); + const qreal geometryLowerBody = geometryPoint.y(); + // lower extreme + geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyRight, m_data.m_low), validData); + if (!validData) + return; + const qreal geometryLowerExtreme = geometryPoint.y(); + + // Real Body + m_bodyRect.setCoords(geometryBodyLeft, geometryUpperBody, geometryBodyRight, geometryLowerBody); + if (m_maximumColumnWidth != -1.0) { + if (m_bodyRect.width() > m_maximumColumnWidth) { + qreal extra = (m_bodyRect.width() - m_maximumColumnWidth) / 2.0; + m_bodyRect.adjust(extra, 0.0, 0.0, 0.0); + m_bodyRect.setWidth(m_maximumColumnWidth); + } + } + if (m_minimumColumnWidth != -1.0) { + if (m_bodyRect.width() < m_minimumColumnWidth) { + qreal extra = (m_minimumColumnWidth - m_bodyRect.width()) / 2.0; + m_bodyRect.adjust(-extra, 0.0, 0.0, 0.0); + m_bodyRect.setWidth(m_minimumColumnWidth); + } + } + + const qreal geometryCapsExtra = (m_bodyRect.width() - (m_bodyRect.width() * m_capsWidth)) /2.0; + const qreal geometryCapsLeft = m_bodyRect.left() + geometryCapsExtra; + const qreal geometryCapsRight = m_bodyRect.right() - geometryCapsExtra; + + // Upper Wick and Cap + if (upperWickVisible) { + m_capsPath.moveTo(geometryCapsLeft, geometryUpperExtreme); + m_capsPath.lineTo(geometryCapsRight, geometryUpperExtreme); + m_wicksPath.moveTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryUpperExtreme); + m_wicksPath.lineTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryUpperBody); + } + // Lower Wick and Cap + if (lowerWickVisible) { + m_capsPath.moveTo(geometryCapsLeft, geometryLowerExtreme); + m_capsPath.lineTo(geometryCapsRight, geometryLowerExtreme); + m_wicksPath.moveTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryLowerBody); + m_wicksPath.lineTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryLowerExtreme); + } + m_wicksPath.closeSubpath(); + + // bounding rectangle top + qreal boundingRectTop; + if (upperWickVisible) + boundingRectTop = m_wicksPath.boundingRect().top(); + else + boundingRectTop = m_bodyRect.top(); + boundingRectTop = qMax(boundingRectTop, parentItem()->boundingRect().top()); + // bounding rectangle right + qreal boundingRectRight = qMin(m_bodyRect.right(), parentItem()->boundingRect().right()); + // bounding rectangle bottom + qreal boundingRectBottom; + if (lowerWickVisible) + boundingRectBottom = m_wicksPath.boundingRect().bottom(); + else + boundingRectBottom = m_bodyRect.bottom(); + boundingRectBottom = qMin(boundingRectBottom, parentItem()->boundingRect().bottom()); + // bounding rectangle left + qreal boundingRectLeft = qMax(m_bodyRect.left(), parentItem()->boundingRect().left()); + + m_boundingRect.setTop(boundingRectTop); + m_boundingRect.setRight(boundingRectRight); + m_boundingRect.setBottom(boundingRectBottom); + m_boundingRect.setLeft(boundingRectLeft); + + qreal extra = m_pen.widthF(); + m_boundingRect.adjust(-extra, -extra, extra, extra); +} + +#include "moc_candlestick_p.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/candlestickchart/candlestick_p.h b/src/charts/candlestickchart/candlestick_p.h new file mode 100644 index 00000000..fb39bbfa --- /dev/null +++ b/src/charts/candlestickchart/candlestick_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CANDLESTICK_P_H +#define CANDLESTICK_P_H + +#include <QtGui/QBrush> +#include <QtGui/QPainterPath> +#include <QtGui/QPen> +#include <QtWidgets/QGraphicsObject> +#include <private/candlestickdata_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +class AbstractDomain; +class QCandlestickSet; + +class Candlestick : public QGraphicsObject +{ + Q_OBJECT + +public: + Candlestick(QCandlestickSet *set, AbstractDomain *domain, QGraphicsObject *parent); + ~Candlestick(); + + void setTimePeriod(qreal timePeriod); + void setMaximumColumnWidth(qreal maximumColumnWidth); + void setMinimumColumnWidth(qreal minimumColumnWidth); + void setBodyWidth(qreal bodyWidth); + void setBodyOutlineVisible(bool bodyOutlineVisible); + void setCapsWidth(qreal capsWidth); + void setCapsVisible(bool capsVisible); + void setIncreasingColor(const QColor &color); + void setDecreasingColor(const QColor &color); + void setBrush(const QBrush &brush); + void setPen(const QPen &pen); + void setLayout(const CandlestickData &data); + + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget = nullptr); + +Q_SIGNALS: + void clicked(QCandlestickSet *set); + void hovered(bool status, QCandlestickSet *set); + void pressed(QCandlestickSet *set); + void released(QCandlestickSet *set); + void doubleClicked(QCandlestickSet *set); + +private: + void updateGeometry(AbstractDomain *domain); + +private: + QCandlestickSet *m_set; + AbstractDomain *m_domain; + qreal m_timePeriod; + qreal m_maximumColumnWidth; + qreal m_minimumColumnWidth; + qreal m_bodyWidth; + bool m_bodyOutlineVisible; + qreal m_capsWidth; + bool m_capsVisible; + QColor m_increasingColor; + QColor m_decreasingColor; + QBrush m_brush; + QPen m_pen; + CandlestickData m_data; + bool m_hovering; + bool m_mousePressed; + QRectF m_boundingRect; + QRectF m_bodyRect; + QPainterPath m_wicksPath; + QPainterPath m_capsPath; + + friend class CandlestickAnimation; + friend class CandlestickChartItem; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // CANDLESTICK_P_H diff --git a/src/charts/candlestickchart/candlestickchart.pri b/src/charts/candlestickchart/candlestickchart.pri new file mode 100644 index 00000000..35cdd37a --- /dev/null +++ b/src/charts/candlestickchart/candlestickchart.pri @@ -0,0 +1,26 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +SOURCES += \ + $$PWD/candlestick.cpp \ + $$PWD/candlestickchartitem.cpp \ + $$PWD/qcandlestickseries.cpp \ + $$PWD/qcandlestickset.cpp \ + $$PWD/qcandlestickmodelmapper.cpp \ + $$PWD/qhcandlestickmodelmapper.cpp \ + $$PWD/qvcandlestickmodelmapper.cpp + +PRIVATE_HEADERS += \ + $$PWD/candlestick_p.h \ + $$PWD/candlestickchartitem_p.h \ + $$PWD/candlestickdata_p.h \ + $$PWD/qcandlestickseries_p.h \ + $$PWD/qcandlestickset_p.h \ + $$PWD/qcandlestickmodelmapper_p.h + +PUBLIC_HEADERS += \ + $$PWD/qcandlestickseries.h \ + $$PWD/qcandlestickset.h \ + $$PWD/qcandlestickmodelmapper.h \ + $$PWD/qhcandlestickmodelmapper.h \ + $$PWD/qvcandlestickmodelmapper.h diff --git a/src/charts/candlestickchart/candlestickchartitem.cpp b/src/charts/candlestickchart/candlestickchartitem.cpp new file mode 100644 index 00000000..ad64dd13 --- /dev/null +++ b/src/charts/candlestickchart/candlestickchartitem.cpp @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** 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) +{ + 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->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<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->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 diff --git a/src/charts/candlestickchart/candlestickchartitem_p.h b/src/charts/candlestickchart/candlestickchartitem_p.h new file mode 100644 index 00000000..ee3b1f71 --- /dev/null +++ b/src/charts/candlestickchart/candlestickchartitem_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CANDLESTICKCHARTITEM_P_H +#define CANDLESTICKCHARTITEM_P_H + +#include <private/chartitem_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +class Candlestick; +class CandlestickAnimation; +class QCandlestickSeries; +class QCandlestickSet; + +class CandlestickChartItem : public ChartItem +{ + Q_OBJECT + +public: + CandlestickChartItem(QCandlestickSeries *series, QGraphicsItem *item = nullptr); + ~CandlestickChartItem(); + + void setAnimation(CandlestickAnimation *animation); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + +public Q_SLOTS: + void handleDomainUpdated(); + void handleLayoutUpdated(); + void handleCandlesticksUpdated(); + void handleCandlestickSeriesChange(); + +private Q_SLOTS: + void handleCandlestickSetsAdd(const QList<QCandlestickSet *> &sets); + void handleCandlestickSetsRemove(const QList<QCandlestickSet *> &sets); + void handleDataStructureChanged(); + +private: + bool updateCandlestickGeometry(Candlestick *item, int index); + void updateCandlestickAppearance(Candlestick *item, QCandlestickSet *set); + + void addTimestamp(qreal timestamp); + void removeTimestamp(qreal timestamp); + void updateTimePeriod(); + +protected: + QRectF m_boundingRect; + QCandlestickSeries *m_series; // Not owned. + int m_seriesIndex; + int m_seriesCount; + QHash<QCandlestickSet *, Candlestick *> m_candlesticks; + QList<qreal> m_timestamps; + qreal m_timePeriod; + CandlestickAnimation *m_animation; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // CANDLESTICKCHARTITEM_P_H diff --git a/src/charts/candlestickchart/candlestickdata_p.h b/src/charts/candlestickchart/candlestickdata_p.h new file mode 100644 index 00000000..8cb1ab8c --- /dev/null +++ b/src/charts/candlestickchart/candlestickdata_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef CANDLESTICKDATA_P_H +#define CANDLESTICKDATA_P_H + +#include <QtCharts/QCandlestickSeries> + +QT_CHARTS_BEGIN_NAMESPACE + +class CandlestickData +{ +public: + CandlestickData() : + m_timestamp(0.0), + m_open(0.0), + m_high(0.0), + m_low(0.0), + m_close(0.0), + m_index(0), + m_maxX(0.0), + m_minX(0.0), + m_maxY(0.0), + m_minY(0.0), + m_series(nullptr), + m_seriesIndex(0), + m_seriesCount(0) + { + } + + // Candlestick related statistics + qreal m_timestamp; + qreal m_open; + qreal m_high; + qreal m_low; + qreal m_close; + int m_index; + + // Domain boundaries + qreal m_maxX; + qreal m_minX; + qreal m_maxY; + qreal m_minY; + + // Series related data + QCandlestickSeries *m_series; + int m_seriesIndex; + int m_seriesCount; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // CANDLESTICKDATA_P_H 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 diff --git a/src/charts/candlestickchart/qcandlestickmodelmapper.h b/src/charts/candlestickchart/qcandlestickmodelmapper.h new file mode 100644 index 00000000..217468c9 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickmodelmapper.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QCANDLESTICKMODELMAPPER_H +#define QCANDLESTICKMODELMAPPER_H + +#include <QtCharts/QChartGlobal> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +QT_END_NAMESPACE + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickModelMapperPrivate; +class QCandlestickSeries; + +class QT_CHARTS_EXPORT QCandlestickModelMapper : public QObject +{ + Q_OBJECT + Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelReplaced) + Q_PROPERTY(QCandlestickSeries *series READ series WRITE setSeries NOTIFY seriesReplaced) + +public: + explicit QCandlestickModelMapper(QObject *parent = nullptr); + + void setModel(QAbstractItemModel *model); + QAbstractItemModel *model() const; + + void setSeries(QCandlestickSeries *series); + QCandlestickSeries *series() const; + + virtual Qt::Orientation orientation() const = 0; + +Q_SIGNALS: + void modelReplaced(); + void seriesReplaced(); + +protected: + void setTimestamp(int timestamp); + int timestamp() const; + + void setOpen(int open); + int open() const; + + void setHigh(int high); + int high() const; + + void setLow(int low); + int low() const; + + void setClose(int close); + int close() const; + + void setFirstCandlestickSetSection(int firstCandlestickSetSection); + int firstCandlestickSetSection() const; + + void setLastCandlestickSetSection(int lastCandlestickSetSection); + int lastCandlestickSetSection() const; + +protected: + QCandlestickModelMapperPrivate * const d_ptr; + Q_DECLARE_PRIVATE(QCandlestickModelMapper) +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKMODELMAPPER_H diff --git a/src/charts/candlestickchart/qcandlestickmodelmapper_p.h b/src/charts/candlestickchart/qcandlestickmodelmapper_p.h new file mode 100644 index 00000000..df84e3f2 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickmodelmapper_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QCANDLESTICKMODELMAPPER_P_H +#define QCANDLESTICKMODELMAPPER_P_H + +#include <QtCharts/QCandlestickModelMapper> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickSet; + +class QCandlestickModelMapperPrivate : public QObject +{ + Q_OBJECT + +public: + explicit QCandlestickModelMapperPrivate(QCandlestickModelMapper *q); + +Q_SIGNALS: + void timestampChanged(); + void openChanged(); + void highChanged(); + void lowChanged(); + void closeChanged(); + void firstCandlestickSetSectionChanged(); + void lastCandlestickSetSectionChanged(); + +private Q_SLOTS: + void initializeCandlestickFromModel(); + + // for the model + void modelDataUpdated(QModelIndex topLeft, QModelIndex bottomRight); + void modelHeaderDataUpdated(Qt::Orientation orientation, int first, int last); + void modelRowsInserted(QModelIndex parent, int start, int end); + void modelRowsRemoved(QModelIndex parent, int start, int end); + void modelColumnsInserted(QModelIndex parent, int start, int end); + void modelColumnsRemoved(QModelIndex parent, int start, int end); + void modelDestroyed(); + + // for the series + void candlestickSetsAdded(const QList<QCandlestickSet *> &sets); + void candlestickSetsRemoved(const QList<QCandlestickSet *> &sets); + void candlestickSetChanged(); + void seriesDestroyed(); + +private: + QCandlestickSet *candlestickSet(QModelIndex index); + QModelIndex candlestickModelIndex(int section, int pos); + void insertData(int start, int end); + void removeData(int start, int end); + void blockModelSignals(bool block = true); + void blockSeriesSignals(bool block = true); + +private: + QAbstractItemModel *m_model; + QCandlestickSeries *m_series; + int m_timestamp; + int m_open; + int m_high; + int m_low; + int m_close; + int m_firstCandlestickSetSection; + int m_lastCandlestickSetSection; + QList<QCandlestickSet *> m_candlestickSets; + bool m_modelSignalsBlock; + bool m_seriesSignalsBlock; + +private: + QCandlestickModelMapper *q_ptr; + Q_DECLARE_PUBLIC(QCandlestickModelMapper) +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKMODELMAPPER_P_H diff --git a/src/charts/candlestickchart/qcandlestickseries.cpp b/src/charts/candlestickchart/qcandlestickseries.cpp new file mode 100644 index 00000000..2e04d6cf --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickseries.cpp @@ -0,0 +1,1138 @@ +/**************************************************************************** +** +** 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/QBarCategoryAxis> +#include <QtCharts/QCandlestickLegendMarker> +#include <QtCharts/QCandlestickSeries> +#include <QtCharts/QCandlestickSet> +#include <QtCharts/QValueAxis> +#include <QtCore/QDateTime> +#include <private/candlestickanimation_p.h> +#include <private/candlestickchartitem_p.h> +#include <private/chartdataset_p.h> +#include <private/charttheme_p.h> +#include <private/qcandlestickseries_p.h> +#include <private/qcandlestickset_p.h> +#include <private/qchart_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +/*! + \class QCandlestickSeries + \since 5.8 + \inmodule Qt Charts + \brief Series for creating a candlestick chart. + + QCandlestickSeries represents a series of data shown as candlesticks. The purpose of this class + is to act as a container for single candlestick items. Each item is drawn to its own category + when using QBarCategoryAxis. QDateTimeAxis and QValueAxis can be used as alternatives to + QBarCategoryAxis. In this case, each candlestick item is drawn according to its timestamp value. + + \note The timestamps must be unique within a QCandlestickSeries. When using QBarCategoryAxis, + only the first one of the candlestick items sharing a timestamp is drawn. If the chart includes + multiple instances of QCandlestickSeries, items from different series sharing a timestamp are + drawn to the same category. When using QValueAxis or QDateTimeAxis, candlestick items sharing a + timestamp will overlap each other. + + See the \l {Candlestick Chart Example} {candlestick chart example} to learn how to create + a candlestick chart. + \image examples_candlestickchart.png + + \sa QCandlestickSet, QBarCategoryAxis, QDateTimeAxis, QValueAxis +*/ + +/*! + \qmltype CandlestickSeries + \since 2.2 + \instantiates QCandlestickSeries + \inqmlmodule QtCharts + \inherits AbstractSeries + \brief Series for creating a candlestick chart. + + CandlestickSeries represents a series of data shown as candlesticks. The purpose of this class + is to act as a container for single candlestick items. Each item is drawn to its own category + when using BarCategoryAxis. DateTimeAxis and ValueAxis can be used as an alternative to + BarCategoryAxis. In this case each candlestick item is drawn according to its timestamp value. + + \note The timestamps must be unique within a CandlestickSeries. When using BarCategoryAxis, only + the first one of the candlestick items sharing a timestamp is drawn. If the chart includes + multiple instances of CandlestickSeries, items from different series sharing a timestamp are + drawn to the same category. When using ValueAxis or DateTimeAxis, candlestick items sharing a + timestamp will overlap each other. + + The following QML shows how to create a simple candlestick chart: + \code + import QtQuick 2.5 + import QtCharts 2.2 + + ChartView { + title: "Candlestick Series" + width: 400 + height: 300 + + CandlestickSeries { + name: "Acme Ltd." + increasingColor: "green" + decreasingColor: "red" + + CandlestickSet { timestamp: 1435708800000; open: 690; high: 694; low: 599; close: 660 } + CandlestickSet { timestamp: 1435795200000; open: 669; high: 669; low: 669; close: 669 } + CandlestickSet { timestamp: 1436140800000; open: 485; high: 623; low: 485; close: 600 } + CandlestickSet { timestamp: 1436227200000; open: 589; high: 615; low: 377; close: 569 } + CandlestickSet { timestamp: 1436313600000; open: 464; high: 464; low: 254; close: 254 } + } + } + \endcode + + \beginfloatleft + \image examples_qmlcandlestick.png + \endfloat + \clearfloat + + \sa CandlestickSet, BarCategoryAxis, DateTimeAxis, ValueAxis +*/ + +/*! + \property QCandlestickSeries::count + \brief The count of sets in series. +*/ + +/*! + \qmlproperty int CandlestickSeries::count + The count of sets in series. +*/ + +/*! + \property QCandlestickSeries::maximumColumnWidth + \brief The maximum width of the candlestick items in pixels. Setting a negative value means + there is no maximum width. All negative values are converted to -1.0. +*/ + +/*! + \qmlproperty qreal CandlestickSeries::maximumColumnWidth + \brief The maximum width of the candlestick items in pixels. Setting a negative value means + there is no maximum width. All negative values are converted to -1.0. +*/ + +/*! + \property QCandlestickSeries::minimumColumnWidth + \brief The minimum width of the candlestick items in pixels. Setting a negative value means + there is no minimum width. All negative values are converted to -1.0. +*/ + +/*! + \qmlproperty qreal CandlestickSeries::minimumColumnWidth + \brief The minimum width of the candlestick items in pixels. Setting a negative value means + there is no minimum width. All negative values are converted to -1.0. +*/ + +/*! + \property QCandlestickSeries::bodyWidth + \brief The width of the candlestick items. + + The value signifies the relative width of the candlestick item inside its own slot, in the range + 0.0 to 1.0. Values outside this range are clamped to 0.0 or 1.0. +*/ + +/*! + \qmlproperty qreal CandlestickSeries::bodyWidth + \brief The width of the candlestick items. + + The value signifies the relative width of the candlestick item inside its own slot, in the range + 0.0 to 1.0. Values outside this range are clamped to 0.0 or 1.0. +*/ + +/*! + \property QCandlestickSeries::bodyOutlineVisible + \brief The visibility of the candlestick body outlines. +*/ + +/*! + \qmlproperty bool CandlestickSeries::bodyOutlineVisible + \brief The visibility of the candlestick body outlines. +*/ + +/*! + \property QCandlestickSeries::capsWidth + \brief The width of the caps. + + The value signifies the relative width of the caps inside its own candlestick, in the range 0.0 + to 1.0. Values outside this range are clamped to 0.0 or 1.0. +*/ + +/*! + \qmlproperty qreal CandlestickSeries::capsWidth + \brief The width of the caps. + + The value signifies the relative width of the caps inside its own candlestick, in the range 0.0 + to 1.0. Values outside this range are clamped to 0.0 or 1.0. +*/ + +/*! + \property QCandlestickSeries::capsVisible + \brief The visibility of the caps. +*/ + +/*! + \qmlproperty bool CandlestickSeries::capsVisible + \brief The visibility of the caps. +*/ + +/*! + \property QCandlestickSeries::increasingColor + \brief The color of the increasing candlestick item body. Candlestick is \e increasing when its + close value is higher than the open value. By default this property is set to brush color. + Default color is used also when the property is set to an invalid color value. +*/ + +/*! + \qmlproperty QColor CandlestickSeries::increasingColor + \brief The color of the increasing candlestick item body. Candlestick is \e increasing when its + close value is higher than the open value. By default this property is set to brush color. + Default color is used also when the property is set to an invalid color value. +*/ + +/*! + \property QCandlestickSeries::decreasingColor + \brief The color of the decreasing candlestick item body. Candlestick is \e decreasing when its + open value is higher than the close value. By default this property is set to brush color with + alpha channel set to 128. Default color is used also when the property is set to an invalid + color value. +*/ + +/*! + \qmlproperty QColor CandlestickSeries::decreasingColor + \brief The color of the decreasing candlestick item body. Candlestick is \e decreasing when its + open value is higher than the close value. By default this property is set to brush color with + alpha channel set to 128. Default color is used also when the property is set to an invalid + color value. +*/ + +/*! + \property QCandlestickSeries::brush + \brief The brush of the candlestick items. +*/ + +/*! + \property QCandlestickSeries::pen + \brief The pen of the candlestick items. +*/ + +/*! + \qmlproperty QString CandlestickSeries::brushFilename + \brief The name of the file used as a brush for the series. +*/ + +/*! + \fn void QCandlestickSeries::clicked(QCandlestickSet *set) + \brief Emitted when a \a set is clicked (pressed and released) on the chart. +*/ + +/*! + \qmlsignal CandlestickSeries::clicked(CandlestickSet set) + \brief Emitted when a \a set is clicked (pressed and released) on the chart. + + The corresponding signal handler is \c {onClicked}. +*/ + +/*! + \fn void QCandlestickSeries::hovered(bool status, QCandlestickSet *set) + \brief Emitted when there is change in hover \a status over the \a set. +*/ + +/*! + \qmlsignal CandlestickSeries::hovered(bool status, CandlestickSet set) + \brief Emitted when there is change in hover \a status over the \a set. + + The corresponding signal handler is \c {onHovered}. +*/ + +/*! + \fn void QCandlestickSeries::pressed(QCandlestickSet *set) + \brief Emitted when a \a set is pressed on the chart. +*/ + +/*! + \qmlsignal CandlestickSeries::pressed(CandlestickSet set) + \brief Emitted when a \a set is pressed on the chart. + + The corresponding signal handler is \c {onPressed}. +*/ + +/*! + \fn void QCandlestickSeries::released(QCandlestickSet *set) + \brief Emitted when a \a set is released on the chart. +*/ + +/*! + \qmlsignal CandlestickSeries::released(CandlestickSet set) + \brief Emitted when a \a set is released on the chart. + + The corresponding signal handler is \c {onReleased}. +*/ + +/*! + \fn void QCandlestickSeries::doubleClicked(QCandlestickSet *set) + \brief Emitted when a \a set is double-clicked on the chart. +*/ + +/*! + \qmlsignal CandlestickSeries::doubleClicked(CandlestickSet set) + \brief Emitted when a \a set is double-clicked on the chart. + + The corresponding signal handler is \c {onDoubleClicked}. +*/ + +/*! + \fn void QCandlestickSeries::candlestickSetsAdded(const QList<QCandlestickSet *> &sets) + \brief Emitted when new \a sets are added to the series. +*/ + +/*! + \qmlsignal CandlestickSeries::candlestickSetsAdded(list<CandlestickSet> sets) + \brief Emitted when new \a sets are added to the series. + + The corresponding signal handler is \c {onCandlestickSetsAdded}. +*/ + +/*! + \fn void QCandlestickSeries::candlestickSetsRemoved(const QList<QCandlestickSet *> &sets) + \brief Emitted when \a sets are removed from the series. +*/ + +/*! + \qmlsignal CandlestickSeries::candlestickSetsRemoved(list<CandlestickSet> sets) + \brief Emitted when \a sets are removed from the series. + + The corresponding signal handler is \c {onCandlestickSetsRemoved}. +*/ + +/*! + \fn void QCandlestickSeries::countChanged() + \brief Emitted when there is a change in the count of candlestick items in the series. + \sa count +*/ + +/*! + \qmlsignal CandlestickSeries::countChanged() + \brief Emitted when there is a change in the count of candlestick items in the series. + \sa count + + The corresponding signal handler is \c {onCountChanged}. +*/ + +/*! + \fn void QCandlestickSeries::maximumColumnWidthChanged() + \brief Emitted when there is a change in the maximum column width of candlestick items. + \sa maximumColumnWidth +*/ + +/*! + \qmlsignal CandlestickSeries::maximumColumnWidthChanged() + \brief Emitted when there is a change in the maximum column width of candlestick items. + \sa maximumColumnWidth + + The corresponding signal handler is \c {onMaximumColumnWidthChanged}. +*/ + +/*! + \fn void QCandlestickSeries::minimumColumnWidthChanged() + \brief Emitted when there is a change in the minimum column width of candlestick items. + \sa minimumColumnWidth +*/ + +/*! + \qmlsignal CandlestickSeries::minimumColumnWidthChanged() + \brief Emitted when there is a change in the minimum column width of candlestick items. + \sa minimumColumnWidth + + The corresponding signal handler is \c {onMinimumColumnWidthChanged}. +*/ + +/*! + \fn void QCandlestickSeries::bodyWidthChanged() + \brief Emitted when the candlestick item width is changed. + \sa bodyWidth +*/ + +/*! + \qmlsignal CandlestickSeries::bodyWidthChanged() + \brief Emitted when the candlestick item width is changed. + \sa bodyWidth + + The corresponding signal handler is \c {onBodyWidthChanged}. +*/ + +/*! + \fn void QCandlestickSeries::bodyOutlineVisibilityChanged() + \brief Emitted when the visibility of the candlestick item body outline is changed. + \sa bodyOutlineVisible +*/ + +/*! + \qmlsignal CandlestickSeries::bodyOutlineVisibilityChanged() + \brief Emitted when the visibility of the candlestick item body outline is changed. + \sa bodyOutlineVisible + + The corresponding signal handler is \c {onBodyOutlineVisibilityChanged}. +*/ + +/*! + \fn void QCandlestickSeries::capsWidthChanged() + \brief Emitted when the candlestick item caps width is changed. + \sa capsWidth +*/ + +/*! + \qmlsignal CandlestickSeries::capsWidthChanged() + \brief Emitted when the candlestick item caps width is changed. + \sa capsWidth + + The corresponding signal handler is \c {onCapsWidthChanged}. +*/ + +/*! + \fn void QCandlestickSeries::capsVisibilityChanged() + \brief Emitted when the visibility of the candlestick item caps is changed. + \sa capsVisible +*/ + +/*! + \qmlsignal CandlestickSeries::capsVisibilityChanged() + \brief Emitted when the visibility of the candlestick item caps is changed. + \sa capsVisible + + The corresponding signal handler is \c {onCapsVisibilityChanged}. +*/ + +/*! + \fn void QCandlestickSeries::increasingColorChanged() + \brief Emitted when the candlestick item increasing color is changed. + \sa increasingColor +*/ + +/*! + \qmlsignal CandlestickSeries::increasingColorChanged() + \brief Emitted when the candlestick item increasing color is changed. + \sa increasingColor + + The corresponding signal handler is \c {onIncreasingColorChanged}. +*/ + +/*! + \fn void QCandlestickSeries::decreasingColorChanged() + \brief Emitted when the candlestick item decreasing color is changed. + \sa decreasingColor +*/ + +/*! + \qmlsignal CandlestickSeries::decreasingColorChanged() + \brief Emitted when the candlestick item decreasing color is changed. + \sa decreasingColor + + The corresponding signal handler is \c {onDecreasingColorChanged}. +*/ + +/*! + \fn void QCandlestickSeries::brushChanged() + \brief Emitted when the candlestick item brush is changed. + \sa brush +*/ + +/*! + \fn void QCandlestickSeries::penChanged() + \brief Emitted when the candlestick item pen is changed. + \sa pen +*/ + +/*! + Constructs an empty QCandlestickSeries. The \a parent is optional. +*/ +QCandlestickSeries::QCandlestickSeries(QObject *parent) + : QAbstractSeries(*new QCandlestickSeriesPrivate(this), parent) +{ +} + +/*! + Destroys the series. Removes the series from the chart. +*/ +QCandlestickSeries::~QCandlestickSeries() +{ + Q_D(QCandlestickSeries); + if (d->m_chart) + d->m_chart->removeSeries(this); +} + +/*! + Adds a single set to the series. Takes ownership of the \a set. If the set is \e null or is + already in the series, it won't be appended. + Returns \c true if appending succeeded, \c false otherwise. +*/ +bool QCandlestickSeries::append(QCandlestickSet *set) +{ + QList<QCandlestickSet *> sets; + sets.append(set); + + return append(sets); +} + +/*! + Removes a single set from the series. + Returns \c true if the \a set is successfully deleted, \c false otherwise. +*/ +bool QCandlestickSeries::remove(QCandlestickSet *set) +{ + QList<QCandlestickSet *> sets; + sets.append(set); + + return remove(sets); +} + +/*! + Adds a list of sets to the series. Takes ownership of the \a sets. If any of the sets are + \e null, already appended to the series, or the list contains duplicated sets, nothing is + appended. + Returns \c true if all sets were appended successfully, \c false otherwise. +*/ +bool QCandlestickSeries::append(const QList<QCandlestickSet *> &sets) +{ + Q_D(QCandlestickSeries); + + bool success = d->append(sets); + if (success) { + emit candlestickSetsAdded(sets); + emit countChanged(); + } + + return success; +} + +/*! + Removes a list of sets from the series. If any of the \a sets are \e null, already removed from + the series, or the list contains duplicated sets, nothing is removed. + Returns \c true if all sets were removed successfully, \c false otherwise. +*/ +bool QCandlestickSeries::remove(const QList<QCandlestickSet *> &sets) +{ + Q_D(QCandlestickSeries); + + bool success = d->remove(sets); + if (success) { + emit candlestickSetsRemoved(sets); + emit countChanged(); + foreach (QCandlestickSet *set, sets) + set->deleteLater(); + } + + return success; +} + +/*! + Inserts a set to the series at \a index position. Takes ownership of the \a set. If the set is + \e null or already in the series, it won't be appended. + Returns \c true if inserting succeeded, \c false otherwise. +*/ +bool QCandlestickSeries::insert(int index, QCandlestickSet *set) +{ + Q_D(QCandlestickSeries); + + bool success = d->insert(index, set); + if (success) { + QList<QCandlestickSet *> sets; + sets.append(set); + emit candlestickSetsAdded(sets); + emit countChanged(); + } + + return success; +} + +/*! + Takes a single \a set from the series. Does not delete the set object. + Returns \c true if take was successful, \c false otherwise. + \note The series remains as the set's parent object. You must set the parent object to take full + ownership. +*/ +bool QCandlestickSeries::take(QCandlestickSet *set) +{ + Q_D(QCandlestickSeries); + + QList<QCandlestickSet *> sets; + sets.append(set); + + bool success = d->remove(sets); + if (success) { + emit candlestickSetsRemoved(sets); + emit countChanged(); + } + + return success; +} + +/*! + Removes all sets from the series, and deletes them. +*/ +void QCandlestickSeries::clear() +{ + Q_D(QCandlestickSeries); + + QList<QCandlestickSet *> sets = candlestickSets(); + + bool success = d->remove(sets); + if (success) { + emit candlestickSetsRemoved(sets); + emit countChanged(); + foreach (QCandlestickSet *set, sets) + set->deleteLater(); + } +} + +/*! + Returns the list of sets in the series. Ownership of the sets is unchanged. + */ +QList<QCandlestickSet *> QCandlestickSeries::candlestickSets() const +{ + Q_D(const QCandlestickSeries); + + return d->m_candlestickSets; +} + +/*! + Returns the number of the sets in the series. +*/ +int QCandlestickSeries::count() const +{ + return candlestickSets().count(); +} + +/*! + Returns the type of the series (QAbstractSeries::SeriesTypeCandlestick). +*/ +QAbstractSeries::SeriesType QCandlestickSeries::type() const +{ + return QAbstractSeries::SeriesTypeCandlestick; +} + +void QCandlestickSeries::setMaximumColumnWidth(qreal maximumColumnWidth) +{ + Q_D(QCandlestickSeries); + + if (maximumColumnWidth < 0.0 && maximumColumnWidth != -1.0) + maximumColumnWidth = -1.0; + + if (d->m_maximumColumnWidth == maximumColumnWidth) + return; + + d->m_maximumColumnWidth = maximumColumnWidth; + + emit d->updatedLayout(); + emit maximumColumnWidthChanged(); +} + +qreal QCandlestickSeries::maximumColumnWidth() const +{ + Q_D(const QCandlestickSeries); + + return d->m_maximumColumnWidth; +} + +void QCandlestickSeries::setMinimumColumnWidth(qreal minimumColumnWidth) +{ + Q_D(QCandlestickSeries); + + if (minimumColumnWidth < 0.0 && minimumColumnWidth != -1.0) + minimumColumnWidth = -1.0; + + if (d->m_minimumColumnWidth == minimumColumnWidth) + return; + + d->m_minimumColumnWidth = minimumColumnWidth; + + d->updatedLayout(); + emit minimumColumnWidthChanged(); +} + +qreal QCandlestickSeries::minimumColumnWidth() const +{ + Q_D(const QCandlestickSeries); + + return d->m_minimumColumnWidth; +} + +void QCandlestickSeries::setBodyWidth(qreal bodyWidth) +{ + Q_D(QCandlestickSeries); + + if (bodyWidth < 0.0) + bodyWidth = 0.0; + else if (bodyWidth > 1.0) + bodyWidth = 1.0; + + if (d->m_bodyWidth == bodyWidth) + return; + + d->m_bodyWidth = bodyWidth; + + emit d->updatedLayout(); + emit bodyWidthChanged(); +} + +qreal QCandlestickSeries::bodyWidth() const +{ + Q_D(const QCandlestickSeries); + + return d->m_bodyWidth; +} + +void QCandlestickSeries::setBodyOutlineVisible(bool bodyOutlineVisible) +{ + Q_D(QCandlestickSeries); + + if (d->m_bodyOutlineVisible == bodyOutlineVisible) + return; + + d->m_bodyOutlineVisible = bodyOutlineVisible; + + emit d->updated(); + emit bodyOutlineVisibilityChanged(); +} + +bool QCandlestickSeries::bodyOutlineVisible() const +{ + Q_D(const QCandlestickSeries); + + return d->m_bodyOutlineVisible; +} + +void QCandlestickSeries::setCapsWidth(qreal capsWidth) +{ + Q_D(QCandlestickSeries); + + if (capsWidth < 0.0) + capsWidth = 0.0; + else if (capsWidth > 1.0) + capsWidth = 1.0; + + if (d->m_capsWidth == capsWidth) + return; + + d->m_capsWidth = capsWidth; + + emit d->updatedLayout(); + emit capsWidthChanged(); +} + +qreal QCandlestickSeries::capsWidth() const +{ + Q_D(const QCandlestickSeries); + + return d->m_capsWidth; +} + +void QCandlestickSeries::setCapsVisible(bool capsVisible) +{ + Q_D(QCandlestickSeries); + + if (d->m_capsVisible == capsVisible) + return; + + d->m_capsVisible = capsVisible; + + emit d->updated(); + emit capsVisibilityChanged(); +} + +bool QCandlestickSeries::capsVisible() const +{ + Q_D(const QCandlestickSeries); + + return d->m_capsVisible; +} + +void QCandlestickSeries::setIncreasingColor(const QColor &increasingColor) +{ + Q_D(QCandlestickSeries); + + QColor color; + if (increasingColor.isValid()) { + color = increasingColor; + d->m_customIncreasingColor = true; + } else { + color = d->m_brush.color(); + color.setAlpha(128); + d->m_customIncreasingColor = false; + } + + if (d->m_increasingColor == color) + return; + + d->m_increasingColor = color; + + emit d->updated(); + emit increasingColorChanged(); +} + +QColor QCandlestickSeries::increasingColor() const +{ + Q_D(const QCandlestickSeries); + + return d->m_increasingColor; +} + +void QCandlestickSeries::setDecreasingColor(const QColor &decreasingColor) +{ + Q_D(QCandlestickSeries); + + QColor color; + if (decreasingColor.isValid()) { + color = decreasingColor; + d->m_customDecreasingColor = true; + } else { + color = d->m_brush.color(); + d->m_customDecreasingColor = false; + } + + if (d->m_decreasingColor == color) + return; + + d->m_decreasingColor = color; + + emit d->updated(); + emit decreasingColorChanged(); +} + +QColor QCandlestickSeries::decreasingColor() const +{ + Q_D(const QCandlestickSeries); + + return d->m_decreasingColor; +} + +void QCandlestickSeries::setBrush(const QBrush &brush) +{ + Q_D(QCandlestickSeries); + + if (d->m_brush == brush) + return; + + d->m_brush = brush; + if (!d->m_customIncreasingColor) { + QColor color = d->m_brush.color(); + color.setAlpha(128); + if (d->m_increasingColor != color) { + d->m_increasingColor = color; + emit increasingColorChanged(); + } + } + if (!d->m_customDecreasingColor && d->m_decreasingColor != d->m_brush.color()) { + d->m_decreasingColor = d->m_brush.color(); + emit decreasingColorChanged(); + } + + emit d->updated(); + emit brushChanged(); +} + +QBrush QCandlestickSeries::brush() const +{ + Q_D(const QCandlestickSeries); + + return d->m_brush; +} + +void QCandlestickSeries::setPen(const QPen &pen) +{ + Q_D(QCandlestickSeries); + + if (d->m_pen == pen) + return; + + d->m_pen = pen; + + emit d->updated(); + emit penChanged(); +} + +QPen QCandlestickSeries::pen() const +{ + Q_D(const QCandlestickSeries); + + return d->m_pen; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +QCandlestickSeriesPrivate::QCandlestickSeriesPrivate(QCandlestickSeries *q) + : QAbstractSeriesPrivate(q), + m_maximumColumnWidth(-1.0), + m_minimumColumnWidth(5.0), + m_bodyWidth(0.5), + m_bodyOutlineVisible(true), + m_capsWidth(0.5), + m_capsVisible(false), + m_increasingColor(QColor(Qt::transparent)), + m_decreasingColor(QChartPrivate::defaultBrush().color()), + m_customIncreasingColor(false), + m_customDecreasingColor(false), + m_brush(QChartPrivate::defaultBrush()), + m_pen(QChartPrivate::defaultPen()), + m_animation(nullptr) +{ +} + +QCandlestickSeriesPrivate::~QCandlestickSeriesPrivate() +{ + disconnect(this, 0, 0, 0); +} + +void QCandlestickSeriesPrivate::initializeDomain() +{ + qreal minX(domain()->minX()); + qreal maxX(domain()->maxX()); + qreal minY(domain()->minY()); + qreal maxY(domain()->maxY()); + + if (m_candlestickSets.count()) { + QCandlestickSet *set = m_candlestickSets.first(); + minX = set->timestamp(); + maxX = set->timestamp(); + minY = set->low(); + maxY = set->high(); + for (int i = 1; i < m_candlestickSets.count(); ++i) { + set = m_candlestickSets.at(i); + minX = qMin(minX, qreal(set->timestamp())); + maxX = qMax(maxX, qreal(set->timestamp())); + minY = qMin(minY, set->low()); + maxY = qMax(maxY, set->high()); + } + qreal extra = (maxX - minX) / m_candlestickSets.count() / 2; + minX = minX - extra; + maxX = maxX + extra; + } + + domain()->setRange(minX, maxX, minY, maxY); +} + +void QCandlestickSeriesPrivate::initializeAxes() +{ + foreach (QAbstractAxis* axis, m_axes) { + if (axis->type() == QAbstractAxis::AxisTypeBarCategory) { + if (axis->orientation() == Qt::Horizontal) + populateBarCategories(qobject_cast<QBarCategoryAxis *>(axis)); + } + } +} + +void QCandlestickSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced) +{ + Q_Q(QCandlestickSeries); + + if (forced || QChartPrivate::defaultBrush() == m_brush) { + const QList<QGradient> gradients = theme->seriesGradients(); + const QGradient gradient = gradients.at(index % gradients.size()); + const QBrush brush(ChartThemeManager::colorAt(gradient, 0.5)); + q->setBrush(brush); + } + + if (forced || QChartPrivate::defaultPen() == m_pen) { + QPen pen = theme->outlinePen(); + pen.setCosmetic(true); + q->setPen(pen); + } +} + +void QCandlestickSeriesPrivate::initializeGraphics(QGraphicsItem *parent) +{ + Q_Q(QCandlestickSeries); + + CandlestickChartItem *item = new CandlestickChartItem(q, parent); + m_item.reset(item); + QAbstractSeriesPrivate::initializeGraphics(parent); + + if (m_chart) { + connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesAdded(QAbstractSeries *)), + this, SLOT(handleSeriesChange(QAbstractSeries *))); + connect(m_chart->d_ptr->m_dataset, SIGNAL(seriesRemoved(QAbstractSeries *)), + this, SLOT(handleSeriesRemove(QAbstractSeries *))); + + item->handleCandlestickSeriesChange(); + } +} + +void QCandlestickSeriesPrivate::initializeAnimations(QChart::AnimationOptions options, int duration, + QEasingCurve &curve) +{ + CandlestickChartItem *item = static_cast<CandlestickChartItem *>(m_item.data()); + Q_ASSERT(item); + + if (item->animation()) + item->animation()->stopAndDestroyLater(); + + if (options.testFlag(QChart::SeriesAnimations)) + m_animation = new CandlestickAnimation(item, duration, curve); + else + m_animation = nullptr; + item->setAnimation(m_animation); + + QAbstractSeriesPrivate::initializeAnimations(options, duration, curve); +} + +QList<QLegendMarker *> QCandlestickSeriesPrivate::createLegendMarkers(QLegend *legend) +{ + Q_Q(QCandlestickSeries); + + QList<QLegendMarker *> list; + + return list << new QCandlestickLegendMarker(q, legend); +} + +QAbstractAxis::AxisType QCandlestickSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const +{ + if (orientation == Qt::Horizontal) + return QAbstractAxis::AxisTypeBarCategory; + + if (orientation == Qt::Vertical) + return QAbstractAxis::AxisTypeValue; + + return QAbstractAxis::AxisTypeNoAxis; +} + +QAbstractAxis* QCandlestickSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const +{ + const QAbstractAxis::AxisType axisType = defaultAxisType(orientation); + + if (axisType == QAbstractAxis::AxisTypeBarCategory) + return new QBarCategoryAxis; + + if (axisType == QAbstractAxis::AxisTypeValue) + return new QValueAxis; + + return 0; // axisType == QAbstractAxis::AxisTypeNoAxis +} + +bool QCandlestickSeriesPrivate::append(const QList<QCandlestickSet *> &sets) +{ + foreach (QCandlestickSet *set, sets) { + if ((set == 0) || m_candlestickSets.contains(set) || set->d_ptr->m_series) + return false; // Fail if any of the sets is null or is already appended. + if (sets.count(set) != 1) + return false; // Also fail if the same set occurs more than once in the given list. + } + + foreach (QCandlestickSet *set, sets) { + m_candlestickSets.append(set); + connect(set->d_func(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout())); + connect(set->d_func(), SIGNAL(updatedCandlestick()), this, SIGNAL(updatedCandlesticks())); + set->d_ptr->m_series = this; + } + + return true; +} + +bool QCandlestickSeriesPrivate::remove(const QList<QCandlestickSet *> &sets) +{ + if (sets.count() == 0) + return false; + + foreach (QCandlestickSet *set, sets) { + if ((set == 0) || (!m_candlestickSets.contains(set))) + return false; // Fail if any of the sets is null or is not in series. + if (sets.count(set) != 1) + return false; // Also fail if the same set occurs more than once in the given list. + } + + foreach (QCandlestickSet *set, sets) { + set->d_ptr->m_series = nullptr; + m_candlestickSets.removeOne(set); + disconnect(set->d_func(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout())); + disconnect(set->d_func(), SIGNAL(updatedCandlestick()),this, SIGNAL(updatedCandlesticks())); + } + + return true; +} + +bool QCandlestickSeriesPrivate::insert(int index, QCandlestickSet *set) +{ + if ((m_candlestickSets.contains(set)) || (set == 0) || set->d_ptr->m_series) + return false; // Fail if set is already in list or set is null. + + m_candlestickSets.insert(index, set); + connect(set->d_func(), SIGNAL(updatedLayout()), this, SIGNAL(updatedLayout())); + connect(set->d_func(), SIGNAL(updatedCandlestick()), this, SIGNAL(updatedCandlesticks())); + set->d_ptr->m_series = this; + + return true; +} + +void QCandlestickSeriesPrivate::handleSeriesChange(QAbstractSeries *series) +{ + Q_UNUSED(series); + + if (m_chart) { + CandlestickChartItem *item = static_cast<CandlestickChartItem *>(m_item.data()); + if (item) + item->handleCandlestickSeriesChange(); + } +} + +void QCandlestickSeriesPrivate::handleSeriesRemove(QAbstractSeries *series) +{ + Q_Q(const QCandlestickSeries); + + QCandlestickSeries *removedSeries = static_cast<QCandlestickSeries *>(series); + + if (q == removedSeries && m_animation) { + m_animation->stopAll(); + disconnect(m_chart->d_ptr->m_dataset, 0, removedSeries->d_func(), 0); + } + + if (q != removedSeries) { + CandlestickChartItem *item = static_cast<CandlestickChartItem *>(m_item.data()); + if (item) + item->handleCandlestickSeriesChange(); + } +} + +void QCandlestickSeriesPrivate::populateBarCategories(QBarCategoryAxis *axis) +{ + if (axis->categories().isEmpty()) { + QStringList categories; + for (int i = 0; i < m_candlestickSets.count(); ++i) { + const qint64 timestamp = qRound64(m_candlestickSets.at(i)->timestamp()); + const QString timestampFormat = m_chart->locale().dateTimeFormat(QLocale::ShortFormat); + categories << QDateTime::fromMSecsSinceEpoch(timestamp).toString(timestampFormat); + } + axis->append(categories); + } +} + +#include "moc_qcandlestickseries.cpp" +#include "moc_qcandlestickseries_p.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/candlestickchart/qcandlestickseries.h b/src/charts/candlestickchart/qcandlestickseries.h new file mode 100644 index 00000000..3dea6433 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickseries.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QCANDLESTICKSERIES_H +#define QCANDLESTICKSERIES_H + +#include <QtCharts/QAbstractSeries> + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickSeriesPrivate; +class QCandlestickSet; + +class QT_CHARTS_EXPORT QCandlestickSeries : public QAbstractSeries +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(qreal maximumColumnWidth READ maximumColumnWidth WRITE setMaximumColumnWidth NOTIFY maximumColumnWidthChanged) + Q_PROPERTY(qreal minimumColumnWidth READ minimumColumnWidth WRITE setMinimumColumnWidth NOTIFY minimumColumnWidthChanged) + Q_PROPERTY(qreal bodyWidth READ bodyWidth WRITE setBodyWidth NOTIFY bodyWidthChanged) + Q_PROPERTY(bool bodyOutlineVisible READ bodyOutlineVisible WRITE setBodyOutlineVisible NOTIFY bodyOutlineVisibilityChanged) + Q_PROPERTY(qreal capsWidth READ capsWidth WRITE setCapsWidth NOTIFY capsWidthChanged) + Q_PROPERTY(bool capsVisible READ capsVisible WRITE setCapsVisible NOTIFY capsVisibilityChanged) + Q_PROPERTY(QColor increasingColor READ increasingColor WRITE setIncreasingColor NOTIFY increasingColorChanged) + Q_PROPERTY(QColor decreasingColor READ decreasingColor WRITE setDecreasingColor NOTIFY decreasingColorChanged) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush NOTIFY brushChanged) + Q_PROPERTY(QPen pen READ pen WRITE setPen NOTIFY penChanged) + +public: + explicit QCandlestickSeries(QObject *parent = nullptr); + ~QCandlestickSeries(); + + bool append(QCandlestickSet *set); + bool remove(QCandlestickSet *set); + bool append(const QList<QCandlestickSet *> &sets); + bool remove(const QList<QCandlestickSet *> &sets); + bool insert(int index, QCandlestickSet *set); + bool take(QCandlestickSet *set); + void clear(); + + QList<QCandlestickSet *> candlestickSets() const; + int count() const; + + QAbstractSeries::SeriesType type() const; + + void setMaximumColumnWidth(qreal maximumColumnWidth); + qreal maximumColumnWidth() const; + + void setMinimumColumnWidth(qreal minimumColumnWidth); + qreal minimumColumnWidth() const; + + void setBodyWidth(qreal bodyWidth); + qreal bodyWidth() const; + + void setBodyOutlineVisible(bool bodyOutlineVisible); + bool bodyOutlineVisible() const; + + void setCapsWidth(qreal capsWidth); + qreal capsWidth() const; + + void setCapsVisible(bool capsVisible); + bool capsVisible() const; + + void setIncreasingColor(const QColor &increasingColor); + QColor increasingColor() const; + + void setDecreasingColor(const QColor &decreasingColor); + QColor decreasingColor() const; + + void setBrush(const QBrush &brush); + QBrush brush() const; + + void setPen(const QPen &pen); + QPen pen() const; + +Q_SIGNALS: + void clicked(QCandlestickSet *set); + void hovered(bool status, QCandlestickSet *set); + void pressed(QCandlestickSet *set); + void released(QCandlestickSet *set); + void doubleClicked(QCandlestickSet *set); + void candlestickSetsAdded(const QList<QCandlestickSet *> &sets); + void candlestickSetsRemoved(const QList<QCandlestickSet *> &sets); + void countChanged(); + void maximumColumnWidthChanged(); + void minimumColumnWidthChanged(); + void bodyWidthChanged(); + void bodyOutlineVisibilityChanged(); + void capsWidthChanged(); + void capsVisibilityChanged(); + void increasingColorChanged(); + void decreasingColorChanged(); + void brushChanged(); + void penChanged(); + +private: + Q_DISABLE_COPY(QCandlestickSeries) + Q_DECLARE_PRIVATE(QCandlestickSeries) + friend class CandlestickChartItem; + friend class QCandlestickLegendMarkerPrivate; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKSERIES_H diff --git a/src/charts/candlestickchart/qcandlestickseries_p.h b/src/charts/candlestickchart/qcandlestickseries_p.h new file mode 100644 index 00000000..7ee50103 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickseries_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QCANDLESTICKSERIES_P_H +#define QCANDLESTICKSERIES_P_H + +#include <private/qabstractseries_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +class CandlestickAnimation; +class QBarCategoryAxis; +class QCandlestickSeries; +class QCandlestickSet; +class QDateTimeAxis; + +class QCandlestickSeriesPrivate : public QAbstractSeriesPrivate +{ + Q_OBJECT + +public: + QCandlestickSeriesPrivate(QCandlestickSeries *q); + ~QCandlestickSeriesPrivate(); + + void initializeDomain(); + void initializeAxes(); + void initializeTheme(int index, ChartTheme* theme, bool forced = false); + void initializeGraphics(QGraphicsItem* parent); + void initializeAnimations(QChart::AnimationOptions options, int duration, QEasingCurve &curve); + + QList<QLegendMarker *> createLegendMarkers(QLegend *legend); + + virtual QAbstractAxis::AxisType defaultAxisType(Qt::Orientation orientation) const; + QAbstractAxis *createDefaultAxis(Qt::Orientation orientation) const; + + bool append(const QList<QCandlestickSet *> &sets); + bool remove(const QList<QCandlestickSet *> &sets); + bool insert(int index, QCandlestickSet *set); + +Q_SIGNALS: + void clicked(int index, QCandlestickSet *set); + void pressed(int index, QCandlestickSet *set); + void released(int index, QCandlestickSet *set); + void doubleClicked(int index, QCandlestickSet *set); + void updated(); + void updatedLayout(); + void updatedCandlesticks(); + +private Q_SLOTS: + void handleSeriesChange(QAbstractSeries *series); + void handleSeriesRemove(QAbstractSeries *series); + +private: + void populateBarCategories(QBarCategoryAxis *axis); + +protected: + QList<QCandlestickSet *> m_candlestickSets; + qreal m_maximumColumnWidth; + qreal m_minimumColumnWidth; + qreal m_bodyWidth; + bool m_bodyOutlineVisible; + qreal m_capsWidth; + bool m_capsVisible; + QColor m_increasingColor; + QColor m_decreasingColor; + bool m_customIncreasingColor; + bool m_customDecreasingColor; + QBrush m_brush; + QPen m_pen; + CandlestickAnimation *m_animation; + +private: + Q_DECLARE_PUBLIC(QCandlestickSeries) +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKSERIES_P_H diff --git a/src/charts/candlestickchart/qcandlestickset.cpp b/src/charts/candlestickchart/qcandlestickset.cpp new file mode 100644 index 00000000..99779ef7 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickset.cpp @@ -0,0 +1,487 @@ +/**************************************************************************** +** +** 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/QCandlestickSet> +#include <private/qcandlestickset_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +/*! + \class QCandlestickSet + \since 5.8 + \inmodule Qt Charts + \brief Building block for a candlestick chart. + + QCandlestickSet represents a single candlestick item in a QCandlestickSeries. It takes five + values to create a graphical representation of a candlestick item: \e open, \e high, \e low, + \e close, and \e timestamp. These values can be either passed to a QCandlestickSet constructor, + or set by using setOpen(), setHigh(), setLow(), setClose(), and setTimestamp(). + + \sa QCandlestickSeries +*/ + +/*! + \qmltype CandlestickSet + \since 2.2 + \instantiates QCandlestickSet + \inqmlmodule QtCharts + \brief Building block for a candlestick chart. + + CandlestickSet represents a single candlestick item in a CandlestickSeries. It takes five + values to create a graphical representation of a candlestick item: \l open, \l high, \l low, + \l close, and \l timestamp. + + \sa CandlestickSeries +*/ + +/*! + \property QCandlestickSet::timestamp + \brief The timestamp value of the set. +*/ + +/*! + \qmlproperty qreal CandlestickSet::timestamp + \brief The timestamp value of the set. +*/ + +/*! + \property QCandlestickSet::open + \brief The open value of the set. +*/ + +/*! + \qmlproperty qreal CandlestickSet::open + \brief The open value of the set. +*/ + +/*! + \property QCandlestickSet::high + \brief The high value of the set. +*/ + +/*! + \qmlproperty qreal CandlestickSet::high + \brief The high value of the set. +*/ + +/*! + \property QCandlestickSet::low + \brief The low value of the set. +*/ + +/*! + \qmlproperty qreal CandlestickSet::low + \brief The low value of the set. +*/ + +/*! + \property QCandlestickSet::close + \brief The close value of the set. +*/ + +/*! + \qmlproperty qreal CandlestickSet::close + \brief The close value of the set. +*/ + +/*! + \property QCandlestickSet::brush + \brief The brush used for drawing the candlestick. +*/ + +/*! + \property QCandlestickSet::pen + \brief The pen used for drawing the candlestick. +*/ + +/*! + \qmlproperty QString CandlestickSet::brushFilename + \brief The name of the file used as a brush for the set. +*/ + +/*! + \fn void QCandlestickSet::clicked() + \brief Emitted when the candlestick item is clicked (pressed and released). +*/ + +/*! + \qmlsignal CandlestickSet::clicked() + \brief Emitted when the candlestick item is clicked (pressed and released). + + The corresponding signal handler is \c {onClicked}. +*/ + +/*! + \fn void QCandlestickSet::hovered(bool status) + \brief Emitted when there is change in hover \a status over a candlestick item. + + Parameter \a status indicates whether the mouse has entered (\c true) or left (\c false) the + area of the candlestick item. +*/ + +/*! + \qmlsignal CandlestickSet::hovered(bool status) + \brief Emitted when there is change in hover \a status over a candlestick item. + + Parameter \a status indicates whether the mouse has entered (\c true) or left (\c false) the + area of the candlestick item. + + The corresponding signal handler is \c {onHovered}. +*/ + +/*! + \fn void QCandlestickSet::pressed() + \brief Emitted when there is a press on a candlestick item. +*/ + +/*! + \qmlsignal CandlestickSet::pressed() + \brief Emitted when there is a press on a candlestick item. + + The corresponding signal handler is \c {onPressed}. +*/ + +/*! + \fn void QCandlestickSet::released() + \brief Emitted when there is a release on a candlestick item. +*/ + +/*! + \qmlsignal CandlestickSet::released() + \brief Emitted when there is a release on a candlestick item. + + The corresponding signal handler is \c {onReleased}. +*/ + +/*! + \fn void QCandlestickSet::doubleClicked() + \brief Emitted when there is a double-click on a candlestick item. +*/ + +/*! + \qmlsignal CandlestickSet::doubleClicked() + \brief Emitted when there is a double-click on a candlestick item. + + The corresponding signal handler is \c {onDoubleClicked}. +*/ + +/*! + \fn void QCandlestickSet::timestampChanged() + \brief Emitted when the candlestick item timestamp is changed. + \sa timestamp +*/ + +/*! + \qmlsignal CandlestickSet::timestampChanged() + \brief Emitted when the candlestick item timestamp is changed. + \sa timestamp + + The corresponding signal handler is \c {onTimestampChanged}. +*/ + +/*! + \fn void QCandlestickSet::openChanged() + \brief Emitted when the candlestick item open value is changed. + \sa open +*/ + +/*! + \qmlsignal CandlestickSet::openChanged() + \brief Emitted when the candlestick item open value is changed. + \sa open + + The corresponding signal handler is \c {onOpenChanged}. +*/ + +/*! + \fn void QCandlestickSet::highChanged() + \brief Emitted when the candlestick item high value is changed. + \sa high +*/ + +/*! + \qmlsignal CandlestickSet::highChanged() + \brief Emitted when the candlestick item high value is changed. + \sa high + + The corresponding signal handler is \c {onHighChanged}. +*/ + +/*! + \fn void QCandlestickSet::lowChanged() + \brief Emitted when the candlestick item low value is changed. + \sa low +*/ + +/*! + \qmlsignal CandlestickSet::lowChanged() + \brief Emitted when the candlestick item low value is changed. + \sa low + + The corresponding signal handler is \c {onLowChanged}. +*/ + +/*! + \fn void QCandlestickSet::closeChanged() + \brief Emitted when the candlestick item close value is changed. + \sa close +*/ + +/*! + \qmlsignal CandlestickSet::closeChanged() + \brief Emitted when the candlestick item close value is changed. + \sa close + + The corresponding signal handler is \c {onCloseChanged}. +*/ + +/*! + \fn void QCandlestickSet::brushChanged() + \brief Emitted when the candlestick item brush is changed. + \sa brush +*/ + +/*! + \fn void QCandlestickSet::penChanged() + \brief Emitted when the candlestick item pen is changed. + \sa pen +*/ + +/*! + Constructs a QCandlestickSet with an optional \a timestamp and a \a parent. +*/ +QCandlestickSet::QCandlestickSet(qreal timestamp, QObject *parent) + : QObject(parent), + d_ptr(new QCandlestickSetPrivate(timestamp, this)) +{ +} + +/*! + Constructs a QCandlestickSet with given ordered values. The values \a open, \a high, \a low + and \a close are mandatory. The values \a timestamp and \a parent are optional. +*/ +QCandlestickSet::QCandlestickSet(qreal open, qreal high, qreal low, qreal close, qreal timestamp, + QObject *parent) + : QObject(parent), + d_ptr(new QCandlestickSetPrivate(timestamp, this)) +{ + Q_D(QCandlestickSet); + + d->m_open = open; + d->m_high = high; + d->m_low = low; + d->m_close = close; + + emit d->updatedLayout(); +} + +/*! + Destroys the set. +*/ +QCandlestickSet::~QCandlestickSet() +{ +} + +void QCandlestickSet::setTimestamp(qreal timestamp) +{ + Q_D(QCandlestickSet); + + bool changed = d->setTimestamp(timestamp); + if (!changed) + return; + + emit d->updatedLayout(); + emit timestampChanged(); +} + +qreal QCandlestickSet::timestamp() const +{ + Q_D(const QCandlestickSet); + + return d->m_timestamp; +} + +void QCandlestickSet::setOpen(qreal open) +{ + Q_D(QCandlestickSet); + + if (d->m_open == open) + return; + + d->m_open = open; + + emit d->updatedLayout(); + emit openChanged(); +} + +qreal QCandlestickSet::open() const +{ + Q_D(const QCandlestickSet); + + return d->m_open; +} + +void QCandlestickSet::setHigh(qreal high) +{ + Q_D(QCandlestickSet); + + if (d->m_high == high) + return; + + d->m_high = high; + + emit d->updatedLayout(); + emit highChanged(); +} + +qreal QCandlestickSet::high() const +{ + Q_D(const QCandlestickSet); + + return d->m_high; +} + +void QCandlestickSet::setLow(qreal low) +{ + Q_D(QCandlestickSet); + + if (d->m_low == low) + return; + + d->m_low = low; + + emit d->updatedLayout(); + emit lowChanged(); +} + +qreal QCandlestickSet::low() const +{ + Q_D(const QCandlestickSet); + + return d->m_low; +} + +void QCandlestickSet::setClose(qreal close) +{ + Q_D(QCandlestickSet); + + if (d->m_close == close) + return; + + d->m_close = close; + + emit d->updatedLayout(); + emit closeChanged(); +} + +qreal QCandlestickSet::close() const +{ + Q_D(const QCandlestickSet); + + return d->m_close; +} + +void QCandlestickSet::setBrush(const QBrush &brush) +{ + Q_D(QCandlestickSet); + + if (d->m_brush == brush) + return; + + d->m_brush = brush; + + emit d->updatedCandlestick(); + emit brushChanged(); +} + +QBrush QCandlestickSet::brush() const +{ + Q_D(const QCandlestickSet); + + return d->m_brush; +} + +void QCandlestickSet::setPen(const QPen &pen) +{ + Q_D(QCandlestickSet); + + if (d->m_pen == pen) + return; + + d->m_pen = pen; + + emit d->updatedCandlestick(); + emit penChanged(); +} + +QPen QCandlestickSet::pen() const +{ + Q_D(const QCandlestickSet); + + return d->m_pen; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +QCandlestickSetPrivate::QCandlestickSetPrivate(qreal timestamp, QCandlestickSet *parent) + : QObject(parent), + q_ptr(parent), + m_timestamp(0.0), + m_open(0.0), + m_high(0.0), + m_low(0.0), + m_close(0.0), + m_brush(QBrush(Qt::NoBrush)), + m_pen(QPen(Qt::NoPen)), + m_series(nullptr) +{ + setTimestamp(timestamp); +} + +QCandlestickSetPrivate::~QCandlestickSetPrivate() +{ +} + +bool QCandlestickSetPrivate::setTimestamp(qreal timestamp) +{ + timestamp = qMax(timestamp, 0.0); + timestamp = qRound64(timestamp); + + if (m_timestamp == timestamp) + return false; + + m_timestamp = timestamp; + + return true; +} + +#include "moc_qcandlestickset.cpp" +#include "moc_qcandlestickset_p.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/candlestickchart/qcandlestickset.h b/src/charts/candlestickchart/qcandlestickset.h new file mode 100644 index 00000000..ddbb6c16 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickset.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QCANDLESTICKSET_H +#define QCANDLESTICKSET_H + +#include <QtCharts/QChartGlobal> +#include <QtGui/QBrush> +#include <QtGui/QPen> + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickSetPrivate; + +class QT_CHARTS_EXPORT QCandlestickSet : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal timestamp READ timestamp WRITE setTimestamp NOTIFY timestampChanged) + Q_PROPERTY(qreal open READ open WRITE setOpen NOTIFY openChanged) + Q_PROPERTY(qreal high READ high WRITE setHigh NOTIFY highChanged) + Q_PROPERTY(qreal low READ low WRITE setLow NOTIFY lowChanged) + Q_PROPERTY(qreal close READ close WRITE setClose NOTIFY closeChanged) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush NOTIFY brushChanged) + Q_PROPERTY(QPen pen READ pen WRITE setPen NOTIFY penChanged) + +public: + explicit QCandlestickSet(qreal timestamp = 0.0, QObject *parent = nullptr); + explicit QCandlestickSet(qreal open, qreal high, qreal low, qreal close, qreal timestamp = 0.0, + QObject *parent = nullptr); + virtual ~QCandlestickSet(); + + void setTimestamp(qreal timestamp); + qreal timestamp() const; + + void setOpen(qreal open); + qreal open() const; + + void setHigh(qreal high); + qreal high() const; + + void setLow(qreal low); + qreal low() const; + + void setClose(qreal close); + qreal close() const; + + void setBrush(const QBrush &brush); + QBrush brush() const; + + void setPen(const QPen &pen); + QPen pen() const; + +Q_SIGNALS: + void clicked(); + void hovered(bool status); + void pressed(); + void released(); + void doubleClicked(); + void timestampChanged(); + void openChanged(); + void highChanged(); + void lowChanged(); + void closeChanged(); + void brushChanged(); + void penChanged(); + +private: + QScopedPointer<QCandlestickSetPrivate> d_ptr; + Q_DECLARE_PRIVATE(QCandlestickSet) + Q_DISABLE_COPY(QCandlestickSet) + friend class QCandlestickSeriesPrivate; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKSET_H diff --git a/src/charts/candlestickchart/qcandlestickset_p.h b/src/charts/candlestickchart/qcandlestickset_p.h new file mode 100644 index 00000000..327e4ee8 --- /dev/null +++ b/src/charts/candlestickchart/qcandlestickset_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QCANDLESTICKSET_P_H +#define QCANDLESTICKSET_P_H + +#include <QtCharts/QChartGlobal> +#include <QtGui/QBrush> +#include <QtGui/QPen> + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickSeriesPrivate; +class QCandlestickSet; + +class QCandlestickSetPrivate : public QObject +{ + Q_OBJECT + +public: + QCandlestickSetPrivate(qreal timestamp, QCandlestickSet *parent); + ~QCandlestickSetPrivate(); + + bool setTimestamp(qreal timestamp); + +Q_SIGNALS: + void updatedLayout(); + void updatedCandlestick(); + +private: + QCandlestickSet *q_ptr; + qreal m_timestamp; + qreal m_open; + qreal m_high; + qreal m_low; + qreal m_close; + QBrush m_brush; + QPen m_pen; + QCandlestickSeriesPrivate *m_series; + +private: + Q_DECLARE_PUBLIC(QCandlestickSet) + friend class QCandlestickSeriesPrivate; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKSET_P_H diff --git a/src/charts/candlestickchart/qhcandlestickmodelmapper.cpp b/src/charts/candlestickchart/qhcandlestickmodelmapper.cpp new file mode 100644 index 00000000..b27d37d6 --- /dev/null +++ b/src/charts/candlestickchart/qhcandlestickmodelmapper.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** 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/QHCandlestickModelMapper> +#include <private/qcandlestickmodelmapper_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +/*! + \class QHCandlestickModelMapper + \since 5.8 + \inmodule Qt Charts + \brief Horizontal model mapper for a 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 horizontal + model mapper maintains an equal size across all \l {QCandlestickSet} {QCandlestickSets}, and + reads the values of the set from the model's rows. + + \note The model used must support adding and removing rows/columns and modifying the data of the + cells. +*/ + +/*! + \qmltype HCandlestickModelMapper + \since 2.2 + \instantiates QHCandlestickModelMapper + \inqmlmodule QtCharts + \brief Horizontal model mapper for a candlestick series. + + HCandlestickModelMapper allows the use of a QAbstractItemModel-derived model with data in rows + as a data source for a candlestick series. It's possible to manipulate the data either through + QAbstractItemModel or QCandlestickSeries. + + The following QML example creates a candlestick series with three candlestick sets (assuming the + model has at least four rows). Each candlestick set would contain data defined by timestamp, + open, high, low and close columns. The name of a set would be defined by the vertical header of + the row. + \qml + CandlestickSeries { + HCandlestickModelMapper { + model: myCustomModel // QAbstractItemModel derived implementation + timestampColumn: 1 + openColumn: 2 + highColumn: 3 + lowColumn: 4 + closeColumn: 5 + firstCandlestickSetRow: 1 + lastCandlestickSetRow: 3 + } + } + \endqml + + \note HCandlestickModelMapper keeps the series and the model in sync. +*/ + +/*! + \qmlproperty QAbstractItemModel HCandlestickModelMapper::model + \brief The QAbstractItemModel-based model that is used by the mapper. The model must be + implemented and exposed to QML. + + \note The model used must support adding and removing rows/columns and modifying the data of the + cells. +*/ + +/*! + \qmlproperty CandlestickSeries HCandlestickModelMapper::series + \brief Defines the CandlestickSeries based object that is used by the mapper. + + All the 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). +*/ + +/*! + \property QHCandlestickModelMapper::timestampColumn + \brief Defines the column of the model that contains the timestamp values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::timestampColumn + \brief Defines the column of the model that contains the timestamp values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QHCandlestickModelMapper::openColumn + \brief Defines the column of the model that contains the open values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::openColumn + \brief Defines the column of the model that contains the open values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QHCandlestickModelMapper::highColumn + \brief Defines the column of the model that contains the high values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::highColumn + \brief Defines the column of the model that contains the high values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QHCandlestickModelMapper::lowColumn + \brief Defines the column of the model that contains the low values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::lowColumn + \brief Defines the column of the model that contains the low values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QHCandlestickModelMapper::closeColumn + \brief Defines the column of the model that contains the close values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::closeColumn + \brief Defines the column of the model that contains the close values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QHCandlestickModelMapper::firstCandlestickSetRow + \brief Defines the row of the model that is used as the data source for the first set. Default + value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::firstCandlestickSetRow + \brief Defines the row of the model that is used as the data source for the first set. Default + value is -1 (invalid mapping). +*/ + +/*! + \property QHCandlestickModelMapper::lastCandlestickSetRow + \brief Defines the row of the model that is used as the data source for the last set. Default + value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int HCandlestickModelMapper::lastCandlestickSetRow + \brief Defines the row of the model that is used as the data source for the last set. Default + value is -1 (invalid mapping). +*/ + +/*! + \fn void QHCandlestickModelMapper::timestampColumnChanged() + \brief Emitted when the column of the model that contains timestamp values is changed + \sa timestampColumn +*/ + +/*! + \fn void QHCandlestickModelMapper::openColumnChanged() + \brief Emitted when the column of the model that contains open values is changed. + \sa openColumn +*/ +/*! + \fn void QHCandlestickModelMapper::highColumnChanged() + \brief Emitted when the column of the model that contains high values is changed. + \sa highColumn +*/ + +/*! + \fn void QHCandlestickModelMapper::lowColumnChanged() + \brief Emitted when the column of the model that contains low values is changed. + \sa lowColumn +*/ + +/*! + \fn void QHCandlestickModelMapper::closeColumnChanged() + \brief Emitted when the column of the model that contains close values is changed. + \sa closeColumn +*/ + +/*! + \fn void QHCandlestickModelMapper::firstCandlestickSetRowChanged() + \brief Emitted when the row of the model that contains the data of the first set is changed. + \sa firstCandlestickSetRow +*/ + +/*! + \fn void QHCandlestickModelMapper::lastCandlestickSetRowChanged() + \brief Emitted when the row of the model that contains the data of the last set is changed. + \sa lastCandlestickSetRow +*/ + +/*! + Constructs a horizontal model mapper object which is a child of \a parent. +*/ +QHCandlestickModelMapper::QHCandlestickModelMapper(QObject *parent) + : QCandlestickModelMapper(parent) +{ + connect(d_ptr, SIGNAL(timestampChanged()), this, SIGNAL(timestampColumnChanged())); + connect(d_ptr, SIGNAL(openChanged()), this, SIGNAL(openColumnChanged())); + connect(d_ptr, SIGNAL(highChanged()), this, SIGNAL(highColumnChanged())); + connect(d_ptr, SIGNAL(lowChanged()), this, SIGNAL(lowColumnChanged())); + connect(d_ptr, SIGNAL(closeChanged()), this, SIGNAL(closeColumnChanged())); + connect(d_ptr, SIGNAL(firstCandlestickSetSectionChanged()), + this, SIGNAL(firstCandlestickSetRowChanged())); + connect(d_ptr, SIGNAL(lastCandlestickSetSectionChanged()), + this, SIGNAL(lastCandlestickSetRowChanged())); +} + +/*! + Returns Qt::Horizontal. This means that values of the set are read from rows. +*/ +Qt::Orientation QHCandlestickModelMapper::orientation() const +{ + return Qt::Horizontal; +} + +void QHCandlestickModelMapper::setTimestampColumn(int timestampColumn) +{ + QCandlestickModelMapper::setTimestamp(timestampColumn); +} + +int QHCandlestickModelMapper::timestampColumn() const +{ + return QCandlestickModelMapper::timestamp(); +} + +void QHCandlestickModelMapper::setOpenColumn(int openColumn) +{ + QCandlestickModelMapper::setOpen(openColumn); +} + +int QHCandlestickModelMapper::openColumn() const +{ + return QCandlestickModelMapper::open(); +} + +void QHCandlestickModelMapper::setHighColumn(int highColumn) +{ + QCandlestickModelMapper::setHigh(highColumn); +} + +int QHCandlestickModelMapper::highColumn() const +{ + return QCandlestickModelMapper::high(); +} + +void QHCandlestickModelMapper::setLowColumn(int lowColumn) +{ + QCandlestickModelMapper::setLow(lowColumn); +} + +int QHCandlestickModelMapper::lowColumn() const +{ + return QCandlestickModelMapper::low(); +} + +void QHCandlestickModelMapper::setCloseColumn(int closeColumn) +{ + QCandlestickModelMapper::setClose(closeColumn); +} + +int QHCandlestickModelMapper::closeColumn() const +{ + return QCandlestickModelMapper::close(); +} + +void QHCandlestickModelMapper::setFirstCandlestickSetRow(int firstCandlestickSetRow) +{ + QCandlestickModelMapper::setFirstCandlestickSetSection(firstCandlestickSetRow); +} + +int QHCandlestickModelMapper::firstCandlestickSetRow() const +{ + return QCandlestickModelMapper::firstCandlestickSetSection(); +} + +void QHCandlestickModelMapper::setLastCandlestickSetRow(int lastCandlestickSetRow) +{ + QCandlestickModelMapper::setLastCandlestickSetSection(lastCandlestickSetRow); +} + +int QHCandlestickModelMapper::lastCandlestickSetRow() const +{ + return QCandlestickModelMapper::lastCandlestickSetSection(); +} + +#include "moc_qhcandlestickmodelmapper.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/candlestickchart/qhcandlestickmodelmapper.h b/src/charts/candlestickchart/qhcandlestickmodelmapper.h new file mode 100644 index 00000000..5bae93c2 --- /dev/null +++ b/src/charts/candlestickchart/qhcandlestickmodelmapper.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QHCANDLESTICKMODELMAPPER_H +#define QHCANDLESTICKMODELMAPPER_H + +#include <QtCharts/QCandlestickModelMapper> + +QT_CHARTS_BEGIN_NAMESPACE +/* Comment line for syncqt to generate the fwd-include correctly, due to QTBUG-22432 */ +class QT_CHARTS_EXPORT QHCandlestickModelMapper : public QCandlestickModelMapper +{ + Q_OBJECT + Q_PROPERTY(int timestampColumn READ timestampColumn WRITE setTimestampColumn NOTIFY timestampColumnChanged) + Q_PROPERTY(int openColumn READ openColumn WRITE setOpenColumn NOTIFY openColumnChanged) + Q_PROPERTY(int highColumn READ highColumn WRITE setHighColumn NOTIFY highColumnChanged) + Q_PROPERTY(int lowColumn READ lowColumn WRITE setLowColumn NOTIFY lowColumnChanged) + Q_PROPERTY(int closeColumn READ closeColumn WRITE setCloseColumn NOTIFY closeColumnChanged) + Q_PROPERTY(int firstCandlestickSetRow READ firstCandlestickSetRow WRITE setFirstCandlestickSetRow NOTIFY firstCandlestickSetRowChanged) + Q_PROPERTY(int lastCandlestickSetRow READ lastCandlestickSetRow WRITE setLastCandlestickSetRow NOTIFY lastCandlestickSetRowChanged) + +public: + explicit QHCandlestickModelMapper(QObject *parent = nullptr); + + Qt::Orientation orientation() const; + + void setTimestampColumn(int timestampColumn); + int timestampColumn() const; + + void setOpenColumn(int openColumn); + int openColumn() const; + + void setHighColumn(int highColumn); + int highColumn() const; + + void setLowColumn(int lowColumn); + int lowColumn() const; + + void setCloseColumn(int closeColumn); + int closeColumn() const; + + void setFirstCandlestickSetRow(int firstCandlestickSetRow); + int firstCandlestickSetRow() const; + + void setLastCandlestickSetRow(int lastCandlestickSetRow); + int lastCandlestickSetRow() const; + +Q_SIGNALS: + void timestampColumnChanged(); + void openColumnChanged(); + void highColumnChanged(); + void lowColumnChanged(); + void closeColumnChanged(); + void firstCandlestickSetRowChanged(); + void lastCandlestickSetRowChanged(); +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QHCANDLESTICKMODELMAPPER_H diff --git a/src/charts/candlestickchart/qvcandlestickmodelmapper.cpp b/src/charts/candlestickchart/qvcandlestickmodelmapper.cpp new file mode 100644 index 00000000..990c438c --- /dev/null +++ b/src/charts/candlestickchart/qvcandlestickmodelmapper.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** 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/QVCandlestickModelMapper> +#include <private/qcandlestickmodelmapper_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +/*! + \class QVCandlestickModelMapper + \since 5.8 + \inmodule Qt Charts + \brief Vertical model mapper for a 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 vertical + model mapper maintains an equal size across all \l {QCandlestickSet} {QCandlestickSets}, and + reads the values of the set from the model's columns. + + \note The model used must support adding and removing rows/columns and modifying the data of the + cells. +*/ + +/*! + \qmltype VCandlestickModelMapper + \since 2.2 + \instantiates QVCandlestickModelMapper + \inqmlmodule QtCharts + \brief Vertical model mapper for a candlestick series. + + VCandlestickModelMapper allows the use of a QAbstractItemModel-derived model with data in + columns as a data source for a candlestick series. It's possible to manipulate the data either + through QAbstractItemModel or QCandlestickSeries. + + The following QML example creates a candlestick series with three candlestick sets (assuming the + model has at least four columns). Each candlestick set would contain data defined by timestamp, + open, high, low and close rows. The name of a set would be defined by the horizontal header of + the column. + \qml + CandlestickSeries { + VCandlestickModelMapper { + model: myCustomModel // QAbstractItemModel derived implementation + timestampRow: 1 + openRow: 2 + highRow: 3 + lowRow: 4 + closeRow: 5 + firstCandlestickSetColumn: 1 + lastCandlestickSetColumn: 3 + } + } + \endqml + + \note VCandlestickModelMapper keeps the series and the model in sync. +*/ + +/*! + \qmlproperty QAbstractItemModel VCandlestickModelMapper::model + \brief The QAbstractItemModel-based model that is used by the mapper. The model must be + implemented and exposed to QML. + + \note The model used must support adding and removing rows/columns and modifying the data of the + cells. +*/ + +/*! + \qmlproperty CandlestickSeries VCandlestickModelMapper::series + \brief Defines the CandlestickSeries based object that is used by the mapper. + + All the 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). +*/ + +/*! + \property QVCandlestickModelMapper::timestampRow + \brief Defines the row of the model that contains the timestamp values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::timestampRow + \brief Defines the row of the model that contains the timestamp values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QVCandlestickModelMapper::openRow + \brief Defines the row of the model that contains the open values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::openRow + \brief Defines the row of the model that contains the open values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QVCandlestickModelMapper::highRow + \brief Defines the row of the model that contains the high values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::highRow + \brief Defines the row of the model that contains the high values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QVCandlestickModelMapper::lowRow + \brief Defines the row of the model that contains the low values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::lowRow + \brief Defines the row of the model that contains the low values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QVCandlestickModelMapper::closeRow + \brief Defines the row of the model that contains the close values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::closeRow + \brief Defines the row of the model that contains the close values of the + \l {QCandlestickSet} {QCandlestickSets} in the series. Default value is -1 (invalid mapping). +*/ + +/*! + \property QVCandlestickModelMapper::firstCandlestickSetColumn + \brief Defines the column of the model that is used as the data source for the first set. + Default value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::firstCandlestickSetColumn + \brief Defines the column of the model that is used as the data source for the first set. + Default value is -1 (invalid mapping). +*/ + +/*! + \property QVCandlestickModelMapper::lastCandlestickSetColumn + \brief Defines the column of the model that is used as the data source for the last set. Default + value is -1 (invalid mapping). +*/ + +/*! + \qmlproperty int VCandlestickModelMapper::lastCandlestickSetColumn + \brief Defines the column of the model that is used as the data source for the last set. Default + value is -1 (invalid mapping). +*/ + +/*! + \fn void QVCandlestickModelMapper::timestampRowChanged() + \brief Emitted when the row of the model that contains timestamp values is changed. + \sa timestampRow +*/ + +/*! + \fn void QVCandlestickModelMapper::openRowChanged() + \brief Emitted when the row of the model that contains open values is changed. + \sa openRow +*/ + +/*! + \fn void QVCandlestickModelMapper::highRowChanged() + \brief Emitted when the row of the model that contains high values is changed. + \sa highRow +*/ + +/*! + \fn void QVCandlestickModelMapper::lowRowChanged() + \brief Emitted when the row of the model that contains low values is changed. + \sa lowRow +*/ + +/*! + \fn void QVCandlestickModelMapper::closeRowChanged() + \brief Emitted when the row of the model that contains close values is changed. + \sa closeRow +*/ + +/*! + \fn void QVCandlestickModelMapper::firstCandlestickSetColumnChanged() + \brief Emitted when the column of the model that contains the data of the first set is changed. + \sa firstCandlestickSetColumn +*/ + +/*! + \fn void QVCandlestickModelMapper::lastCandlestickSetColumnChanged() + \brief Emitted when the column of the model that contains the data of the last set is changed. + \sa lastCandlestickSetColumn +*/ + +/*! + Constructs a vertical model mapper object which is a child of \a parent. +*/ +QVCandlestickModelMapper::QVCandlestickModelMapper(QObject *parent) + : QCandlestickModelMapper(parent) +{ + connect(d_ptr, SIGNAL(timestampChanged()), this, SIGNAL(timestampRowChanged())); + connect(d_ptr, SIGNAL(openChanged()), this, SIGNAL(openRowChanged())); + connect(d_ptr, SIGNAL(highChanged()), this, SIGNAL(highRowChanged())); + connect(d_ptr, SIGNAL(lowChanged()), this, SIGNAL(lowRowChanged())); + connect(d_ptr, SIGNAL(closeChanged()), this, SIGNAL(closeRowChanged())); + connect(d_ptr, SIGNAL(firstCandlestickSetSectionChanged()), + this, SIGNAL(firstCandlestickSetColumnChanged())); + connect(d_ptr, SIGNAL(lastCandlestickSetSectionChanged()), + this, SIGNAL(lastCandlestickSetColumnChanged())); +} + +/*! + Returns Qt::Vertical. This means that values of the set are read from columns. +*/ +Qt::Orientation QVCandlestickModelMapper::orientation() const +{ + return Qt::Vertical; +} + +void QVCandlestickModelMapper::setTimestampRow(int timestampRow) +{ + QCandlestickModelMapper::setTimestamp(timestampRow); +} + +int QVCandlestickModelMapper::timestampRow() const +{ + return QCandlestickModelMapper::timestamp(); +} + +void QVCandlestickModelMapper::setOpenRow(int openRow) +{ + QCandlestickModelMapper::setOpen(openRow); +} + +int QVCandlestickModelMapper::openRow() const +{ + return QCandlestickModelMapper::open(); +} + +void QVCandlestickModelMapper::setHighRow(int highRow) +{ + QCandlestickModelMapper::setHigh(highRow); +} + +int QVCandlestickModelMapper::highRow() const +{ + return QCandlestickModelMapper::high(); +} + +void QVCandlestickModelMapper::setLowRow(int lowRow) +{ + QCandlestickModelMapper::setLow(lowRow); +} + +int QVCandlestickModelMapper::lowRow() const +{ + return QCandlestickModelMapper::low(); +} + +void QVCandlestickModelMapper::setCloseRow(int closeRow) +{ + QCandlestickModelMapper::setClose(closeRow); +} + +int QVCandlestickModelMapper::closeRow() const +{ + return QCandlestickModelMapper::close(); +} + +void QVCandlestickModelMapper::setFirstCandlestickSetColumn(int firstCandlestickSetColumn) +{ + QCandlestickModelMapper::setFirstCandlestickSetSection(firstCandlestickSetColumn); +} + +int QVCandlestickModelMapper::firstCandlestickSetColumn() const +{ + return QCandlestickModelMapper::firstCandlestickSetSection(); +} + +void QVCandlestickModelMapper::setLastCandlestickSetColumn(int lastCandlestickSetColumn) +{ + QCandlestickModelMapper::setLastCandlestickSetSection(lastCandlestickSetColumn); +} + +int QVCandlestickModelMapper::lastCandlestickSetColumn() const +{ + return QCandlestickModelMapper::lastCandlestickSetSection(); +} + +#include "moc_qvcandlestickmodelmapper.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/candlestickchart/qvcandlestickmodelmapper.h b/src/charts/candlestickchart/qvcandlestickmodelmapper.h new file mode 100644 index 00000000..5cd20f9d --- /dev/null +++ b/src/charts/candlestickchart/qvcandlestickmodelmapper.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QVCANDLESTICKMODELMAPPER_H +#define QVCANDLESTICKMODELMAPPER_H + +#include <QtCharts/QCandlestickModelMapper> + +QT_CHARTS_BEGIN_NAMESPACE +/* Comment line for syncqt to generate the fwd-include correctly, due to QTBUG-22432 */ +class QT_CHARTS_EXPORT QVCandlestickModelMapper : public QCandlestickModelMapper +{ + Q_OBJECT + Q_PROPERTY(int timestampRow READ timestampRow WRITE setTimestampRow NOTIFY timestampRowChanged) + Q_PROPERTY(int openRow READ openRow WRITE setOpenRow NOTIFY openRowChanged) + Q_PROPERTY(int highRow READ highRow WRITE setHighRow NOTIFY highRowChanged) + Q_PROPERTY(int lowRow READ lowRow WRITE setLowRow NOTIFY lowRowChanged) + Q_PROPERTY(int closeRow READ closeRow WRITE setCloseRow NOTIFY closeRowChanged) + Q_PROPERTY(int firstCandlestickSetColumn READ firstCandlestickSetColumn WRITE setFirstCandlestickSetColumn NOTIFY firstCandlestickSetColumnChanged) + Q_PROPERTY(int lastCandlestickSetColumn READ lastCandlestickSetColumn WRITE setLastCandlestickSetColumn NOTIFY lastCandlestickSetColumnChanged) + +public: + explicit QVCandlestickModelMapper(QObject *parent = nullptr); + + Qt::Orientation orientation() const; + + void setTimestampRow(int timestampRow); + int timestampRow() const; + + void setOpenRow(int openRow); + int openRow() const; + + void setHighRow(int highRow); + int highRow() const; + + void setLowRow(int lowRow); + int lowRow() const; + + void setCloseRow(int closeRow); + int closeRow() const; + + void setFirstCandlestickSetColumn(int firstCandlestickSetColumn); + int firstCandlestickSetColumn() const; + + void setLastCandlestickSetColumn(int lastCandlestickSetColumn); + int lastCandlestickSetColumn() const; + +Q_SIGNALS: + void timestampRowChanged(); + void openRowChanged(); + void highRowChanged(); + void lowRowChanged(); + void closeRowChanged(); + void firstCandlestickSetColumnChanged(); + void lastCandlestickSetColumnChanged(); +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QVCANDLESTICKMODELMAPPER_H diff --git a/src/charts/chartdataset.cpp b/src/charts/chartdataset.cpp index cbc4dfbd..97602b57 100644 --- a/src/charts/chartdataset.cpp +++ b/src/charts/chartdataset.cpp @@ -153,20 +153,19 @@ void ChartDataSet::addAxis(QAbstractAxis *axis, Qt::Alignment aligment) */ void ChartDataSet::removeSeries(QAbstractSeries *series) { - if (! m_seriesList.contains(series)) { qWarning() << QObject::tr("Can not remove series. Series not found on the chart."); return; } - QList<QAbstractAxis*> axes = series->d_ptr->m_axes; + QList<QAbstractAxis *> axes = series->d_ptr->m_axes; - foreach(QAbstractAxis* axis, axes) { - detachAxis(series,axis); + foreach (QAbstractAxis *axis, axes) { + detachAxis(series, axis); } - emit seriesRemoved(series); m_seriesList.removeAll(series); + emit seriesRemoved(series); // Reset domain to default series->d_ptr->setDomain(new XYDomain()); diff --git a/src/charts/chartpresenter_p.h b/src/charts/chartpresenter_p.h index 5b713799..e3db5ff8 100644 --- a/src/charts/chartpresenter_p.h +++ b/src/charts/chartpresenter_p.h @@ -79,6 +79,7 @@ public: ScatterSeriesZValue = SeriesZValue, PieSeriesZValue = SeriesZValue, BoxPlotSeriesZValue = SeriesZValue, + CandlestickSeriesZValue = SeriesZValue, LegendZValue, TopMostZValue }; diff --git a/src/charts/charts.pro b/src/charts/charts.pro index 30546fd0..20f9d005 100644 --- a/src/charts/charts.pro +++ b/src/charts/charts.pro @@ -77,6 +77,7 @@ include($$PWD/themes/themes.pri) include($$PWD/xychart/xychart.pri) include($$PWD/layout/layout.pri) include($$PWD/boxplotchart/boxplotchart.pri) +include($$PWD/candlestickchart/candlestickchart.pri) HEADERS += $$PUBLIC_HEADERS HEADERS += $$PRIVATE_HEADERS diff --git a/src/charts/common.pri b/src/charts/common.pri index 1d5b6494..18dee553 100644 --- a/src/charts/common.pri +++ b/src/charts/common.pri @@ -4,6 +4,7 @@ INCLUDEPATH += $$PWD/ \ $$PWD/axis \ $$PWD/barchart \ $$PWD/boxplotchart \ + $$PWD/candlestickchart \ $$PWD/domain \ $$PWD/layout \ $$PWD/legend \ diff --git a/src/charts/doc/images/examples_candlestickchart.png b/src/charts/doc/images/examples_candlestickchart.png Binary files differnew file mode 100644 index 00000000..ac8cebeb --- /dev/null +++ b/src/charts/doc/images/examples_candlestickchart.png diff --git a/src/charts/doc/images/examples_qmlcandlestick.png b/src/charts/doc/images/examples_qmlcandlestick.png Binary files differnew file mode 100755 index 00000000..c380445f --- /dev/null +++ b/src/charts/doc/images/examples_qmlcandlestick.png diff --git a/src/charts/doc/snippets/doc_src_qmlcharts.cpp b/src/charts/doc/snippets/doc_src_qmlcharts.cpp index c90c02ee..b087fa95 100644 --- a/src/charts/doc/snippets/doc_src_qmlcharts.cpp +++ b/src/charts/doc/snippets/doc_src_qmlcharts.cpp @@ -28,5 +28,5 @@ ****************************************************************************/ //! [0] -import QtCharts 2.0 +import QtCharts 2.2 //! [0] diff --git a/src/charts/doc/src/examples-candlestickchart.qdoc b/src/charts/doc/src/examples-candlestickchart.qdoc new file mode 100644 index 00000000..2d4d63bc --- /dev/null +++ b/src/charts/doc/src/examples-candlestickchart.qdoc @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/*! + \example candlestickchart + \title Candlestick Chart Example + \ingroup qtcharts_examples + + \brief Shows how to create a candlestick chart. + + \image examples_candlestickchart.png + + To display a candlestick chart, we start by creating QCandlestickSeries to handle daily data. We + are also specifying custom increasing and decreasing body colors. + + \snippet candlestickchart/main.cpp 1 + + QFile is used for accessing a text file where the non-continuous data is kept. The + \c CandlestickDataReader is an auxiliary class for reading the text file and finding the open, + high, low, close, and timestamp values from the data. The \c CandlestickDataReader is explained + in more detail later. The method \c readCandlestickSet() reads the values and sets them to the + QCandlestickSet item which the method returns for the caller. The returned QCandlestickSet item + is added to the series. We are also saving custom categories list for later use. + + \snippet candlestickchart/main.cpp 2 + + Below, a new QChart instance is created and the previously created series object is added to it. + We also define a title, and set an animation as + \l {QChart::AnimationOption} {QChart::SeriesAnimation}. + + \snippet candlestickchart/main.cpp 3 + + Here, we ask the chart to create default axes for our presentation. Then, we set custom + categories for the horizontal axis by querying the pointer for the axis from the chart, and then + setting the categories from previously saved custom categories list. We also set the range for + the vertical axis by querying the pointer for the axis from the chart, and then setting the min + and max values for that axis. + + \snippet candlestickchart/main.cpp 4 + + Below, we set the legend to be visible and place it at the bottom of the chart. + + \snippet candlestickchart/main.cpp 5 + + Finally, we add the chart onto a view. We also turn on the antialiasing for the chartView. + + \snippet candlestickchart/main.cpp 6 + + The chart is ready to be shown. We set the chart to be the central widget of the window. We also + set the size for the chart window and show it. + + \snippet candlestickchart/main.cpp 7 + + Here, the method \c readCandlestickSet() is explained in detail. First, a line is read from + the file, rejecting any lines starting with # as they are considered comment lines. + + \snippet candlestickchart/candlestickdatareader.cpp 1 + + In the file, the data is arranged as a space-delimited sequence of numbers. On this snippet the + line is split into single number strings which are stored in a QStringList. + + \snippet candlestickchart/candlestickdatareader.cpp 2 + + To select values from the continuous data, following code is used. The values in a \c strList + are stored in the following order: timestamp, open, high, low, close. + + \snippet candlestickchart/candlestickdatareader.cpp 3 + + Finally, the following snippet shows how to create a new QCandlestickSet and provide it with all + the necessary values. + + \snippet candlestickchart/candlestickdatareader.cpp 4 +*/ diff --git a/src/charts/doc/src/qtcharts.qdoc b/src/charts/doc/src/qtcharts.qdoc index e83e2631..fff47320 100644 --- a/src/charts/doc/src/qtcharts.qdoc +++ b/src/charts/doc/src/qtcharts.qdoc @@ -50,7 +50,7 @@ */ /*! - \qmlmodule QtCharts 2.1 + \qmlmodule QtCharts 2.2 \title Qt Charts QML Types \ingroup qmlmodules diff --git a/src/charts/legend/legend.pri b/src/charts/legend/legend.pri index c14cf6b0..5fcb53c7 100644 --- a/src/charts/legend/legend.pri +++ b/src/charts/legend/legend.pri @@ -11,8 +11,9 @@ SOURCES += \ $$PWD/qxylegendmarker.cpp \ $$PWD/qarealegendmarker.cpp \ $$PWD/legendscroller.cpp \ - $$PWD/qboxplotlegendmarker.cpp - + $$PWD/qboxplotlegendmarker.cpp \ + $$PWD/qcandlesticklegendmarker.cpp + PRIVATE_HEADERS += \ $$PWD/legendscroller_p.h \ $$PWD/qlegend_p.h \ @@ -23,9 +24,9 @@ PRIVATE_HEADERS += \ $$PWD/qbarlegendmarker_p.h \ $$PWD/qxylegendmarker_p.h \ $$PWD/qarealegendmarker_p.h \ - $$PWD/qboxplotlegendmarker_p.h - - + $$PWD/qboxplotlegendmarker_p.h \ + $$PWD/qcandlesticklegendmarker_p.h + PUBLIC_HEADERS += \ $$PWD/qlegend.h \ $$PWD/qlegendmarker.h \ @@ -33,4 +34,5 @@ PUBLIC_HEADERS += \ $$PWD/qbarlegendmarker.h \ $$PWD/qxylegendmarker.h \ $$PWD/qarealegendmarker.h \ - $$PWD/qboxplotlegendmarker.h + $$PWD/qboxplotlegendmarker.h \ + $$PWD/qcandlesticklegendmarker.h diff --git a/src/charts/legend/legendmarkeritem.cpp b/src/charts/legend/legendmarkeritem.cpp index 125199ea..1c0b5901 100644 --- a/src/charts/legend/legendmarkeritem.cpp +++ b/src/charts/legend/legendmarkeritem.cpp @@ -88,8 +88,14 @@ QBrush LegendMarkerItem::brush() const void LegendMarkerItem::setFont(const QFont &font) { m_textItem->setFont(font); + QFontMetrics fn(font); - m_markerRect = QRectF(0,0,fn.height()/2,fn.height()/2); + QRectF markerRect = QRectF(0, 0, fn.height() / 2, fn.height() / 2); + if (m_markerRect != markerRect) { + m_markerRect = markerRect; + emit markerRectChanged(); + } + updateGeometry(); } @@ -152,6 +158,11 @@ QRectF LegendMarkerItem::boundingRect() const return m_boundingRect; } +QRectF LegendMarkerItem::markerRect() const +{ + return m_markerRect; +} + void LegendMarkerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option) diff --git a/src/charts/legend/legendmarkeritem_p.h b/src/charts/legend/legendmarkeritem_p.h index 870cfb2e..c3371742 100644 --- a/src/charts/legend/legendmarkeritem_p.h +++ b/src/charts/legend/legendmarkeritem_p.h @@ -56,7 +56,7 @@ class LegendMarkerItem : public QGraphicsObject, public QGraphicsLayoutItem Q_OBJECT Q_INTERFACES(QGraphicsLayoutItem) public: - explicit LegendMarkerItem(QLegendMarkerPrivate *marker, QGraphicsObject *parent = 0); + explicit LegendMarkerItem(QLegendMarkerPrivate *marker, QGraphicsObject *parent = nullptr); ~LegendMarkerItem(); void setPen(const QPen &pen); @@ -76,8 +76,9 @@ public: void setGeometry(const QRectF &rect); QRectF boundingRect() const; + QRectF markerRect() const; - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,QWidget *widget = nullptr); QSizeF sizeHint (Qt::SizeHint which, const QSizeF &constraint) const; void hoverEnterEvent(QGraphicsSceneHoverEvent *event); @@ -85,6 +86,10 @@ public: QString displayedLabel() const; void setToolTip(const QString &tooltip); + +Q_SIGNALS: + void markerRectChanged(); + protected: QLegendMarkerPrivate *m_marker; // Knows QRectF m_markerRect; diff --git a/src/charts/legend/qcandlesticklegendmarker.cpp b/src/charts/legend/qcandlesticklegendmarker.cpp new file mode 100644 index 00000000..97208e3c --- /dev/null +++ b/src/charts/legend/qcandlesticklegendmarker.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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/QCandlestickLegendMarker> +#include <private/legendmarkeritem_p.h> +#include <private/qcandlesticklegendmarker_p.h> +#include <private/qcandlestickseries_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +QCandlestickLegendMarker::QCandlestickLegendMarker(QCandlestickSeries *series, QLegend *legend, + QObject *parent) + : QLegendMarker(*new QCandlestickLegendMarkerPrivate(this, series, legend), parent) +{ + Q_D(QCandlestickLegendMarker); + + d->updated(); +} + +QCandlestickLegendMarker::~QCandlestickLegendMarker() +{ +} + +QLegendMarker::LegendMarkerType QCandlestickLegendMarker::type() +{ + return LegendMarkerTypeCandlestick; +} + +QCandlestickSeries* QCandlestickLegendMarker::series() +{ + Q_D(QCandlestickLegendMarker); + + return d->m_series; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +QCandlestickLegendMarkerPrivate::QCandlestickLegendMarkerPrivate(QCandlestickLegendMarker *q, + QCandlestickSeries *series, + QLegend *legend) + : QLegendMarkerPrivate(q, legend), + q_ptr(q), + m_series(series) +{ + QObject::connect(m_item, SIGNAL(markerRectChanged()), this, SLOT(updated())); + QObject::connect(m_series, SIGNAL(nameChanged()), this, SLOT(updated())); + QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(updated())); +} + +QCandlestickLegendMarkerPrivate::~QCandlestickLegendMarkerPrivate() +{ +} + +QAbstractSeries* QCandlestickLegendMarkerPrivate::series() +{ + return m_series; +} + +QObject* QCandlestickLegendMarkerPrivate::relatedObject() +{ + return m_series; +} + +void QCandlestickLegendMarkerPrivate::updated() +{ + bool labelChanged = false; + bool brushChanged = false; + + if (!m_customLabel && (m_item->label() != m_series->name())) { + m_item->setLabel(m_series->name()); + labelChanged = true; + } + if (!m_customBrush) { + QLinearGradient gradient; + gradient.setStart(0.0, 0.0); + gradient.setFinalStop(m_item->markerRect().width(), m_item->markerRect().height()); + gradient.setColorAt(0.0, m_series->increasingColor()); + gradient.setColorAt(0.49, m_series->increasingColor()); + gradient.setColorAt(0.50, m_series->decreasingColor()); + gradient.setColorAt(1.0, m_series->decreasingColor()); + + QBrush brush(gradient); + if (m_item->brush() != brush) { + m_item->setBrush(brush); + brushChanged = true; + } + } + invalidateLegend(); + + if (labelChanged) + emit q_ptr->labelChanged(); + if (brushChanged) + emit q_ptr->brushChanged(); +} + +#include "moc_qcandlesticklegendmarker.cpp" +#include "moc_qcandlesticklegendmarker_p.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/charts/legend/qcandlesticklegendmarker.h b/src/charts/legend/qcandlesticklegendmarker.h new file mode 100644 index 00000000..dad57c4f --- /dev/null +++ b/src/charts/legend/qcandlesticklegendmarker.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QCANDLESTICKLEGENDMARKER_H +#define QCANDLESTICKLEGENDMARKER_H + +#include <QtCharts/QCandlestickSeries> +#include <QtCharts/QLegendMarker> + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickLegendMarkerPrivate; + +class QT_CHARTS_EXPORT QCandlestickLegendMarker : public QLegendMarker +{ + Q_OBJECT + +public: + explicit QCandlestickLegendMarker(QCandlestickSeries *series, QLegend *legend, + QObject *parent = nullptr); + virtual ~QCandlestickLegendMarker(); + + virtual LegendMarkerType type(); + + // Related series + virtual QCandlestickSeries* series(); + +private: + Q_DECLARE_PRIVATE(QCandlestickLegendMarker) + Q_DISABLE_COPY(QCandlestickLegendMarker) +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKLEGENDMARKER_H diff --git a/src/charts/legend/qcandlesticklegendmarker_p.h b/src/charts/legend/qcandlesticklegendmarker_p.h new file mode 100644 index 00000000..1c786b3a --- /dev/null +++ b/src/charts/legend/qcandlesticklegendmarker_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Chart API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef QCANDLESTICKLEGENDMARKER_P_H +#define QCANDLESTICKLEGENDMARKER_P_H + +#include <private/qlegendmarker_p.h> + +QT_CHARTS_BEGIN_NAMESPACE + +class QCandlestickLegendMarker; +class QCandlestickSeries; + +class QCandlestickLegendMarkerPrivate : public QLegendMarkerPrivate +{ + Q_OBJECT +public: + explicit QCandlestickLegendMarkerPrivate(QCandlestickLegendMarker *q, + QCandlestickSeries *series, QLegend *legend); + virtual ~QCandlestickLegendMarkerPrivate(); + + virtual QAbstractSeries *series(); + virtual QObject *relatedObject(); + +public Q_SLOTS: + virtual void updated(); + +private: + QCandlestickLegendMarker *q_ptr; + QCandlestickSeries *m_series; + + Q_DECLARE_PUBLIC(QCandlestickLegendMarker) +}; + +QT_CHARTS_END_NAMESPACE + +#endif // QCANDLESTICKLEGENDMARKER_P_H diff --git a/src/charts/legend/qlegendmarker.cpp b/src/charts/legend/qlegendmarker.cpp index 8d240876..b9da4852 100644 --- a/src/charts/legend/qlegendmarker.cpp +++ b/src/charts/legend/qlegendmarker.cpp @@ -63,6 +63,7 @@ QT_CHARTS_BEGIN_NAMESPACE \value LegendMarkerTypePie \value LegendMarkerTypeXY \value LegendMarkerTypeBoxPlot + \value LegendMarkerTypeCandlestick */ /*! diff --git a/src/charts/legend/qlegendmarker.h b/src/charts/legend/qlegendmarker.h index 142b7e45..86721440 100644 --- a/src/charts/legend/qlegendmarker.h +++ b/src/charts/legend/qlegendmarker.h @@ -52,7 +52,8 @@ public: LegendMarkerTypeBar, LegendMarkerTypePie, LegendMarkerTypeXY, - LegendMarkerTypeBoxPlot + LegendMarkerTypeBoxPlot, + LegendMarkerTypeCandlestick }; Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) diff --git a/src/charts/qabstractseries.cpp b/src/charts/qabstractseries.cpp index 31a75044..b5c7d8a7 100644 --- a/src/charts/qabstractseries.cpp +++ b/src/charts/qabstractseries.cpp @@ -77,6 +77,7 @@ QT_CHARTS_BEGIN_NAMESPACE \value SeriesTypeHorizontalStackedBar \value SeriesTypeHorizontalPercentBar \value SeriesTypeBoxPlot + \value SeriesTypeCandlestick */ /*! diff --git a/src/charts/qabstractseries.h b/src/charts/qabstractseries.h index 8fedf5c3..2d987ee3 100644 --- a/src/charts/qabstractseries.h +++ b/src/charts/qabstractseries.h @@ -63,11 +63,12 @@ public: SeriesTypeHorizontalBar, SeriesTypeHorizontalStackedBar, SeriesTypeHorizontalPercentBar, - SeriesTypeBoxPlot + SeriesTypeBoxPlot, + SeriesTypeCandlestick }; protected: - QAbstractSeries(QAbstractSeriesPrivate &d, QObject *parent = Q_NULLPTR); + QAbstractSeries(QAbstractSeriesPrivate &d, QObject *parent = nullptr); public: ~QAbstractSeries(); diff --git a/src/charts/qchart.h b/src/charts/qchart.h index 40b53a8f..cf2efb4f 100644 --- a/src/charts/qchart.h +++ b/src/charts/qchart.h @@ -191,6 +191,7 @@ protected: friend class ChartThemeManager; friend class QAbstractSeries; friend class QBoxPlotSeriesPrivate; + friend class QCandlestickSeriesPrivate; private: Q_DISABLE_COPY(QChart) diff --git a/src/chartsqml2/chartsqml2.pro b/src/chartsqml2/chartsqml2.pro index fae7dc26..765f25b9 100644 --- a/src/chartsqml2/chartsqml2.pro +++ b/src/chartsqml2/chartsqml2.pro @@ -36,7 +36,8 @@ SOURCES += \ declarativepolarchart.cpp \ declarativeboxplotseries.cpp \ declarativechartnode.cpp \ - declarativerendernode.cpp + declarativerendernode.cpp \ + declarativecandlestickseries.cpp HEADERS += \ declarativechart.h \ @@ -54,7 +55,8 @@ HEADERS += \ declarativepolarchart.h \ declarativeboxplotseries.h \ declarativechartnode.h \ - declarativerendernode.h + declarativerendernode.h \ + declarativecandlestickseries.h OTHER_FILES = qmldir diff --git a/src/chartsqml2/chartsqml2_plugin.cpp b/src/chartsqml2/chartsqml2_plugin.cpp index 5492a45b..af1f15a4 100644 --- a/src/chartsqml2/chartsqml2_plugin.cpp +++ b/src/chartsqml2/chartsqml2_plugin.cpp @@ -42,6 +42,7 @@ #include "declarativescatterseries.h" #include "declarativebarseries.h" #include "declarativeboxplotseries.h" +#include "declarativecandlestickseries.h" #include "declarativepieseries.h" #include "declarativeaxes.h" #include <QtCharts/QVXYModelMapper> @@ -58,6 +59,9 @@ #include <QtCharts/QBoxPlotModelMapper> #include <QtCharts/QHBoxPlotModelMapper> #include <QtCharts/QVBoxPlotModelMapper> +#include <QtCharts/QCandlestickModelMapper> +#include <QtCharts/QHCandlestickModelMapper> +#include <QtCharts/QVCandlestickModelMapper> #ifndef QT_QREAL_IS_FLOAT #include <QtCharts/QDateTimeAxis> #endif @@ -78,6 +82,8 @@ QML_DECLARE_TYPE(DeclarativeBarSeries) QML_DECLARE_TYPE(DeclarativeBarSet) QML_DECLARE_TYPE(DeclarativeBoxPlotSeries) QML_DECLARE_TYPE(DeclarativeBoxSet) +QML_DECLARE_TYPE(DeclarativeCandlestickSeries) +QML_DECLARE_TYPE(DeclarativeCandlestickSet) QML_DECLARE_TYPE(DeclarativeLineSeries) QML_DECLARE_TYPE(DeclarativePieSeries) QML_DECLARE_TYPE(DeclarativePieSlice) @@ -112,6 +118,9 @@ QML_DECLARE_TYPE(QXYModelMapper) QML_DECLARE_TYPE(QBoxPlotModelMapper) QML_DECLARE_TYPE(QHBoxPlotModelMapper) QML_DECLARE_TYPE(QVBoxPlotModelMapper) +QML_DECLARE_TYPE(QCandlestickModelMapper) +QML_DECLARE_TYPE(QHCandlestickModelMapper) +QML_DECLARE_TYPE(QVCandlestickModelMapper) QML_DECLARE_TYPE(QAbstractSeries) QML_DECLARE_TYPE(QXYSeries) @@ -325,6 +334,14 @@ public: qmlRegisterType<DeclarativeScatterSeries, 5>(uri, 2, 1, "ScatterSeries"); qmlRegisterType<DeclarativeLineSeries, 4>(uri, 2, 1, "LineSeries"); qmlRegisterType<DeclarativeSplineSeries, 4>(uri, 2, 1, "SplineSeries"); + + // QtCharts 2.2 + qmlRegisterType<DeclarativeCandlestickSeries>(uri, 2, 2, "CandlestickSeries"); + qmlRegisterType<DeclarativeCandlestickSet>(uri, 2, 2, "CandlestickSet"); + qmlRegisterUncreatableType<QCandlestickModelMapper>(uri, 2, 2, "CandlestickModelMapper", + QLatin1String("Trying to create uncreatable: CandlestickModelMapper.")); + qmlRegisterType<QHCandlestickModelMapper>(uri, 2, 2, "HCandlestickModelMapper"); + qmlRegisterType<QVCandlestickModelMapper>(uri, 2, 2, "VCandlestickModelMapper"); } }; diff --git a/src/chartsqml2/declarativecandlestickseries.cpp b/src/chartsqml2/declarativecandlestickseries.cpp new file mode 100644 index 00000000..6981e85d --- /dev/null +++ b/src/chartsqml2/declarativecandlestickseries.cpp @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** 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/QHCandlestickModelMapper> +#include <QtCharts/QVCandlestickModelMapper> +#include "declarativeaxes.h" +#include "declarativecandlestickseries.h" + +QT_CHARTS_BEGIN_NAMESPACE + +DeclarativeCandlestickSet::DeclarativeCandlestickSet(qreal timestamp, QObject *parent) + : QCandlestickSet(timestamp, parent) +{ + connect(this, SIGNAL(brushChanged()), this, SLOT(handleBrushChanged())); +} + +void DeclarativeCandlestickSet::setBrushFilename(const QString &brushFilename) +{ + QImage brushImage(brushFilename); + if (QCandlestickSet::brush().textureImage() != brushImage) { + QBrush brush = QCandlestickSet::brush(); + brush.setTextureImage(brushImage); + + QCandlestickSet::setBrush(brush); + + m_brushFilename = brushFilename; + m_brushImage = brushImage; + + emit brushFilenameChanged(brushFilename); + } +} + +QString DeclarativeCandlestickSet::brushFilename() const +{ + return m_brushFilename; +} + +void DeclarativeCandlestickSet::handleBrushChanged() +{ + // If the texture image of the brush has changed along the brush + // the brush file name needs to be cleared. + if (!m_brushFilename.isEmpty() && QCandlestickSet::brush().textureImage() != m_brushImage) { + m_brushFilename.clear(); + emit brushFilenameChanged(QString()); + } +} + +// Declarative candlestick series ================================================================== + +DeclarativeCandlestickSeries::DeclarativeCandlestickSeries(QQuickItem *parent) + : QCandlestickSeries(parent), + m_axes(new DeclarativeAxes(this)) +{ + connect(m_axes, SIGNAL(axisXChanged(QAbstractAxis*)), + this, SIGNAL(axisXChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisYChanged(QAbstractAxis*)), + this, SIGNAL(axisYChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisXTopChanged(QAbstractAxis*)), + this, SIGNAL(axisXTopChanged(QAbstractAxis*))); + connect(m_axes, SIGNAL(axisYRightChanged(QAbstractAxis*)), + this, SIGNAL(axisYRightChanged(QAbstractAxis*))); + + connect(this, SIGNAL(hovered(bool, QCandlestickSet *)), + this, SLOT(onHovered(bool, QCandlestickSet *))); + connect(this, SIGNAL(clicked(QCandlestickSet *)), this, SLOT(onClicked(QCandlestickSet *))); + connect(this, SIGNAL(pressed(QCandlestickSet *)), this, SLOT(onPressed(QCandlestickSet *))); + connect(this, SIGNAL(released(QCandlestickSet *)), this, SLOT(onReleased(QCandlestickSet *))); + connect(this, SIGNAL(doubleClicked(QCandlestickSet *)), + this, SLOT(onDoubleClicked(QCandlestickSet *))); + + connect(this, SIGNAL(brushChanged()), this, SLOT(handleBrushChanged())); +} + +QQmlListProperty<QObject> DeclarativeCandlestickSeries::seriesChildren() +{ + return QQmlListProperty<QObject>(this, 0, &DeclarativeCandlestickSeries::appendSeriesChildren, + 0, 0, 0); +} + +void DeclarativeCandlestickSeries::setBrushFilename(const QString &brushFilename) +{ + QImage brushImage(brushFilename); + if (QCandlestickSeries::brush().textureImage() != brushImage) { + QBrush brush = QCandlestickSeries::brush(); + brush.setTextureImage(brushImage); + + QCandlestickSeries::setBrush(brush); + + m_brushFilename = brushFilename; + m_brushImage = brushImage; + + emit brushFilenameChanged(brushFilename); + } +} + +QString DeclarativeCandlestickSeries::brushFilename() const +{ + return m_brushFilename; +} + +DeclarativeCandlestickSet *DeclarativeCandlestickSeries::at(int index) +{ + QList<QCandlestickSet *> sets = candlestickSets(); + if (index >= 0 && index < sets.count()) + return qobject_cast<DeclarativeCandlestickSet *>(sets[index]); + + return 0; +} + +bool DeclarativeCandlestickSeries::append(DeclarativeCandlestickSet *set) +{ + return QCandlestickSeries::append(qobject_cast<QCandlestickSet *>(set)); +} + +bool DeclarativeCandlestickSeries::remove(DeclarativeCandlestickSet *set) +{ + return QCandlestickSeries::remove(qobject_cast<QCandlestickSet *>(set)); +} + +bool DeclarativeCandlestickSeries::append(qreal open, qreal high, qreal low, qreal close, + qreal timestamp) +{ + QCandlestickSet *set = new QCandlestickSet(open, high, low, close, timestamp); + if (!QCandlestickSeries::append(set)) { + delete set; + return false; + } + + return true; +} + +bool DeclarativeCandlestickSeries::remove(qreal timestamp) +{ + for (int i = 0; i < count(); ++i) { + QCandlestickSet *set = candlestickSets().at(i); + if (set->timestamp() == timestamp) + return QCandlestickSeries::remove(set); + } + + return false; +} + +bool DeclarativeCandlestickSeries::insert(int index, DeclarativeCandlestickSet *set) +{ + return QCandlestickSeries::insert(index, qobject_cast<QCandlestickSet *>(set)); +} + +void DeclarativeCandlestickSeries::clear() +{ + QCandlestickSeries::clear(); +} + +void DeclarativeCandlestickSeries::classBegin() +{ + // do nothing +} + +void DeclarativeCandlestickSeries::componentComplete() +{ + foreach (QObject *child, children()) { + if (qobject_cast<DeclarativeCandlestickSet *>(child)) { + QCandlestickSeries::append(qobject_cast<DeclarativeCandlestickSet *>(child)); + } else if (qobject_cast<QHCandlestickModelMapper *>(child)) { + QHCandlestickModelMapper *mapper = qobject_cast<QHCandlestickModelMapper *>(child); + mapper->setSeries(this); + } else if (qobject_cast<QVCandlestickModelMapper *>(child)) { + QVCandlestickModelMapper *mapper = qobject_cast<QVCandlestickModelMapper *>(child); + mapper->setSeries(this); + } // else: do nothing + } +} + +void DeclarativeCandlestickSeries::appendSeriesChildren(QQmlListProperty<QObject> *list, + QObject *element) +{ + // Empty implementation; the children are parsed in componentComplete instead + Q_UNUSED(list); + Q_UNUSED(element); +} + +void DeclarativeCandlestickSeries::onClicked(QCandlestickSet *set) +{ + emit clicked(qobject_cast<DeclarativeCandlestickSet *>(set)); +} + +void DeclarativeCandlestickSeries::onHovered(bool status, QCandlestickSet *set) +{ + emit hovered(status, qobject_cast<DeclarativeCandlestickSet *>(set)); +} + +void DeclarativeCandlestickSeries::onPressed(QCandlestickSet *set) +{ + emit pressed(qobject_cast<DeclarativeCandlestickSet *>(set)); +} + +void DeclarativeCandlestickSeries::onReleased(QCandlestickSet *set) +{ + emit released(qobject_cast<DeclarativeCandlestickSet *>(set)); +} + +void DeclarativeCandlestickSeries::onDoubleClicked(QCandlestickSet *set) +{ + emit doubleClicked(qobject_cast<DeclarativeCandlestickSet *>(set)); +} + +void DeclarativeCandlestickSeries::handleBrushChanged() +{ + // If the texture image of the brush has changed along the brush + // the brush file name needs to be cleared. + if (!m_brushFilename.isEmpty() && QCandlestickSeries::brush().textureImage() != m_brushImage) { + m_brushFilename.clear(); + emit brushFilenameChanged(QString()); + } +} + +#include "moc_declarativecandlestickseries.cpp" + +QT_CHARTS_END_NAMESPACE diff --git a/src/chartsqml2/declarativecandlestickseries.h b/src/chartsqml2/declarativecandlestickseries.h new file mode 100644 index 00000000..9b7c0e37 --- /dev/null +++ b/src/chartsqml2/declarativecandlestickseries.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef DECLARATIVECANDLESTICKSERIES_H +#define DECLARATIVECANDLESTICKSERIES_H + +#include <QtCharts/QCandlestickSeries> +#include <QtCharts/QCandlestickSet> +#include <QtQml/QQmlParserStatus> +#include <QtQuick/QQuickItem> + +QT_CHARTS_BEGIN_NAMESPACE + +class DeclarativeAxes; +class QAbstractAxis; + +class DeclarativeCandlestickSet : public QCandlestickSet +{ + Q_OBJECT + Q_PROPERTY(QString brushFilename READ brushFilename WRITE setBrushFilename NOTIFY brushFilenameChanged) + +public: + explicit DeclarativeCandlestickSet(qreal timestamp = 0.0, QObject *parent = nullptr); + void setBrushFilename(const QString &brushFilename); + QString brushFilename() const; + +Q_SIGNALS: + void brushFilenameChanged(const QString &brushFilename); + +private Q_SLOTS: + void handleBrushChanged(); + +private: + QString m_brushFilename; + QImage m_brushImage; +}; + +class DeclarativeCandlestickSeries : public QCandlestickSeries, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QAbstractAxis *axisX READ axisX WRITE setAxisX NOTIFY axisXChanged) + Q_PROPERTY(QAbstractAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged) + Q_PROPERTY(QAbstractAxis *axisXTop READ axisXTop WRITE setAxisXTop NOTIFY axisXTopChanged) + Q_PROPERTY(QAbstractAxis *axisYRight READ axisYRight WRITE setAxisYRight NOTIFY axisYRightChanged) + Q_PROPERTY(QQmlListProperty<QObject> seriesChildren READ seriesChildren) + Q_PROPERTY(QString brushFilename READ brushFilename WRITE setBrushFilename NOTIFY brushFilenameChanged) + Q_CLASSINFO("DefaultProperty", "seriesChildren") + +public: + explicit DeclarativeCandlestickSeries(QQuickItem *parent = nullptr); + void setAxisX(QAbstractAxis *axis) { m_axes->setAxisX(axis); } + QAbstractAxis *axisX() { return m_axes->axisX(); } + void setAxisY(QAbstractAxis *axis) { m_axes->setAxisY(axis); } + QAbstractAxis *axisY() { return m_axes->axisY(); } + void setAxisXTop(QAbstractAxis *axis) { m_axes->setAxisXTop(axis); } + QAbstractAxis *axisXTop() { return m_axes->axisXTop(); } + void setAxisYRight(QAbstractAxis *axis) { m_axes->setAxisYRight(axis); } + QAbstractAxis *axisYRight() { return m_axes->axisYRight(); } + QQmlListProperty<QObject> seriesChildren(); + void setBrushFilename(const QString &brushFilename); + QString brushFilename() const; + +public: + Q_INVOKABLE DeclarativeCandlestickSet *at(int index); + Q_INVOKABLE bool append(DeclarativeCandlestickSet *set); + Q_INVOKABLE bool remove(DeclarativeCandlestickSet *set); + Q_INVOKABLE bool append(qreal open, qreal high, qreal low, qreal close, qreal timestamp); + Q_INVOKABLE bool remove(qreal timestamp); + Q_INVOKABLE bool insert(int index, DeclarativeCandlestickSet *set); + Q_INVOKABLE void clear(); + +public: // from QDeclarativeParserStatus + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void axisXChanged(QAbstractAxis *axis); + void axisYChanged(QAbstractAxis *axis); + void axisXTopChanged(QAbstractAxis *axis); + void axisYRightChanged(QAbstractAxis *axis); + void clicked(DeclarativeCandlestickSet *set); + void hovered(bool status, DeclarativeCandlestickSet *set); + void pressed(DeclarativeCandlestickSet *set); + void released(DeclarativeCandlestickSet *set); + void doubleClicked(DeclarativeCandlestickSet *set); + void brushFilenameChanged(const QString &brushFilename); + +public Q_SLOTS: + static void appendSeriesChildren(QQmlListProperty<QObject> *list, QObject *element); + void onClicked(QCandlestickSet *set); + void onHovered(bool status, QCandlestickSet *set); + void onPressed(QCandlestickSet *set); + void onReleased(QCandlestickSet *set); + void onDoubleClicked(QCandlestickSet *set); + +private Q_SLOTS: + void handleBrushChanged(); + +public: + DeclarativeAxes *m_axes; + +private: + QString m_brushFilename; + QImage m_brushImage; +}; + +QT_CHARTS_END_NAMESPACE + +#endif // DECLARATIVECANDLESTICKSERIES_H diff --git a/src/chartsqml2/declarativechart.cpp b/src/chartsqml2/declarativechart.cpp index ec5e83a0..66b47722 100644 --- a/src/chartsqml2/declarativechart.cpp +++ b/src/chartsqml2/declarativechart.cpp @@ -35,6 +35,7 @@ #include "declarativepieseries.h" #include "declarativesplineseries.h" #include "declarativeboxplotseries.h" +#include "declarativecandlestickseries.h" #include "declarativescatterseries.h" #include "declarativechartnode.h" #include "declarativerendernode.h" @@ -1077,6 +1078,9 @@ QAbstractSeries *DeclarativeChart::createSeries(int type, QString name, QAbstrac case DeclarativeChart::SeriesTypeBoxPlot: series = new DeclarativeBoxPlotSeries(); break; + case DeclarativeChart::SeriesTypeCandlestick: + series = new DeclarativeCandlestickSeries(); + break; case DeclarativeChart::SeriesTypePie: series = new DeclarativePieSeries(); break; @@ -1189,6 +1193,8 @@ void DeclarativeChart::initializeAxes(QAbstractSeries *series) doInitializeAxes(series, qobject_cast<DeclarativeHorizontalPercentBarSeries *>(series)->m_axes); else if (qobject_cast<DeclarativeBoxPlotSeries *>(series)) doInitializeAxes(series, qobject_cast<DeclarativeBoxPlotSeries *>(series)->m_axes); + else if (qobject_cast<DeclarativeCandlestickSeries *>(series)) + doInitializeAxes(series, qobject_cast<DeclarativeCandlestickSeries *>(series)->m_axes); // else: do nothing } diff --git a/src/chartsqml2/declarativechart.h b/src/chartsqml2/declarativechart.h index 2a37d8ee..dd58b54b 100644 --- a/src/chartsqml2/declarativechart.h +++ b/src/chartsqml2/declarativechart.h @@ -97,6 +97,7 @@ public: SeriesTypeStackedBar, SeriesTypePercentBar, SeriesTypeBoxPlot, + SeriesTypeCandlestick, SeriesTypePie, SeriesTypeScatter, SeriesTypeSpline, diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index e8cad75c..1b04ac7c 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -23,7 +23,10 @@ SUBDIRS += \ domain \ chartdataset \ qlegend \ - cmake + cmake \ + qcandlestickmodelmapper \ + qcandlestickseries \ + qcandlestickset !contains(QT_COORD_TYPE, float): { SUBDIRS += \ diff --git a/tests/auto/qcandlestickmodelmapper/qcandlestickmodelmapper.pro b/tests/auto/qcandlestickmodelmapper/qcandlestickmodelmapper.pro new file mode 100644 index 00000000..81f0b368 --- /dev/null +++ b/tests/auto/qcandlestickmodelmapper/qcandlestickmodelmapper.pro @@ -0,0 +1,5 @@ +!include( ../auto.pri ) { + error( "Couldn't find the auto.pri file!" ) +} + +SOURCES += tst_qcandlestickmodelmapper.cpp diff --git a/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp b/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp new file mode 100644 index 00000000..b6110c7f --- /dev/null +++ b/tests/auto/qcandlestickmodelmapper/tst_qcandlestickmodelmapper.cpp @@ -0,0 +1,632 @@ +/**************************************************************************** +** +** 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 <QtCharts/QChartView> +#include <QtCharts/QHCandlestickModelMapper> +#include <QtCharts/QVCandlestickModelMapper> +#include <QtCore/QString> +#include <QtGui/QStandardItemModel> +#include <QtTest/QtTest> + +QT_CHARTS_USE_NAMESPACE + +class tst_qcandlestickmodelmapper : public QObject +{ + Q_OBJECT + +public: + tst_qcandlestickmodelmapper(); + + void createVerticalMapper(); + void createHorizontalMapper(); + +public Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private Q_SLOTS: + void verticalMapper_data(); + void verticalMapper(); + void verticalMapperCustomMapping_data(); + void verticalMapperCustomMapping(); + void horizontalMapper_data(); + void horizontalMapper(); + void horizontalMapperCustomMapping_data(); + void horizontalMapperCustomMapping(); + void seriesUpdated(); + void verticalModelInsertRows(); + void verticalModelRemoveRows(); + void verticalModelInsertColumns(); + void verticalModelRemoveColumns(); + void horizontalModelInsertRows(); + void horizontalModelRemoveRows(); + void horizontalModelInsertColumns(); + void horizontalModelRemoveColumns(); + void modelUpdateCell(); + void verticalMapperSignals(); + void horizontalMapperSignals(); + +private: + QStandardItemModel *m_model; + int m_modelRowCount; + int m_modelColumnCount; + + QCandlestickSeries *m_series; + QChart *m_chart; + QChartView *m_chartView; + + QHCandlestickModelMapper *m_hMapper; + QVCandlestickModelMapper *m_vMapper; +}; + +tst_qcandlestickmodelmapper::tst_qcandlestickmodelmapper() + : m_model(nullptr), + m_modelRowCount(10), + m_modelColumnCount(8), + m_series(nullptr), + m_chart(nullptr), + m_chartView(nullptr), + m_hMapper(nullptr), + m_vMapper(nullptr) +{ +} + +void tst_qcandlestickmodelmapper::createHorizontalMapper() +{ + m_hMapper = new QHCandlestickModelMapper; + QVERIFY(m_hMapper->model() == nullptr); + m_hMapper->setTimestampColumn(0); + m_hMapper->setOpenColumn(1); + m_hMapper->setHighColumn(3); + m_hMapper->setLowColumn(5); + m_hMapper->setCloseColumn(6); + m_hMapper->setFirstCandlestickSetRow(0); + m_hMapper->setLastCandlestickSetRow(4); + m_hMapper->setModel(m_model); + m_hMapper->setSeries(m_series); +} + +void tst_qcandlestickmodelmapper::createVerticalMapper() +{ + m_vMapper = new QVCandlestickModelMapper; + QVERIFY(m_vMapper->model() == nullptr); + m_vMapper->setTimestampRow(0); + m_vMapper->setOpenRow(1); + m_vMapper->setHighRow(3); + m_vMapper->setLowRow(5); + m_vMapper->setCloseRow(6); + m_vMapper->setFirstCandlestickSetColumn(0); + m_vMapper->setLastCandlestickSetColumn(4); + m_vMapper->setModel(m_model); + m_vMapper->setSeries(m_series); +} + +void tst_qcandlestickmodelmapper::initTestCase() +{ + m_chart = new QChart(); + m_chartView = new QChartView(m_chart); + m_chartView->show(); +} + +void tst_qcandlestickmodelmapper::cleanupTestCase() +{ + delete m_chartView; + QTest::qWait(1); // Allow final deleteLaters to run +} + +void tst_qcandlestickmodelmapper::init() +{ + m_series = new QCandlestickSeries(); + m_chart->addSeries(m_series); + + m_model = new QStandardItemModel(m_modelRowCount, m_modelColumnCount, this); + for (int row = 0; row < m_modelRowCount; ++row) { + for (int column = 0; column < m_modelColumnCount; ++column) + m_model->setData(m_model->index(row, column), row * column); + } +} + +void tst_qcandlestickmodelmapper::cleanup() +{ + m_chart->removeSeries(m_series); + delete m_series; + m_series = nullptr; + + m_model->clear(); + m_model->deleteLater(); + m_model = nullptr; + + if (m_vMapper) { + m_vMapper->deleteLater(); + m_vMapper = nullptr; + } + + if (m_hMapper) { + m_hMapper->deleteLater(); + m_hMapper = nullptr; + } +} + +void tst_qcandlestickmodelmapper::verticalMapper_data() +{ + QTest::addColumn<int>("firstCandlestickSetColumn"); + QTest::addColumn<int>("lastCandlestickSetColumn"); + QTest::addColumn<int>("expectedCandlestickSetCount"); + + QTest::newRow("last column greater than first column") << 0 << 1 << 2; + QTest::newRow("last column equal to first column") << 1 << 1 << 1; + QTest::newRow("last column lesser than first column") << 1 << 0 << 0; + QTest::newRow("invalid first column and correct last column") << -3 << 1 << 0; + QTest::newRow("first column beyond the size of model and correct last column") << m_modelColumnCount << 1 << 0; + QTest::newRow("first column beyond the size of model and invalid last column") << m_modelColumnCount << -1 << 0; +} + +void tst_qcandlestickmodelmapper::verticalMapper() +{ + QFETCH(int, firstCandlestickSetColumn); + QFETCH(int, lastCandlestickSetColumn); + QFETCH(int, expectedCandlestickSetCount); + + QCandlestickSeries *series = new QCandlestickSeries(); + m_chart->addSeries(series); + + createVerticalMapper(); + m_vMapper->setFirstCandlestickSetColumn(firstCandlestickSetColumn); + m_vMapper->setLastCandlestickSetColumn(lastCandlestickSetColumn); + m_vMapper->setSeries(series); + + QCOMPARE(m_vMapper->firstCandlestickSetColumn(), qMax(firstCandlestickSetColumn, -1)); + QCOMPARE(m_vMapper->lastCandlestickSetColumn(), qMax(lastCandlestickSetColumn, -1)); + QCOMPARE(series->count(), expectedCandlestickSetCount); + + m_chart->removeSeries(series); + delete series; +} + +void tst_qcandlestickmodelmapper::verticalMapperCustomMapping_data() +{ + QTest::addColumn<int>("timestampRow"); + QTest::addColumn<int>("openRow"); + QTest::addColumn<int>("highRow"); + QTest::addColumn<int>("lowRow"); + QTest::addColumn<int>("closeRow"); + + QTest::newRow("all rows are correct") << 0 << 1 << 2 << 3 << 4; + QTest::newRow("all rows are invalid") << -3 << -3 << -3 << -3 << -3; + QTest::newRow("timestamp: -1 (invalid)") << -1 << 1 << 2 << 3 << 4; + QTest::newRow("timestamp: -3 (invalid - should default to -1)") << -3 << 1 << 2 << 3 << 4; + QTest::newRow("timestamp: +1 greater than the number of rows in the model") << m_modelRowCount + 1 << 1 << 2 << 3 << 4; + QTest::newRow("open: -1 (invalid)") << 0 << -1 << 2 << 3 << 4; + QTest::newRow("open: -3 (invalid - should default to -1)") << 0 << -3 << 2 << 3 << 4; + QTest::newRow("open: +1 greater than the number of rows in the model") << 0 << m_modelRowCount + 1 << 2 << 3 << 4; + QTest::newRow("high: -1 (invalid)") << 0 << 1 << -1 << 3 << 4; + QTest::newRow("high: -3 (invalid - should default to -1)") << 0 << 1 << -3 << 3 << 4; + QTest::newRow("high: +1 greater than the number of rows in the model") << 0 << 1 << m_modelRowCount + 1 << 3 << 4; + QTest::newRow("low: -1 (invalid)") << 0 << 1 << 2 << -1 << 4; + QTest::newRow("low: -3 (invalid - should default to -1)") << 0 << 1 << 2 << -3 << 4; + QTest::newRow("low: +1 greater than the number of rows in the model") << 0 << 1 << 2 << m_modelRowCount + 1 << 4; + QTest::newRow("close: -1 (invalid)") << 0 << 1 << 2 << 3 << -1; + QTest::newRow("close: -3 (invalid - should default to -1)") << 0 << 1 << 2 << 3 << -3; + QTest::newRow("close: +1 greater than the number of rows in the model") << 0 << 1 << 2 << 3 << m_modelRowCount + 1; +} + +void tst_qcandlestickmodelmapper::verticalMapperCustomMapping() +{ + QFETCH(int, timestampRow); + QFETCH(int, openRow); + QFETCH(int, highRow); + QFETCH(int, lowRow); + QFETCH(int, closeRow); + + QCandlestickSeries *series = new QCandlestickSeries(); + m_chart->addSeries(series); + QCOMPARE(series->count(), 0); + + createVerticalMapper(); + m_vMapper->setTimestampRow(timestampRow); + m_vMapper->setOpenRow(openRow); + m_vMapper->setHighRow(highRow); + m_vMapper->setLowRow(lowRow); + m_vMapper->setCloseRow(closeRow); + m_vMapper->setSeries(series); + + QCOMPARE(m_vMapper->timestampRow(), qMax(timestampRow, -1)); + QCOMPARE(m_vMapper->openRow(), qMax(openRow, -1)); + QCOMPARE(m_vMapper->highRow(), qMax(highRow, -1)); + QCOMPARE(m_vMapper->lowRow(), qMax(lowRow, -1)); + QCOMPARE(m_vMapper->closeRow(), qMax(closeRow, -1)); + + int count; + if ((m_vMapper->timestampRow() >= 0 && m_vMapper->timestampRow() < m_modelRowCount) + && (m_vMapper->openRow() >= 0 && m_vMapper->openRow() < m_modelRowCount) + && (m_vMapper->highRow() >= 0 && m_vMapper->highRow() < m_modelRowCount) + && (m_vMapper->lowRow() >= 0 && m_vMapper->lowRow() < m_modelRowCount) + && (m_vMapper->closeRow() >= 0 && m_vMapper->closeRow() < m_modelRowCount)) + count = m_vMapper->lastCandlestickSetColumn() - m_vMapper->firstCandlestickSetColumn() + 1; + else + count = 0; + QCOMPARE(series->count(), count); + + // change values column mapping to invalid + m_vMapper->setFirstCandlestickSetColumn(-1); + m_vMapper->setLastCandlestickSetColumn(1); + QCOMPARE(series->count(), 0); + + m_chart->removeSeries(series); + delete series; +} + +void tst_qcandlestickmodelmapper::horizontalMapper_data() +{ + QTest::addColumn<int>("firstCandlestickSetRow"); + QTest::addColumn<int>("lastCandlestickSetRow"); + QTest::addColumn<int>("expectedCandlestickSetCount"); + + QTest::newRow("last row greater than first row") << 0 << 1 << 2; + QTest::newRow("last row equal to first row") << 1 << 1 << 1; + QTest::newRow("last row lesser than first row") << 1 << 0 << 0; + QTest::newRow("invalid first row and correct last row") << -3 << 1 << 0; + QTest::newRow("first row beyond the size of model and correct last row") << m_modelRowCount << 1 << 0; + QTest::newRow("first row beyond the size of model and invalid last row") << m_modelRowCount << -1 << 0; +} + +void tst_qcandlestickmodelmapper::horizontalMapper() +{ + QFETCH(int, firstCandlestickSetRow); + QFETCH(int, lastCandlestickSetRow); + QFETCH(int, expectedCandlestickSetCount); + + QCandlestickSeries *series = new QCandlestickSeries(); + m_chart->addSeries(series); + + createHorizontalMapper(); + m_hMapper->setFirstCandlestickSetRow(firstCandlestickSetRow); + m_hMapper->setLastCandlestickSetRow(lastCandlestickSetRow); + m_hMapper->setSeries(series); + + QCOMPARE(m_hMapper->firstCandlestickSetRow(), qMax(firstCandlestickSetRow, -1)); + QCOMPARE(m_hMapper->lastCandlestickSetRow(), qMax(lastCandlestickSetRow, -1)); + QCOMPARE(series->count(), expectedCandlestickSetCount); + + m_chart->removeSeries(series); + delete series; +} + +void tst_qcandlestickmodelmapper::horizontalMapperCustomMapping_data() +{ + QTest::addColumn<int>("timestampColumn"); + QTest::addColumn<int>("openColumn"); + QTest::addColumn<int>("highColumn"); + QTest::addColumn<int>("lowColumn"); + QTest::addColumn<int>("closeColumn"); + + QTest::newRow("all columns are correct") << 0 << 1 << 2 << 3 << 4; + QTest::newRow("all columns are invalid") << -3 << -3 << -3 << -3 << -3; + QTest::newRow("timestamp: -1 (invalid)") << -1 << 1 << 2 << 3 << 4; + QTest::newRow("timestamp: -3 (invalid - should default to -1)") << -3 << 1 << 2 << 3 << 4; + QTest::newRow("timestamp: +1 greater than the number of columns in the model") << m_modelColumnCount + 1 << 1 << 2 << 3 << 4; + QTest::newRow("open: -1 (invalid)") << 0 << -1 << 2 << 3 << 4; + QTest::newRow("open: -3 (invalid - should default to -1)") << 0 << -3 << 2 << 3 << 4; + QTest::newRow("open: +1 greater than the number of columns in the model") << 0 << m_modelColumnCount + 1 << 2 << 3 << 4; + QTest::newRow("high: -1 (invalid)") << 0 << 1 << -1 << 3 << 4; + QTest::newRow("high: -3 (invalid - should default to -1)") << 0 << 1 << -3 << 3 << 4; + QTest::newRow("high: +1 greater than the number of columns in the model") << 0 << 1 << m_modelColumnCount + 1 << 3 << 4; + QTest::newRow("low: -1 (invalid)") << 0 << 1 << 2 << -1 << 4; + QTest::newRow("low: -3 (invalid - should default to -1)") << 0 << 1 << 2 << -3 << 4; + QTest::newRow("low: +1 greater than the number of columns in the model") << 0 << 1 << 2 << m_modelColumnCount + 1 << 4; + QTest::newRow("close: -1 (invalid)") << 0 << 1 << 2 << 3 << -1; + QTest::newRow("close: -3 (invalid - should default to -1)") << 0 << 1 << 2 << 3 << -3; + QTest::newRow("close: +1 greater than the number of columns in the model") << 0 << 1 << 2 << 3 << m_modelColumnCount + 1; +} + +void tst_qcandlestickmodelmapper::horizontalMapperCustomMapping() +{ + QFETCH(int, timestampColumn); + QFETCH(int, openColumn); + QFETCH(int, highColumn); + QFETCH(int, lowColumn); + QFETCH(int, closeColumn); + + QCandlestickSeries *series = new QCandlestickSeries(); + m_chart->addSeries(series); + QCOMPARE(series->count(), 0); + + createHorizontalMapper(); + m_hMapper->setTimestampColumn(timestampColumn); + m_hMapper->setOpenColumn(openColumn); + m_hMapper->setHighColumn(highColumn); + m_hMapper->setLowColumn(lowColumn); + m_hMapper->setCloseColumn(closeColumn); + m_hMapper->setSeries(series); + + QCOMPARE(m_hMapper->timestampColumn(), qMax(timestampColumn, -1)); + QCOMPARE(m_hMapper->openColumn(), qMax(openColumn, -1)); + QCOMPARE(m_hMapper->highColumn(), qMax(highColumn, -1)); + QCOMPARE(m_hMapper->lowColumn(), qMax(lowColumn, -1)); + QCOMPARE(m_hMapper->closeColumn(), qMax(closeColumn, -1)); + + int count; + if ((m_hMapper->timestampColumn() >= 0 && m_hMapper->timestampColumn() < m_modelColumnCount) + && (m_hMapper->openColumn() >= 0 && m_hMapper->openColumn() < m_modelColumnCount) + && (m_hMapper->highColumn() >= 0 && m_hMapper->highColumn() < m_modelColumnCount) + && (m_hMapper->lowColumn() >= 0 && m_hMapper->lowColumn() < m_modelColumnCount) + && (m_hMapper->closeColumn() >= 0 && m_hMapper->closeColumn() < m_modelColumnCount)) + count = m_hMapper->lastCandlestickSetRow() - m_hMapper->firstCandlestickSetRow() + 1; + else + count = 0; + QCOMPARE(series->count(), count); + + // change values row mapping to invalid + m_hMapper->setFirstCandlestickSetRow(-1); + m_hMapper->setLastCandlestickSetRow(1); + QCOMPARE(series->count(), 0); + + m_chart->removeSeries(series); + delete series; +} + +void tst_qcandlestickmodelmapper::seriesUpdated() +{ + createVerticalMapper(); + QVERIFY(m_vMapper->model() != nullptr); + + QCandlestickSet *set = m_series->candlestickSets().value(0, 0); + QVERIFY(set != nullptr); + + // update values + QCOMPARE(m_model->data(m_model->index(m_vMapper->timestampRow(), 0)).toReal(),set->timestamp()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->openRow(), 0)).toReal(), set->open()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->highRow(), 0)).toReal(), set->high()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->lowRow(), 0)).toReal(), set->low()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->closeRow(), 0)).toReal(), set->close()); + set->setTimestamp(set->timestamp() + 5.0); + set->setOpen(set->open() + 6.0); + set->setHigh(set->high() + 7.0); + set->setLow(set->low() + 8.0); + set->setClose(set->close() + 9.0); + QCOMPARE(m_model->data(m_model->index(m_vMapper->timestampRow(), 0)).toReal(),set->timestamp()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->openRow(), 0)).toReal(), set->open()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->highRow(), 0)).toReal(), set->high()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->lowRow(), 0)).toReal(), set->low()); + QCOMPARE(m_model->data(m_model->index(m_vMapper->closeRow(), 0)).toReal(), set->close()); + + // append new sets + QList<QCandlestickSet *> newCandlestickSets; + newCandlestickSets << new QCandlestickSet(3.0, 5.0, 2.0, 4.0, 1234); + newCandlestickSets << new QCandlestickSet(5.0, 7.0, 4.0, 6.0, 5678); + m_series->append(newCandlestickSets); + QCOMPARE(m_model->columnCount(), m_modelColumnCount + newCandlestickSets.count()); + + // remove sets + newCandlestickSets.clear(); + newCandlestickSets << m_series->candlestickSets().at(m_series->count() - 1); + newCandlestickSets << m_series->candlestickSets().at(m_series->count() - 2); + m_series->remove(newCandlestickSets); + QCOMPARE(m_model->columnCount(), m_modelColumnCount); +} + +void tst_qcandlestickmodelmapper::verticalModelInsertRows() +{ + createVerticalMapper(); + int count = m_vMapper->lastCandlestickSetColumn() - m_vMapper->firstCandlestickSetColumn() + 1; + QVERIFY(m_vMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + m_model->insertRows(3, 4); + QCOMPARE(m_series->count(), count); +} + +void tst_qcandlestickmodelmapper::verticalModelRemoveRows() +{ + createVerticalMapper(); + int count = m_vMapper->lastCandlestickSetColumn() - m_vMapper->firstCandlestickSetColumn() + 1; + QVERIFY(m_vMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + m_model->removeRows(m_modelRowCount - 1, 1); + QCOMPARE(m_series->count(), count); + + int removeCount = m_model->rowCount() - m_vMapper->closeRow(); + m_model->removeRows(m_vMapper->closeRow(), removeCount); + QCOMPARE(m_series->count(), 0); +} + +void tst_qcandlestickmodelmapper::verticalModelInsertColumns() +{ + createVerticalMapper(); + int count = m_vMapper->lastCandlestickSetColumn() - m_vMapper->firstCandlestickSetColumn() + 1; + QVERIFY(m_vMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + m_model->insertColumns(3, 4); + QCOMPARE(m_series->count(), count); +} + +void tst_qcandlestickmodelmapper::verticalModelRemoveColumns() +{ + createVerticalMapper(); + int count = m_vMapper->lastCandlestickSetColumn() - m_vMapper->firstCandlestickSetColumn() + 1; + QVERIFY(m_vMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + int removeCount = m_modelColumnCount - 2; + m_model->removeColumns(0, removeCount); + QCOMPARE(m_series->count(), qMin(m_model->columnCount(), count)); + + // leave all the columns + m_model->removeColumns(0, m_modelColumnCount - removeCount); + QCOMPARE(m_series->count(), 0); +} + +void tst_qcandlestickmodelmapper::horizontalModelInsertRows() +{ + createHorizontalMapper(); + int count = m_hMapper->lastCandlestickSetRow() - m_hMapper->firstCandlestickSetRow() + 1; + QVERIFY(m_hMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + m_model->insertRows(3, 4); + QCOMPARE(m_series->count(), count); +} + +void tst_qcandlestickmodelmapper::horizontalModelRemoveRows() +{ + createHorizontalMapper(); + int count = m_hMapper->lastCandlestickSetRow() - m_hMapper->firstCandlestickSetRow() + 1; + QVERIFY(m_hMapper->model() != 0); + QCOMPARE(m_series->count(), qMin(m_model->rowCount(), count)); + + int removeCount = m_modelRowCount - 2; + m_model->removeRows(0, removeCount); + QCOMPARE(m_series->count(), qMin(m_model->rowCount(), count)); + + // leave all the columns + m_model->removeRows(0, m_modelRowCount - removeCount); + QCOMPARE(m_series->count(), 0); +} + +void tst_qcandlestickmodelmapper::horizontalModelInsertColumns() +{ + createHorizontalMapper(); + int count = m_hMapper->lastCandlestickSetRow() - m_hMapper->firstCandlestickSetRow() + 1; + QVERIFY(m_hMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + m_model->insertColumns(3, 4); + QCOMPARE(m_series->count(), count); +} + +void tst_qcandlestickmodelmapper::horizontalModelRemoveColumns() +{ + createHorizontalMapper(); + int count = m_hMapper->lastCandlestickSetRow() - m_hMapper->firstCandlestickSetRow() + 1; + QVERIFY(m_hMapper->model() != 0); + QCOMPARE(m_series->count(), count); + + m_model->removeColumns(m_modelColumnCount - 1, 1); + QCOMPARE(m_series->count(), count); + + int removeCount = m_model->columnCount() - m_hMapper->closeColumn(); + m_model->removeColumns(m_hMapper->closeColumn(), removeCount); + QCOMPARE(m_series->count(), 0); +} + +void tst_qcandlestickmodelmapper::modelUpdateCell() +{ + createVerticalMapper(); + QVERIFY(m_vMapper->model() != 0); + + QModelIndex index = m_model->index(m_vMapper->timestampRow(), 0); + qreal newValue = 44.0; + QVERIFY(m_model->setData(index, newValue)); + QCOMPARE(m_model->data(index).toReal(), newValue); + QCOMPARE(m_series->candlestickSets().at(index.row())->timestamp(), newValue); +} + +void tst_qcandlestickmodelmapper::verticalMapperSignals() +{ + QVCandlestickModelMapper *mapper = new QVCandlestickModelMapper(); + + QSignalSpy spy0(mapper, SIGNAL(modelReplaced())); + QSignalSpy spy1(mapper, SIGNAL(seriesReplaced())); + QSignalSpy spy2(mapper, SIGNAL(timestampRowChanged())); + QSignalSpy spy3(mapper, SIGNAL(openRowChanged())); + QSignalSpy spy4(mapper, SIGNAL(highRowChanged())); + QSignalSpy spy5(mapper, SIGNAL(lowRowChanged())); + QSignalSpy spy6(mapper, SIGNAL(closeRowChanged())); + QSignalSpy spy7(mapper, SIGNAL(firstCandlestickSetColumnChanged())); + QSignalSpy spy8(mapper, SIGNAL(lastCandlestickSetColumnChanged())); + + mapper->setModel(m_model); + mapper->setSeries(m_series); + mapper->setTimestampRow(1); + mapper->setOpenRow(2); + mapper->setHighRow(3); + mapper->setLowRow(4); + mapper->setCloseRow(5); + mapper->setFirstCandlestickSetColumn(0); + mapper->setLastCandlestickSetColumn(1); + + QCOMPARE(spy0.count(), 1); + QCOMPARE(spy1.count(), 1); + QCOMPARE(spy2.count(), 1); + QCOMPARE(spy3.count(), 1); + QCOMPARE(spy4.count(), 1); + QCOMPARE(spy5.count(), 1); + QCOMPARE(spy6.count(), 1); + QCOMPARE(spy7.count(), 1); + QCOMPARE(spy8.count(), 1); + + delete mapper; +} + +void tst_qcandlestickmodelmapper::horizontalMapperSignals() +{ + QHCandlestickModelMapper *mapper = new QHCandlestickModelMapper(); + + QSignalSpy spy0(mapper, SIGNAL(modelReplaced())); + QSignalSpy spy1(mapper, SIGNAL(seriesReplaced())); + QSignalSpy spy2(mapper, SIGNAL(timestampColumnChanged())); + QSignalSpy spy3(mapper, SIGNAL(openColumnChanged())); + QSignalSpy spy4(mapper, SIGNAL(highColumnChanged())); + QSignalSpy spy5(mapper, SIGNAL(lowColumnChanged())); + QSignalSpy spy6(mapper, SIGNAL(closeColumnChanged())); + QSignalSpy spy7(mapper, SIGNAL(firstCandlestickSetRowChanged())); + QSignalSpy spy8(mapper, SIGNAL(lastCandlestickSetRowChanged())); + + mapper->setModel(m_model); + mapper->setSeries(m_series); + mapper->setTimestampColumn(1); + mapper->setOpenColumn(2); + mapper->setHighColumn(3); + mapper->setLowColumn(4); + mapper->setCloseColumn(5); + mapper->setFirstCandlestickSetRow(0); + mapper->setLastCandlestickSetRow(1); + + QCOMPARE(spy0.count(), 1); + QCOMPARE(spy1.count(), 1); + QCOMPARE(spy2.count(), 1); + QCOMPARE(spy3.count(), 1); + QCOMPARE(spy4.count(), 1); + QCOMPARE(spy5.count(), 1); + QCOMPARE(spy6.count(), 1); + QCOMPARE(spy7.count(), 1); + QCOMPARE(spy8.count(), 1); + + delete mapper; +} + +QTEST_MAIN(tst_qcandlestickmodelmapper) + +#include "tst_qcandlestickmodelmapper.moc" diff --git a/tests/auto/qcandlestickseries/qcandlestickseries.pro b/tests/auto/qcandlestickseries/qcandlestickseries.pro new file mode 100644 index 00000000..ccaa2e25 --- /dev/null +++ b/tests/auto/qcandlestickseries/qcandlestickseries.pro @@ -0,0 +1,5 @@ +!include( ../auto.pri ) { + error( "Couldn't find the auto.pri file!" ) +} + +SOURCES += tst_qcandlestickseries.cpp diff --git a/tests/auto/qcandlestickseries/tst_qcandlestickseries.cpp b/tests/auto/qcandlestickseries/tst_qcandlestickseries.cpp new file mode 100644 index 00000000..61201bf3 --- /dev/null +++ b/tests/auto/qcandlestickseries/tst_qcandlestickseries.cpp @@ -0,0 +1,936 @@ +/**************************************************************************** +** +** 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 <QtCharts/QChartView> +#include <QtTest/QtTest> +#include "tst_definitions.h" + +QT_CHARTS_USE_NAMESPACE + +Q_DECLARE_METATYPE(QCandlestickSet *) +Q_DECLARE_METATYPE(QList<QCandlestickSet *>) + +class tst_QCandlestickSeries : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private Q_SLOTS: + void qCandlestickSeries(); + void append(); + void remove(); + void appendList(); + void removeList(); + void insert(); + void take(); + void clear(); + void candlestickSets(); + void count(); + void type(); + void maximumColumnWidth_data(); + void maximumColumnWidth(); + void minimumColumnWidth_data(); + void minimumColumnWidth(); + void bodyWidth_data(); + void bodyWidth(); + void bodyOutlineVisible(); + void capsWidth_data(); + void capsWidth(); + void capsVisible(); + void increasingColor(); + void decreasingColor(); + void brush(); + void pen(); + void mouseClicked(); + void mouseHovered(); + void mousePressed(); + void mouseReleased(); + void mouseDoubleClicked(); + +private: + QCandlestickSeries *m_series; + QList<QCandlestickSet *> m_sets; +}; + +void tst_QCandlestickSeries::initTestCase() +{ + qRegisterMetaType<QCandlestickSet *>("QCandlestickSet *"); + qRegisterMetaType<QList<QCandlestickSet *>>("QList<QCandlestickSet *>"); +} + +void tst_QCandlestickSeries::cleanupTestCase() +{ + QTest::qWait(1); // Allow final deleteLaters to run +} + +void tst_QCandlestickSeries::init() +{ + m_series = new QCandlestickSeries(); + m_series->setMaximumColumnWidth(5432.1); + m_series->setMinimumColumnWidth(2.0); + m_series->setBodyWidth(0.99); + m_series->setCapsWidth(0.99); + + for (int i = 0; i < 5; ++i) { + qreal timestamp = QDateTime::currentMSecsSinceEpoch() + i * 1000000; + + QCandlestickSet *set = new QCandlestickSet(timestamp); + set->setOpen(4); + set->setHigh(4); + set->setLow(1); + set->setClose(1); + + m_sets.append(set); + } +} + +void tst_QCandlestickSeries::cleanup() +{ + foreach (QCandlestickSet *set, m_sets) { + m_series->remove(set); + m_sets.removeAll(set); + delete set; + } + + delete m_series; + m_series = nullptr; +} + +void tst_QCandlestickSeries::qCandlestickSeries() +{ + QCandlestickSeries *series = new QCandlestickSeries(); + + QVERIFY(series != nullptr); + + delete series; + series = nullptr; +} + +void tst_QCandlestickSeries::append() +{ + QCOMPARE(m_series->count(), 0); + + // Try adding set + QCandlestickSet *set1 = new QCandlestickSet(1234.0); + QVERIFY(m_series->append(set1)); + QCOMPARE(m_series->count(), 1); + + // Try adding another set + QCandlestickSet *set2 = new QCandlestickSet(2345.0); + QVERIFY(m_series->append(set2)); + QCOMPARE(m_series->count(), 2); + + // Try adding same set again + QVERIFY(!m_series->append(set2)); + QCOMPARE(m_series->count(), 2); + + // Try adding null set + QVERIFY(!m_series->append(nullptr)); + QCOMPARE(m_series->count(), 2); +} + +void tst_QCandlestickSeries::remove() +{ + m_series->append(m_sets); + QCOMPARE(m_series->count(), m_sets.count()); + + // Try to remove null pointer (should not remove, should not crash) + QVERIFY(!m_series->remove(nullptr)); + QCOMPARE(m_series->count(), m_sets.count()); + + // Try to remove invalid pointer (should not remove, should not crash) + QVERIFY(!m_series->remove((QCandlestickSet *)(m_sets.at(0) + 1))); + QCOMPARE(m_series->count(), m_sets.count()); + + // Remove some sets + const int removeCount = 3; + for (int i = 0; i < removeCount; ++i) + QVERIFY(m_series->remove(m_sets.at(i))); + QCOMPARE(m_series->count(), m_sets.count() - removeCount); + + for (int i = removeCount; i < m_sets.count(); ++i) + QCOMPARE(m_series->candlestickSets().at(i - removeCount), m_sets.at(i)); + + // Try removing all sets again (should be ok, even if some sets have already been removed) + for (int i = 0; i < m_sets.count(); ++i) + m_series->remove(m_sets.at(i)); + QCOMPARE(m_series->count(), 0); +} + +void tst_QCandlestickSeries::appendList() +{ + QCOMPARE(m_series->count(), 0); + + // Append new sets (should succeed, count should match the count of sets) + QVERIFY(m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_series->count()); + + // Append same sets again (should fail, count should remain same) + QVERIFY(!m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_series->count()); + + // Try append empty list (should succeed, but count should remain same) + QList<QCandlestickSet *> invalidList; + QVERIFY(m_series->append(invalidList)); + QCOMPARE(m_series->count(), m_sets.count()); + + // Try append list with one new and one existing set (should fail, count remains same) + invalidList.append(new QCandlestickSet()); + invalidList.append(m_sets.at(0)); + QVERIFY(!m_series->append(invalidList)); + QCOMPARE(m_series->count(), m_sets.count()); + delete invalidList.at(0); + invalidList.clear(); + + // Try append list with null pointers (should fail, count remains same) + QVERIFY(invalidList.isEmpty()); + invalidList.append(nullptr); + invalidList.append(nullptr); + invalidList.append(nullptr); + QVERIFY(!m_series->append(invalidList)); + QCOMPARE(m_series->count(), m_sets.count()); +} + +void tst_QCandlestickSeries::removeList() +{ + m_series->append(m_sets); + QCOMPARE(m_series->count(), m_sets.count()); + + // Try remove empty list (should fail, but count should remain same) + QList<QCandlestickSet *> invalidList; + QVERIFY(!m_series->remove(invalidList)); + QCOMPARE(m_series->count(), m_sets.count()); + + // Try remove list with one new and one existing set (should fail, count remains same) + invalidList.append(new QCandlestickSet()); + invalidList.append(m_sets.at(0)); + QVERIFY(!m_series->remove(invalidList)); + QCOMPARE(m_series->count(), m_sets.count()); + delete invalidList.at(0); + invalidList.clear(); + + // Try remove list with null pointers (should fail, count remains same) + QVERIFY(invalidList.isEmpty()); + invalidList.append(nullptr); + invalidList.append(nullptr); + invalidList.append(nullptr); + QVERIFY(!m_series->remove(invalidList)); + QCOMPARE(m_series->count(), m_sets.count()); + + // Remove all sets (should succeed, count should be zero) + QVERIFY(m_series->remove(m_sets)); + QCOMPARE(m_series->count(), 0); + + // Remove same sets again (should fail, count should remain zero) + QVERIFY(!m_series->remove(m_sets)); + QCOMPARE(m_series->count(), 0); +} + +void tst_QCandlestickSeries::insert() +{ + QCOMPARE(m_series->count(), 0); + + QSignalSpy countSpy(m_series, SIGNAL(countChanged())); + QSignalSpy addedSpy(m_series, SIGNAL(candlestickSetsAdded(QList<QCandlestickSet *>))); + + for (int i = 0; i < m_sets.count(); ++i) { + QCandlestickSet *set = m_sets.at(i); + QVERIFY(m_series->insert(0, set)); + QCOMPARE(m_series->count(), i + 1); + QTRY_COMPARE(countSpy.count(), i + 1); + QTRY_COMPARE(addedSpy.count(), i + 1); + + QList<QVariant> args = addedSpy.value(i); + QCOMPARE(args.count(), 1); + QList<QCandlestickSet *> sets = qvariant_cast<QList<QCandlestickSet *>>(args.at(0)); + QCOMPARE(sets.count(), 1); + QCOMPARE(sets.first(), set); + } +} + +void tst_QCandlestickSeries::take() +{ + m_series->append(m_sets); + QCOMPARE(m_series->count(), m_sets.count()); + + QSignalSpy countSpy(m_series, SIGNAL(countChanged())); + QSignalSpy removedSpy(m_series, SIGNAL(candlestickSetsRemoved(QList<QCandlestickSet *>))); + + for (int i = 0; i < m_sets.count(); ++i) { + QCandlestickSet *set = m_sets.at(i); + QVERIFY(m_series->take(set)); + QCOMPARE(m_series->count(), m_sets.count() - i - 1); + QTRY_COMPARE(countSpy.count(), i + 1); + QTRY_COMPARE(removedSpy.count(), i + 1); + + QList<QVariant> args = removedSpy.value(i); + QCOMPARE(args.count(), 1); + QList<QCandlestickSet *> sets = qvariant_cast<QList<QCandlestickSet *>>(args.at(0)); + QCOMPARE(sets.count(), 1); + QCOMPARE(sets.first(), set); + } +} + +void tst_QCandlestickSeries::clear() +{ + m_series->append(m_sets); + QCOMPARE(m_series->count(), m_sets.count()); + + m_series->clear(); + QCOMPARE(m_series->count(), 0); +} + +void tst_QCandlestickSeries::candlestickSets() +{ + m_series->append(m_sets); + QCOMPARE(m_series->candlestickSets(), m_sets); + + for (int i = 0; i < m_sets.count(); ++i) + QCOMPARE(m_series->candlestickSets().at(i), m_sets.at(i)); + + m_series->clear(); + QCOMPARE(m_series->candlestickSets(), QList<QCandlestickSet *>()); +} + +void tst_QCandlestickSeries::count() +{ + m_series->append(m_sets); + QCOMPARE(m_series->count(), m_sets.count()); + QCOMPARE(m_series->count(), m_series->candlestickSets().count()); +} + +void tst_QCandlestickSeries::type() +{ + QCOMPARE(m_series->type(), QAbstractSeries::SeriesTypeCandlestick); +} + +void tst_QCandlestickSeries::maximumColumnWidth_data() +{ + QTest::addColumn<qreal>("maximumColumnWidth"); + QTest::addColumn<qreal>("expectedMaximumColumnWidth"); + + QTest::newRow("maximum column width less than -1.0") << -3.0 << -1.0; + QTest::newRow("maximum column equals to -1.0") << -1.0 << -1.0; + QTest::newRow("maximum column width greater than -1.0, but less than zero") << -0.5 << -1.0; + QTest::newRow("maximum column width equals zero") << 0.0 << 0.0; + QTest::newRow("maximum column width greater than zero") << 1.0 << 1.0; + QTest::newRow("maximum column width contains a fractional part") << 3.4 << 3.4; +} + +void tst_QCandlestickSeries::maximumColumnWidth() +{ + QFETCH(qreal, maximumColumnWidth); + QFETCH(qreal, expectedMaximumColumnWidth); + + QSignalSpy spy(m_series, SIGNAL(maximumColumnWidthChanged())); + + m_series->setMaximumColumnWidth(maximumColumnWidth); + QCOMPARE(m_series->maximumColumnWidth(), expectedMaximumColumnWidth); + QCOMPARE(spy.count(), 1); + + // Try set same maximum column width + m_series->setMaximumColumnWidth(expectedMaximumColumnWidth); + QCOMPARE(m_series->maximumColumnWidth(), expectedMaximumColumnWidth); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::minimumColumnWidth_data() +{ + QTest::addColumn<qreal>("minimumColumnWidth"); + QTest::addColumn<qreal>("expectedMinimumColumnWidth"); + + QTest::newRow("minimum column width less than -1.0") << -3.0 << -1.0; + QTest::newRow("minimum column equals to -1.0") << -1.0 << -1.0; + QTest::newRow("minimum column width greater than -1.0, but less than zero") << -0.5 << -1.0; + QTest::newRow("minimum column width equals zero") << 0.0 << 0.0; + QTest::newRow("minimum column width greater than zero") << 1.0 << 1.0; + QTest::newRow("minimum column width contains a fractional part") << 3.4 << 3.4; +} + +void tst_QCandlestickSeries::minimumColumnWidth() +{ + QFETCH(qreal, minimumColumnWidth); + QFETCH(qreal, expectedMinimumColumnWidth); + + QSignalSpy spy(m_series, SIGNAL(minimumColumnWidthChanged())); + + m_series->setMinimumColumnWidth(minimumColumnWidth); + QCOMPARE(m_series->minimumColumnWidth(), expectedMinimumColumnWidth); + QCOMPARE(spy.count(), 1); + + // Try set same minimum column width + m_series->setMinimumColumnWidth(expectedMinimumColumnWidth); + QCOMPARE(m_series->minimumColumnWidth(), expectedMinimumColumnWidth); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::bodyWidth_data() +{ + QTest::addColumn<qreal>("bodyWidth"); + QTest::addColumn<qreal>("expectedBodyWidth"); + + QTest::newRow("body width less than zero") << -1.0 << 0.0; + QTest::newRow("body width equals zero") << 0.0 << 0.0; + QTest::newRow("body width greater than zero and less than one") << 0.5 << 0.5; + QTest::newRow("body width equals one") << 1.0 << 1.0; + QTest::newRow("body width greater than one") << 2.0 << 1.0; +} + +void tst_QCandlestickSeries::bodyWidth() +{ + QFETCH(qreal, bodyWidth); + QFETCH(qreal, expectedBodyWidth); + + QSignalSpy spy(m_series, SIGNAL(bodyWidthChanged())); + + m_series->setBodyWidth(bodyWidth); + QCOMPARE(m_series->bodyWidth(), expectedBodyWidth); + QCOMPARE(spy.count(), 1); + + // Try set same body width + m_series->setBodyWidth(bodyWidth); + QCOMPARE(m_series->bodyWidth(), expectedBodyWidth); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::bodyOutlineVisible() +{ + QSignalSpy spy(m_series, SIGNAL(bodyOutlineVisibilityChanged())); + + bool visible = !m_series->bodyOutlineVisible(); + m_series->setBodyOutlineVisible(visible); + QCOMPARE(m_series->bodyOutlineVisible(), visible); + QCOMPARE(spy.count(), 1); + + // Try set same body outline visibility + m_series->setBodyOutlineVisible(visible); + QCOMPARE(m_series->bodyOutlineVisible(), visible); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::capsWidth_data() +{ + QTest::addColumn<qreal>("capsWidth"); + QTest::addColumn<qreal>("expectedCapsWidth"); + + QTest::newRow("caps width less than zero") << -1.0 << 0.0; + QTest::newRow("caps width equals zero") << 0.0 << 0.0; + QTest::newRow("caps width greater than zero and less than one") << 0.5 << 0.5; + QTest::newRow("caps width equals one") << 1.0 << 1.0; + QTest::newRow("caps width greater than one") << 2.0 << 1.0; +} + +void tst_QCandlestickSeries::capsWidth() +{ + QFETCH(qreal, capsWidth); + QFETCH(qreal, expectedCapsWidth); + + QSignalSpy spy(m_series, SIGNAL(capsWidthChanged())); + + m_series->setCapsWidth(capsWidth); + QCOMPARE(m_series->capsWidth(), expectedCapsWidth); + QCOMPARE(spy.count(), 1); + + // Try set same caps width + m_series->setCapsWidth(capsWidth); + QCOMPARE(m_series->capsWidth(), expectedCapsWidth); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::capsVisible() +{ + QSignalSpy spy(m_series, SIGNAL(capsVisibilityChanged())); + + bool visible = !m_series->capsVisible(); + m_series->setCapsVisible(visible); + QCOMPARE(m_series->capsVisible(), visible); + QCOMPARE(spy.count(), 1); + + // Try set same caps visibility + m_series->setCapsVisible(visible); + QCOMPARE(m_series->capsVisible(), visible); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::increasingColor() +{ + QSignalSpy spy(m_series, SIGNAL(increasingColorChanged())); + + // Try set new increasing color + QColor newColor(200, 200, 200, 200); + m_series->setIncreasingColor(newColor); + QCOMPARE(m_series->increasingColor(), newColor); + QCOMPARE(spy.count(), 1); + + // Try set same increasing color again + m_series->setIncreasingColor(newColor); + QCOMPARE(m_series->increasingColor(), newColor); + QCOMPARE(spy.count(), 1); + + // Try set invalid increasing color (should change to default color) + QColor defaultColor = m_series->brush().color(); + defaultColor.setAlpha(128); + m_series->setIncreasingColor(QColor()); + QCOMPARE(m_series->increasingColor(), defaultColor); + QCOMPARE(spy.count(), 2); + + // Set new brush, increasing color should change accordingly + QBrush brush(newColor); + defaultColor = brush.color(); + defaultColor.setAlpha(128); + m_series->setBrush(brush); + QCOMPARE(m_series->increasingColor(), defaultColor); + QCOMPARE(spy.count(), 3); +} + +void tst_QCandlestickSeries::decreasingColor() +{ + QSignalSpy spy(m_series, SIGNAL(decreasingColorChanged())); + + // Try set new decreasing color + QColor newColor(200, 200, 200, 200); + m_series->setDecreasingColor(newColor); + QCOMPARE(m_series->decreasingColor(), newColor); + QCOMPARE(spy.count(), 1); + + // Try set same decreasing color again + m_series->setDecreasingColor(newColor); + QCOMPARE(m_series->decreasingColor(), newColor); + QCOMPARE(spy.count(), 1); + + // Try set invalid decreasing color (should change to default color) + m_series->setDecreasingColor(QColor()); + QCOMPARE(m_series->decreasingColor(), m_series->brush().color()); + QCOMPARE(spy.count(), 2); + + // Set new brush, decreasing color should change accordingly + m_series->setBrush(QBrush(newColor)); + QCOMPARE(m_series->decreasingColor(), m_series->brush().color()); + QCOMPARE(spy.count(), 3); +} + +void tst_QCandlestickSeries::brush() +{ + QSignalSpy spy(m_series, SIGNAL(brushChanged())); + + QBrush brush(QColor(128, 128, 128, 128)); + QColor increasingColor(brush.color()); + increasingColor.setAlpha(128); + QColor decreasingColor(brush.color()); + m_series->setBrush(brush); + QCOMPARE(m_series->brush(), brush); + QCOMPARE(m_series->increasingColor(), increasingColor); + QCOMPARE(m_series->decreasingColor(), decreasingColor); + QCOMPARE(spy.count(), 1); + + // Try set same brush + m_series->setBrush(brush); + QCOMPARE(m_series->brush(), brush); + QCOMPARE(m_series->increasingColor(), increasingColor); + QCOMPARE(m_series->decreasingColor(), decreasingColor); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::pen() +{ + QSignalSpy spy(m_series, SIGNAL(penChanged())); + + QPen pen(QColor(128, 128, 128, 128)); + m_series->setPen(pen); + QCOMPARE(m_series->pen(), pen); + QCOMPARE(spy.count(), 1); + + // Try set same pen + m_series->setPen(pen); + QCOMPARE(m_series->pen(), pen); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSeries::mouseClicked() +{ + SKIP_IF_CANNOT_TEST_MOUSE_EVENTS(); + + QVERIFY(m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_sets.count()); + + QCandlestickSet *set1 = m_series->candlestickSets().at(1); + QCandlestickSet *set2 = m_series->candlestickSets().at(2); + + QSignalSpy seriesSpy(m_series, SIGNAL(clicked(QCandlestickSet *))); + QSignalSpy setSpy1(set1, SIGNAL(clicked())); + QSignalSpy setSpy2(set2, SIGNAL(clicked())); + + QChartView view(new QChart()); + view.resize(400, 300); + view.chart()->addSeries(m_series); + view.chart()->createDefaultAxes(); + view.show(); + QTest::qWaitForWindowShown(&view); + + // Calculate expected layout for candlesticks + QRectF plotArea = view.chart()->plotArea(); + qreal candlestickWidth = plotArea.width() / m_series->count(); + qreal candlestickHeight = plotArea.height(); + + QMap<QCandlestickSet *, QRectF> layout; + layout.insert(set1, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set1), + plotArea.top(), candlestickWidth, candlestickHeight)); + layout.insert(set2, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set2), + plotArea.top(), candlestickWidth, candlestickHeight)); + + // Click set 1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, layout.value(set1).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 1); + QCOMPARE(setSpy2.count(), 0); + + QList<QVariant> seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set1); + seriesSpyArgs.clear(); + + QVERIFY(setSpy1.takeFirst().isEmpty()); + + // Click set 2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, layout.value(set2).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 0); + QCOMPARE(setSpy2.count(), 1); + + seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set2); + seriesSpyArgs.clear(); + + QVERIFY(setSpy2.takeFirst().isEmpty()); +} + +void tst_QCandlestickSeries::mouseHovered() +{ + SKIP_IF_CANNOT_TEST_MOUSE_EVENTS(); + + QVERIFY(m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_sets.count()); + + QCandlestickSet *set1 = m_series->candlestickSets().at(1); + QCandlestickSet *set2 = m_series->candlestickSets().at(2); + + QSignalSpy seriesSpy(m_series, SIGNAL(hovered(bool, QCandlestickSet *))); + QSignalSpy setSpy1(set1, SIGNAL(hovered(bool))); + QSignalSpy setSpy2(set2, SIGNAL(hovered(bool))); + + QChartView view(new QChart()); + view.resize(400, 300); + view.chart()->addSeries(m_series); + view.chart()->createDefaultAxes(); + view.show(); + QTest::qWaitForWindowShown(&view); + + // This is hack since view does not get events otherwise + view.setMouseTracking(true); + + // Calculate expected layout for candlesticks + QRectF plotArea = view.chart()->plotArea(); + qreal candlestickWidth = plotArea.width() / m_series->count(); + qreal candlestickHeight = plotArea.height(); + + QMap<QCandlestickSet *, QRectF> layout; + layout.insert(set1, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set1), + plotArea.top(), candlestickWidth, candlestickHeight)); + layout.insert(set2, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set2), + plotArea.top(), candlestickWidth, candlestickHeight)); + + // Move mouse to left border + QTest::mouseMove(view.viewport(), QPoint(0, layout.value(set1).center().y())); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 0); + QCOMPARE(setSpy1.count(), 0); + QCOMPARE(setSpy2.count(), 0); + + // Move mouse on top of set 1 + QTest::mouseMove(view.viewport(), layout.value(set1).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 1); + QCOMPARE(setSpy2.count(), 0); + + QList<QVariant> seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(1)), set1); + QCOMPARE(seriesSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(seriesSpyArgs.at(0).toBool(), true); + seriesSpyArgs.clear(); + + QList<QVariant> setSpyArgs = setSpy1.takeFirst(); + QCOMPARE(setSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(setSpyArgs.at(0).toBool(), true); + setSpyArgs.clear(); + + // Move mouse from top of set 1 to top of set 2 + QTest::mouseMove(view.viewport(), layout.value(set2).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 2); + QCOMPARE(setSpy1.count(), 1); + QCOMPARE(setSpy2.count(), 1); + + // Should leave set 1 + seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(1)), set1); + QCOMPARE(seriesSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(seriesSpyArgs.at(0).toBool(), false); + // Don't call seriesSpyArgs.clear() here + + setSpyArgs = setSpy1.takeFirst(); + QCOMPARE(setSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(setSpyArgs.at(0).toBool(), false); + // Don't call setSpyArgs.clear() here + + // Should enter set 2 + seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(1)), set2); + QCOMPARE(seriesSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(seriesSpyArgs.at(0).toBool(), true); + seriesSpyArgs.clear(); + + setSpyArgs = setSpy2.takeFirst(); + QCOMPARE(setSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(setSpyArgs.at(0).toBool(), true); + setSpyArgs.clear(); + + // Move mouse from top of set 2 to background + QTest::mouseMove(view.viewport(), QPoint(layout.value(set2).center().x(), 0)); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 0); + QCOMPARE(setSpy2.count(), 1); + + // Should leave set 2 + seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(1)), set2); + QCOMPARE(seriesSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(seriesSpyArgs.at(0).toBool(), false); + seriesSpyArgs.clear(); + + setSpyArgs = setSpy2.takeFirst(); + QCOMPARE(setSpyArgs.at(0).type(), QVariant::Bool); + QCOMPARE(setSpyArgs.at(0).toBool(), false); + setSpyArgs.clear(); +} + +void tst_QCandlestickSeries::mousePressed() +{ + SKIP_IF_CANNOT_TEST_MOUSE_EVENTS(); + + QVERIFY(m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_sets.count()); + + QCandlestickSet *set1 = m_series->candlestickSets().at(1); + QCandlestickSet *set2 = m_series->candlestickSets().at(2); + + QSignalSpy seriesSpy(m_series, SIGNAL(pressed(QCandlestickSet *))); + QSignalSpy setSpy1(set1, SIGNAL(pressed())); + QSignalSpy setSpy2(set2, SIGNAL(pressed())); + + QChartView view(new QChart()); + view.resize(400, 300); + view.chart()->addSeries(m_series); + view.chart()->createDefaultAxes(); + view.show(); + QTest::qWaitForWindowShown(&view); + + // Calculate expected layout for candlesticks + QRectF plotArea = view.chart()->plotArea(); + qreal candlestickWidth = plotArea.width() / m_series->count(); + qreal candlestickHeight = plotArea.height(); + + QMap<QCandlestickSet *, QRectF> layout; + layout.insert(set1, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set1), + plotArea.top(), candlestickWidth, candlestickHeight)); + layout.insert(set2, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set2), + plotArea.top(), candlestickWidth, candlestickHeight)); + + // Press set 1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, layout.value(set1).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 1); + QCOMPARE(setSpy2.count(), 0); + + QList<QVariant> seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set1); + seriesSpyArgs.clear(); + + QVERIFY(setSpy1.takeFirst().isEmpty()); + + // Press set 2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, layout.value(set2).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 0); + QCOMPARE(setSpy2.count(), 1); + + seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set2); + seriesSpyArgs.clear(); + + QVERIFY(setSpy2.takeFirst().isEmpty()); +} + +void tst_QCandlestickSeries::mouseReleased() +{ + SKIP_IF_CANNOT_TEST_MOUSE_EVENTS(); + + QVERIFY(m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_sets.count()); + + QCandlestickSet *set1 = m_series->candlestickSets().at(1); + QCandlestickSet *set2 = m_series->candlestickSets().at(2); + + QSignalSpy seriesSpy(m_series, SIGNAL(released(QCandlestickSet *))); + QSignalSpy setSpy1(set1, SIGNAL(released())); + QSignalSpy setSpy2(set2, SIGNAL(released())); + + QChartView view(new QChart()); + view.resize(400, 300); + view.chart()->addSeries(m_series); + view.chart()->createDefaultAxes(); + view.show(); + QTest::qWaitForWindowShown(&view); + + // Calculate expected layout for candlesticks + QRectF plotArea = view.chart()->plotArea(); + qreal candlestickWidth = plotArea.width() / m_series->count(); + qreal candlestickHeight = plotArea.height(); + + QMap<QCandlestickSet *, QRectF> layout; + layout.insert(set1, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set1), + plotArea.top(), candlestickWidth, candlestickHeight)); + layout.insert(set2, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set2), + plotArea.top(), candlestickWidth, candlestickHeight)); + + // Release mouse over set 1 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, layout.value(set1).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 1); + QCOMPARE(setSpy2.count(), 0); + + QList<QVariant> seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set1); + seriesSpyArgs.clear(); + + QVERIFY(setSpy1.takeFirst().isEmpty()); + + // Release mouse over set 2 + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, layout.value(set2).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 0); + QCOMPARE(setSpy2.count(), 1); + + seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set2); + seriesSpyArgs.clear(); + + QVERIFY(setSpy2.takeFirst().isEmpty()); +} + +void tst_QCandlestickSeries::mouseDoubleClicked() +{ + SKIP_IF_CANNOT_TEST_MOUSE_EVENTS(); + + QVERIFY(m_series->append(m_sets)); + QCOMPARE(m_series->count(), m_sets.count()); + + QCandlestickSet *set1 = m_series->candlestickSets().at(1); + QCandlestickSet *set2 = m_series->candlestickSets().at(2); + + QSignalSpy seriesSpy(m_series, SIGNAL(doubleClicked(QCandlestickSet *))); + QSignalSpy setSpy1(set1, SIGNAL(doubleClicked())); + QSignalSpy setSpy2(set2, SIGNAL(doubleClicked())); + + QChartView view(new QChart()); + view.resize(400, 300); + view.chart()->addSeries(m_series); + view.chart()->createDefaultAxes(); + view.show(); + QTest::qWaitForWindowShown(&view); + + // Calculate expected layout for candlesticks + QRectF plotArea = view.chart()->plotArea(); + qreal candlestickWidth = plotArea.width() / m_series->count(); + qreal candlestickHeight = plotArea.height(); + + QMap<QCandlestickSet *, QRectF> layout; + layout.insert(set1, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set1), + plotArea.top(), candlestickWidth, candlestickHeight)); + layout.insert(set2, QRectF(plotArea.left() + candlestickWidth * m_sets.indexOf(set2), + plotArea.top(), candlestickWidth, candlestickHeight)); + + // Double-click set 1 + QTest::mouseDClick(view.viewport(), Qt::LeftButton, 0, layout.value(set1).center().toPoint()); + QCoreApplication::processEvents(QEventLoop::AllEvents, 1000); + + QCOMPARE(seriesSpy.count(), 1); + QCOMPARE(setSpy1.count(), 1); + QCOMPARE(setSpy2.count(), 0); + + QList<QVariant> seriesSpyArgs = seriesSpy.takeFirst(); + QCOMPARE(seriesSpyArgs.count(), 1); + QCOMPARE(qvariant_cast<QCandlestickSet *>(seriesSpyArgs.at(0)), set1); + seriesSpyArgs.clear(); + + QVERIFY(setSpy1.takeFirst().isEmpty()); +} + +QTEST_MAIN(tst_QCandlestickSeries) + +#include "tst_qcandlestickseries.moc" diff --git a/tests/auto/qcandlestickset/qcandlestickset.pro b/tests/auto/qcandlestickset/qcandlestickset.pro new file mode 100644 index 00000000..e67d1df8 --- /dev/null +++ b/tests/auto/qcandlestickset/qcandlestickset.pro @@ -0,0 +1,5 @@ +!include( ../auto.pri ) { + error( "Couldn't find the auto.pri file!" ) +} + +SOURCES += tst_qcandlestickset.cpp diff --git a/tests/auto/qcandlestickset/tst_qcandlestickset.cpp b/tests/auto/qcandlestickset/tst_qcandlestickset.cpp new file mode 100644 index 00000000..5e94d4c9 --- /dev/null +++ b/tests/auto/qcandlestickset/tst_qcandlestickset.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** 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/QCandlestickSet> +#include <QtCore/QDateTime> +#include <QtTest/QtTest> + +QT_CHARTS_USE_NAMESPACE + +class tst_QCandlestickSet : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private Q_SLOTS: + void qCandlestickSet_data(); + void qCandlestickSet(); + void timestamp_data(); + void timestamp(); + void open_data(); + void open(); + void high_data(); + void high(); + void low_data(); + void low(); + void close_data(); + void close(); + void brush(); + void pen(); + +private: + QCandlestickSet* m_candlestickSet; +}; + +void tst_QCandlestickSet::initTestCase() +{ +} + +void tst_QCandlestickSet::cleanupTestCase() +{ + QTest::qWait(1); // Allow final deleteLaters to run +} + +void tst_QCandlestickSet::init() +{ + m_candlestickSet = new QCandlestickSet(QDateTime::currentMSecsSinceEpoch()); + m_candlestickSet->setOpen(2345.67); + m_candlestickSet->setHigh(4567.89); + m_candlestickSet->setLow(1234.56); + m_candlestickSet->setClose(3456.78); +} + +void tst_QCandlestickSet::cleanup() +{ + delete m_candlestickSet; + m_candlestickSet = nullptr; +} + +void tst_QCandlestickSet::qCandlestickSet_data() +{ + QTest::addColumn<qreal>("timestamp"); + QTest::addColumn<qreal>("expectedTimestamp"); + + QTest::newRow("timestamp less than zero") << -1.0 << 0.0; + QTest::newRow("timestamp equals zero") << 0.0 << 0.0; + QTest::newRow("timestamp greater than zero") << 1.0 << 1.0; + QTest::newRow("timestamp rounded down") << 4.321 << 4.0; + QTest::newRow("timestamp rounded up") << 5.678 << 6.0; +} + +void tst_QCandlestickSet::qCandlestickSet() +{ + QFETCH(qreal, timestamp); + QFETCH(qreal, expectedTimestamp); + + QCandlestickSet candlestickSet(timestamp); + QCOMPARE(candlestickSet.timestamp(), expectedTimestamp); +} + +void tst_QCandlestickSet::timestamp_data() +{ + QTest::addColumn<qreal>("timestamp"); + QTest::addColumn<qreal>("expectedTimestamp"); + + QTest::newRow("timestamp less than zero") << -1.0 << 0.0; + QTest::newRow("timestamp equals zero") << 0.0 << 0.0; + QTest::newRow("timestamp greater than zero") << 1.0 << 1.0; + QTest::newRow("timestamp rounded down") << 4.321 << 4.0; + QTest::newRow("timestamp rounded up") << 5.678 << 6.0; +} + +void tst_QCandlestickSet::timestamp() +{ + QFETCH(qreal, timestamp); + QFETCH(qreal, expectedTimestamp); + + QSignalSpy spy(m_candlestickSet, SIGNAL(timestampChanged())); + + m_candlestickSet->setTimestamp(timestamp); + QCOMPARE(m_candlestickSet->timestamp(), expectedTimestamp); + QCOMPARE(spy.count(), 1); + + // Try set same timestamp value + m_candlestickSet->setTimestamp(expectedTimestamp); + QCOMPARE(m_candlestickSet->timestamp(), expectedTimestamp); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSet::open_data() +{ + QTest::addColumn<qreal>("open"); + + QTest::newRow("open less than zero") << -1.234; + QTest::newRow("open equals zero") << 0.0; + QTest::newRow("open greater than zero") << 1.234; +} + +void tst_QCandlestickSet::open() +{ + QFETCH(qreal, open); + + QSignalSpy spy(m_candlestickSet, SIGNAL(openChanged())); + + m_candlestickSet->setOpen(open); + QCOMPARE(m_candlestickSet->open(), open); + QCOMPARE(spy.count(), 1); + + // Try set same open value + m_candlestickSet->setOpen(open); + QCOMPARE(m_candlestickSet->open(), open); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSet::high_data() +{ + QTest::addColumn<qreal>("high"); + + QTest::newRow("high less than zero") << -1.234; + QTest::newRow("high equals zero") << 0.0; + QTest::newRow("high greater than zero") << 1.234; +} + +void tst_QCandlestickSet::high() +{ + QFETCH(qreal, high); + + QSignalSpy spy(m_candlestickSet, SIGNAL(highChanged())); + + m_candlestickSet->setHigh(high); + QCOMPARE(m_candlestickSet->high(), high); + QCOMPARE(spy.count(), 1); + + // Try set same high value + m_candlestickSet->setHigh(high); + QCOMPARE(m_candlestickSet->high(), high); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSet::low_data() +{ + QTest::addColumn<qreal>("low"); + + QTest::newRow("low less than zero") << -1.234; + QTest::newRow("low equals zero") << 0.0; + QTest::newRow("low greater than zero") << 1.234; +} + +void tst_QCandlestickSet::low() +{ + QFETCH(qreal, low); + + QSignalSpy spy(m_candlestickSet, SIGNAL(lowChanged())); + + m_candlestickSet->setLow(low); + QCOMPARE(m_candlestickSet->low(), low); + QCOMPARE(spy.count(), 1); + + // Try set same low value + m_candlestickSet->setLow(low); + QCOMPARE(m_candlestickSet->low(), low); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSet::close_data() +{ + QTest::addColumn<qreal>("close"); + + QTest::newRow("close less than zero") << -1.234; + QTest::newRow("close equals zero") << 0.0; + QTest::newRow("close greater than zero") << 1.234; +} + +void tst_QCandlestickSet::close() +{ + QFETCH(qreal, close); + + QSignalSpy spy(m_candlestickSet, SIGNAL(closeChanged())); + + m_candlestickSet->setClose(close); + QCOMPARE(m_candlestickSet->close(), close); + QCOMPARE(spy.count(), 1); + + // Try set same close value + m_candlestickSet->setClose(close); + QCOMPARE(m_candlestickSet->close(), close); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSet::brush() +{ + QSignalSpy spy(m_candlestickSet, SIGNAL(brushChanged())); + + QCOMPARE(m_candlestickSet->brush(), QBrush(Qt::NoBrush)); + + m_candlestickSet->setBrush(QBrush(Qt::NoBrush)); + QCOMPARE(m_candlestickSet->brush(), QBrush(Qt::NoBrush)); + QCOMPARE(spy.count(), 0); + + QBrush brush(QColor(128, 128, 128, 128)); + m_candlestickSet->setBrush(brush); + QCOMPARE(m_candlestickSet->brush(), brush); + QCOMPARE(spy.count(), 1); + + // Try set same brush + m_candlestickSet->setBrush(brush); + QCOMPARE(m_candlestickSet->brush(), brush); + QCOMPARE(spy.count(), 1); +} + +void tst_QCandlestickSet::pen() +{ + QSignalSpy spy(m_candlestickSet, SIGNAL(penChanged())); + + QCOMPARE(m_candlestickSet->pen(), QPen(Qt::NoPen)); + + m_candlestickSet->setPen(QPen(Qt::NoPen)); + QCOMPARE(m_candlestickSet->pen(), QPen(Qt::NoPen)); + QCOMPARE(spy.count(), 0); + + QPen pen(QColor(128, 128, 128, 128)); + m_candlestickSet->setPen(pen); + QCOMPARE(m_candlestickSet->pen(), pen); + QCOMPARE(spy.count(), 1); + + // Try set same pen + m_candlestickSet->setPen(pen); + QCOMPARE(m_candlestickSet->pen(), pen); + QCOMPARE(spy.count(), 1); +} + +QTEST_GUILESS_MAIN(tst_QCandlestickSet) + +#include "tst_qcandlestickset.moc" diff --git a/tests/manual/candlesticktester/candlesticktester.pro b/tests/manual/candlesticktester/candlesticktester.pro new file mode 100644 index 00000000..fd927b6e --- /dev/null +++ b/tests/manual/candlesticktester/candlesticktester.pro @@ -0,0 +1,13 @@ +!include( ../../tests.pri ) { + error( "Couldn't find the test.pri file!" ) +} + +QT += widgets + +SOURCES += main.cpp \ + mainwidget.cpp \ + customtablemodel.cpp + +HEADERS += \ + mainwidget.h \ + customtablemodel.h diff --git a/tests/manual/candlesticktester/customtablemodel.cpp b/tests/manual/candlesticktester/customtablemodel.cpp new file mode 100644 index 00000000..e0122429 --- /dev/null +++ b/tests/manual/candlesticktester/customtablemodel.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** 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/QCandlestickSet> +#include <QtCore/QRect> +#include <QtCore/QVector> +#include <QtGui/QColor> +#include "customtablemodel.h" + +CustomTableModel::CustomTableModel(QObject *parent) + : QAbstractTableModel(parent) +{ + m_categories.append(QStringLiteral("Timestamp")); + m_categories.append(QStringLiteral("Open")); + m_categories.append(QStringLiteral("High")); + m_categories.append(QStringLiteral("Low")); + m_categories.append(QStringLiteral("Close")); +} + +CustomTableModel::~CustomTableModel() +{ + qDeleteAll(m_data); +} + +int CustomTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_data.count(); +} + +int CustomTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_categories.count(); +} + +QVariant CustomTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return m_categories[section]; + else + return QStringLiteral("%1").arg(section); +} + +bool CustomTableModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) { + m_data[index.row()]->replace(index.column(), value.toDouble()); + emit dataChanged(index, index); + + return true; + } + + return false; +} + +QVariant CustomTableModel::data(const QModelIndex &index, int role) const +{ + switch (role) { + case Qt::DisplayRole: + // fall through + case Qt::EditRole: + return m_data[index.row()]->at(index.column()); + case Qt::BackgroundRole: + foreach (QRect rect, m_mapping) { + if (rect.contains(index.column(), index.row())) + return QColor(m_mapping.key(rect)); + } + // cell is not mapped, return white color + return QColor(Qt::white); + default: + return QVariant(); + } +} + +Qt::ItemFlags CustomTableModel::flags(const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +bool CustomTableModel::insertRows(int row, int count, const QModelIndex &parent) +{ + beginInsertRows(parent, row, row + count - 1); + m_data.append(new QVector<qreal>(columnCount())); + endInsertRows(); + + return true; +} + +bool CustomTableModel::removeRows(int row, int count, const QModelIndex &parent) +{ + beginRemoveRows(parent, row, row + count - 1); + for (int i = row + count; i >= row; --i) + m_data.removeAt(i); + endRemoveRows(); + + return true; +} + +void CustomTableModel::addRow(QCandlestickSet *set) +{ + bool changed = insertRows(m_data.count(), 1); + + if (changed) { + QVector<qreal> *row = m_data.last(); + row->insert(0, set->timestamp()); + row->insert(1, set->open()); + row->insert(2, set->high()); + row->insert(3, set->low()); + row->insert(4, set->close()); + } +} + +void CustomTableModel::clearRows() +{ + bool changed = removeRows(0, m_data.count()); + if (changed) + m_data.clear(); +} + +void CustomTableModel::addMapping(QString color, QRect area) +{ + m_mapping.insertMulti(color, area); +} + +void CustomTableModel::clearMapping() +{ + m_mapping.clear(); +} diff --git a/tests/manual/candlesticktester/customtablemodel.h b/tests/manual/candlesticktester/customtablemodel.h new file mode 100644 index 00000000..4a8965c3 --- /dev/null +++ b/tests/manual/candlesticktester/customtablemodel.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef CUSTOMTABLEMODEL_H +#define CUSTOMTABLEMODEL_H + +#include <QtCharts/QChartGlobal> +#include <QtCore/QAbstractTableModel> +#include <QtCore/QHash> +#include <QtCore/QRect> + +QT_CHARTS_BEGIN_NAMESPACE +class QCandlestickSet; +QT_CHARTS_END_NAMESPACE + +QT_CHARTS_USE_NAMESPACE + +class CustomTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit CustomTableModel(QObject *parent = nullptr); + virtual ~CustomTableModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + + void addRow(QCandlestickSet *set); + void clearRows(); + + void addMapping(QString color, QRect area); + void clearMapping(); + +private: + QStringList m_categories; + QList<QVector<qreal> *> m_data; + QHash<QString, QRect> m_mapping; +}; + +#endif // CUSTOMTABLEMODEL_H diff --git a/tests/manual/candlesticktester/main.cpp b/tests/manual/candlesticktester/main.cpp new file mode 100644 index 00000000..3c893315 --- /dev/null +++ b/tests/manual/candlesticktester/main.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** 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 <QtWidgets/QApplication> +#include "mainwidget.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + MainWidget w; + w.setWindowTitle(QStringLiteral("Candlestick Chart Tester")); + w.resize(1280, 720); + w.show(); + + return a.exec(); +} diff --git a/tests/manual/candlesticktester/mainwidget.cpp b/tests/manual/candlesticktester/mainwidget.cpp new file mode 100644 index 00000000..7364f023 --- /dev/null +++ b/tests/manual/candlesticktester/mainwidget.cpp @@ -0,0 +1,691 @@ +/**************************************************************************** +** +** 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/QBarCategoryAxis> +#include <QtCharts/QCandlestickSeries> +#include <QtCharts/QCandlestickSet> +#include <QtCharts/QDateTimeAxis> +#include <QtCharts/QHCandlestickModelMapper> +#include <QtCharts/QValueAxis> +#include <QtCore/QDateTime> +#include <QtCore/QDebug> +#include <QtWidgets/QCheckBox> +#include <QtWidgets/QComboBox> +#include <QtWidgets/QDoubleSpinBox> +#include <QtWidgets/QGridLayout> +#include <QtWidgets/QHeaderView> +#include <QtWidgets/QLabel> +#include <QtWidgets/QPushButton> +#include <QtWidgets/QTableView> +#include "customtablemodel.h" +#include "mainwidget.h" + +QT_CHARTS_USE_NAMESPACE + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent), + m_chart(new QChart()), + m_chartView(new QChartView(m_chart, this)), + m_axisX(nullptr), + m_axisY(nullptr), + m_maximumColumnWidth(-1.0), + m_minimumColumnWidth(5.0), + m_bodyOutlineVisible(true), + m_capsVisible(false), + m_bodyWidth(0.5), + m_capsWidth(0.5), + m_customIncreasingColor(false), + m_customDecreasingColor(false), + m_hModelMapper(new QHCandlestickModelMapper(this)) +{ + qsrand(QDateTime::currentDateTime().toTime_t()); + + m_chartView->setRenderHint(QPainter::Antialiasing, false); + + m_hModelMapper->setModel(new CustomTableModel(this)); + m_hModelMapper->setTimestampColumn(0); + m_hModelMapper->setOpenColumn(1); + m_hModelMapper->setHighColumn(2); + m_hModelMapper->setLowColumn(3); + m_hModelMapper->setCloseColumn(4); + + QGridLayout *mainLayout = new QGridLayout(); + mainLayout->addLayout(createSeriesControlsLayout(), 0, 0); + mainLayout->addLayout(createSetsControlsLayout(), 1, 0); + mainLayout->addLayout(createCandlestickControlsLayout(), 2, 0); + mainLayout->addLayout(createMiscellaneousControlsLayout(), 3, 0); + mainLayout->addWidget(m_chartView, 0, 1, mainLayout->rowCount() + 1, 1); + mainLayout->addLayout(createModelMapperControlsLayout(), 0, 2, mainLayout->rowCount(), 1); + setLayout(mainLayout); + + addSeries(); +} + +MainWidget::~MainWidget() +{ +} + +QGridLayout *MainWidget::createSeriesControlsLayout() +{ + QGridLayout *layout = new QGridLayout(); + int row = 0; + + layout->addWidget(new QLabel(QStringLiteral("Series controls:")), row, 0, Qt::AlignLeft); + + QPushButton *addSeriesButton = new QPushButton(QStringLiteral("Add a series")); + connect(addSeriesButton, SIGNAL(clicked(bool)), this, SLOT(addSeries())); + layout->addWidget(addSeriesButton, row++, 1, Qt::AlignLeft); + + QPushButton *removeSeriesButton = new QPushButton(QStringLiteral("Remove a series")); + connect(removeSeriesButton, SIGNAL(clicked(bool)), this, SLOT(removeSeries())); + layout->addWidget(removeSeriesButton, row++, 1, Qt::AlignLeft); + + QPushButton *removeAllSeriesButton = new QPushButton(QStringLiteral("Remove all series")); + connect(removeAllSeriesButton, SIGNAL(clicked(bool)), this, SLOT(removeAllSeries())); + layout->addWidget(removeAllSeriesButton, row++, 1, Qt::AlignLeft); + + return layout; +} + +QGridLayout *MainWidget::createSetsControlsLayout() +{ + QGridLayout *layout = new QGridLayout(); + int row = 0; + + layout->addWidget(new QLabel(QStringLiteral("Sets controls:")), row, 0, Qt::AlignLeft); + + QPushButton *addSetButton = new QPushButton(QStringLiteral("Add a set")); + connect(addSetButton, SIGNAL(clicked(bool)), this, SLOT(addSet())); + layout->addWidget(addSetButton, row++, 1, Qt::AlignLeft); + + QPushButton *insertSetButton = new QPushButton(QStringLiteral("Insert a set")); + connect(insertSetButton, SIGNAL(clicked(bool)), this, SLOT(insertSet())); + layout->addWidget(insertSetButton, row++, 1, Qt::AlignLeft); + + QPushButton *removeSetButton = new QPushButton(QStringLiteral("Remove a set")); + connect(removeSetButton, SIGNAL(clicked(bool)), this, SLOT(removeSet())); + layout->addWidget(removeSetButton, row++, 1, Qt::AlignLeft); + + QPushButton *removeAllSetsButton = new QPushButton(QStringLiteral("Remove all sets")); + connect(removeAllSetsButton, SIGNAL(clicked(bool)), this, SLOT(removeAllSets())); + layout->addWidget(removeAllSetsButton, row++, 1, Qt::AlignLeft); + + return layout; +} + +QGridLayout *MainWidget::createCandlestickControlsLayout() +{ + QGridLayout *layout = new QGridLayout(); + int row = 0; + + layout->addWidget(new QLabel(QStringLiteral("Maximum column width:")), row, 0, Qt::AlignLeft); + QDoubleSpinBox *maximumColumnWidthSpinBox = new QDoubleSpinBox(); + maximumColumnWidthSpinBox->setRange(-1.0, 1024.0); + maximumColumnWidthSpinBox->setDecimals(0); + maximumColumnWidthSpinBox->setValue(m_maximumColumnWidth); + maximumColumnWidthSpinBox->setSingleStep(1.0); + connect(maximumColumnWidthSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(changeMaximumColumnWidth(double))); + layout->addWidget(maximumColumnWidthSpinBox, row++, 1, Qt::AlignLeft); + + layout->addWidget(new QLabel(QStringLiteral("Minimum column width:")), row, 0, Qt::AlignLeft); + QDoubleSpinBox *minimumColumnWidthSpinBox = new QDoubleSpinBox(); + minimumColumnWidthSpinBox->setRange(-1.0, 1024.0); + minimumColumnWidthSpinBox->setDecimals(0); + minimumColumnWidthSpinBox->setValue(m_minimumColumnWidth); + minimumColumnWidthSpinBox->setSingleStep(1.0); + connect(minimumColumnWidthSpinBox, SIGNAL(valueChanged(double)), + this, SLOT(changeMinimumColumnWidth(double))); + layout->addWidget(minimumColumnWidthSpinBox, row++, 1, Qt::AlignLeft); + + QCheckBox *bodyOutlineVisible = new QCheckBox(QStringLiteral("Body outline visible")); + connect(bodyOutlineVisible, SIGNAL(toggled(bool)), this, SLOT(bodyOutlineVisibleToggled(bool))); + bodyOutlineVisible->setChecked(m_bodyOutlineVisible); + layout->addWidget(bodyOutlineVisible, row++, 0, Qt::AlignLeft); + + QCheckBox *capsVisible = new QCheckBox(QStringLiteral("Caps visible")); + connect(capsVisible, SIGNAL(toggled(bool)), this, SLOT(capsVisibleToggled(bool))); + capsVisible->setChecked(m_capsVisible); + layout->addWidget(capsVisible, row++, 0, Qt::AlignLeft); + + layout->addWidget(new QLabel(QStringLiteral("Candlestick body width:")), row, 0, Qt::AlignLeft); + QDoubleSpinBox *bodyWidthSpinBox = new QDoubleSpinBox(); + bodyWidthSpinBox->setRange(-1.0, 2.0); + bodyWidthSpinBox->setValue(m_bodyWidth); + bodyWidthSpinBox->setSingleStep(0.1); + connect(bodyWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeBodyWidth(double))); + layout->addWidget(bodyWidthSpinBox, row++, 1, Qt::AlignLeft); + + layout->addWidget(new QLabel(QStringLiteral("Candlestick caps width:")), row, 0, Qt::AlignLeft); + QDoubleSpinBox *capsWidthSpinBox = new QDoubleSpinBox(); + capsWidthSpinBox->setRange(-1.0, 2.0); + capsWidthSpinBox->setValue(m_capsWidth); + capsWidthSpinBox->setSingleStep(0.1); + connect(capsWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeCapsWidth(double))); + layout->addWidget(capsWidthSpinBox, row++, 1, Qt::AlignLeft); + + QCheckBox *increasingColor = new QCheckBox(QStringLiteral("Custom increasing color (only S1)")); + connect(increasingColor, SIGNAL(toggled(bool)), this, SLOT(customIncreasingColorToggled(bool))); + increasingColor->setChecked(m_customIncreasingColor); + layout->addWidget(increasingColor, row++, 0, 1, 2, Qt::AlignLeft); + + QCheckBox *decreasingColor = new QCheckBox(QStringLiteral("Custom decreasing color (only S1)")); + connect(decreasingColor, SIGNAL(toggled(bool)), this, SLOT(customDecreasingColorToggled(bool))); + decreasingColor->setChecked(m_customDecreasingColor); + layout->addWidget(decreasingColor, row++, 0, 1, 2, Qt::AlignLeft); + + return layout; +} + +QGridLayout *MainWidget::createMiscellaneousControlsLayout() +{ + QGridLayout *layout = new QGridLayout(); + int row = 0; + + layout->addWidget(new QLabel(QStringLiteral("Miscellaneous:")), row, 0, Qt::AlignLeft); + + QCheckBox *antialiasingCheckBox = new QCheckBox(QStringLiteral("Antialiasing")); + connect(antialiasingCheckBox, SIGNAL(toggled(bool)), this, SLOT(antialiasingToggled(bool))); + antialiasingCheckBox->setChecked(false); + layout->addWidget(antialiasingCheckBox, row++, 1, Qt::AlignLeft); + + QCheckBox *animationCheckBox = new QCheckBox(QStringLiteral("Animation")); + connect(animationCheckBox, SIGNAL(toggled(bool)), this, SLOT(animationToggled(bool))); + animationCheckBox->setChecked(false); + layout->addWidget(animationCheckBox, row++, 1, Qt::AlignLeft); + + QCheckBox *legendCheckBox = new QCheckBox(QStringLiteral("Legend")); + connect(legendCheckBox, SIGNAL(toggled(bool)), this, SLOT(legendToggled(bool))); + legendCheckBox->setChecked(true); + layout->addWidget(legendCheckBox, row++, 1, Qt::AlignLeft); + + QCheckBox *titleCheckBox = new QCheckBox(QStringLiteral("Title")); + connect(titleCheckBox, SIGNAL(toggled(bool)), this, SLOT(titleToggled(bool))); + titleCheckBox->setChecked(true); + layout->addWidget(titleCheckBox, row++, 1, Qt::AlignLeft); + + layout->addWidget(new QLabel(QStringLiteral("Chart theme:")), row, 0, Qt::AlignLeft); + QComboBox *chartThemeComboBox = new QComboBox(); + chartThemeComboBox->addItem(QStringLiteral("Light")); + chartThemeComboBox->addItem(QStringLiteral("Blue Cerulean")); + chartThemeComboBox->addItem(QStringLiteral("Dark")); + chartThemeComboBox->addItem(QStringLiteral("Brown Sand")); + chartThemeComboBox->addItem(QStringLiteral("Blue Ncs")); + chartThemeComboBox->addItem(QStringLiteral("High Contrast")); + chartThemeComboBox->addItem(QStringLiteral("Blue Icy")); + chartThemeComboBox->addItem(QStringLiteral("Qt")); + connect(chartThemeComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(changeChartTheme(int))); + layout->addWidget(chartThemeComboBox, row++, 1, Qt::AlignLeft); + + layout->addWidget(new QLabel(QStringLiteral("Axis X:")), row, 0, Qt::AlignLeft); + QComboBox *axisXComboBox = new QComboBox(); + axisXComboBox->addItem(QStringLiteral("BarCategory")); + axisXComboBox->addItem(QStringLiteral("DateTime")); + axisXComboBox->addItem(QStringLiteral("Value")); + connect(axisXComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAxisX(int))); + layout->addWidget(axisXComboBox, row++, 1, Qt::AlignLeft); + + return layout; +} + +QGridLayout *MainWidget::createModelMapperControlsLayout() +{ + QGridLayout *layout = new QGridLayout(); + int row = 0; + + layout->addWidget(new QLabel(QStringLiteral("First series:")), row, 0, Qt::AlignLeft); + + QPushButton *attachModelMapperButton = new QPushButton(QStringLiteral("Attach model mapper")); + connect(attachModelMapperButton, SIGNAL(clicked(bool)), this, SLOT(attachModelMapper())); + layout->addWidget(attachModelMapperButton, row++, 1, Qt::AlignLeft); + + QPushButton *detachModelMappeButton = new QPushButton(QStringLiteral("Detach model mapper")); + connect(detachModelMappeButton, SIGNAL(clicked(bool)), this, SLOT(detachModelMapper())); + layout->addWidget(detachModelMappeButton, row++, 1, Qt::AlignLeft); + + QTableView *tableView = new QTableView(); + tableView->setMinimumSize(320, 480); + tableView->setMaximumSize(320, 480); + tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); + Q_ASSERT_X(m_hModelMapper->model(), Q_FUNC_INFO, "Model is not initialized"); + tableView->setModel(m_hModelMapper->model()); + layout->addWidget(tableView, row++, 0, 1, 2, Qt::AlignLeft); + + layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding), row++, 0); + + return layout; +} + +qreal MainWidget::randomValue(int min, int max) const +{ + return (qrand() / (qreal(RAND_MAX) + 1)) * ((qMax(min, max) - qMin(min, max)) + qMin(min, max)); +} + +QCandlestickSet *MainWidget::randomSet(qreal timestamp) +{ + QCandlestickSet *set = new QCandlestickSet(timestamp); + set->setOpen(randomValue(4, 11)); + set->setHigh(randomValue(12, 15)); + set->setLow(randomValue(0, 3)); + set->setClose(randomValue(4, 11)); + + return set; +} + +void MainWidget::updateAxes() +{ + if (m_chart->axes().isEmpty()) + m_chart->createDefaultAxes(); + + QCandlestickSeries *series; + if (!m_chart->series().isEmpty()) + series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0)); + else + series = nullptr; + + m_axisX = m_chart->axes(Qt::Horizontal).first(); + if (series && !series->candlestickSets().isEmpty()) { + if (m_axisX->type() == QAbstractAxis::AxisTypeBarCategory) { + QBarCategoryAxis *axisX = qobject_cast<QBarCategoryAxis *>(m_axisX); + QStringList categories; + for (int i = 0; i < series->candlestickSets().count(); ++i) + categories.append(QString::number(i)); + axisX->setCategories(categories); + } else { // QAbstractAxis::AxisTypeDateTime || QAbstractAxis::AxisTypeValue + qreal msInMonth = 31.0 * 24.0 * 60.0 * 60.0 * 1000.0; + qreal min = series->candlestickSets().first()->timestamp() - msInMonth; + qreal max = series->candlestickSets().last()->timestamp() + msInMonth; + QDateTime minDateTime = QDateTime::fromMSecsSinceEpoch(min); + QDateTime maxDateTime = QDateTime::fromMSecsSinceEpoch(max); + + if (m_axisX->type() == QAbstractAxis::AxisTypeDateTime) + m_axisX->setRange(minDateTime, maxDateTime); + else + m_axisX->setRange(min, max); + } + } + + m_axisY = m_chart->axes(Qt::Vertical).first(); + m_axisY->setMax(15); + m_axisY->setMin(0); +} + +void MainWidget::addSeries() +{ + if (m_chart->series().count() > 9) { + qDebug() << "Maximum series count is 10"; + return; + } + + QCandlestickSeries *series = new QCandlestickSeries(); + series->setName(QStringLiteral("S%1").arg(m_chart->series().count() + 1)); + series->setMaximumColumnWidth(m_maximumColumnWidth); + series->setMinimumColumnWidth(m_minimumColumnWidth); + series->setBodyOutlineVisible(m_bodyOutlineVisible); + series->setBodyWidth(m_bodyWidth); + series->setCapsVisible(m_capsVisible); + series->setCapsWidth(m_capsWidth); + + if (m_chart->series().isEmpty()) { + if (m_customIncreasingColor) + series->setIncreasingColor(QColor(Qt::green)); + if (m_customDecreasingColor) + series->setDecreasingColor(QColor(Qt::red)); + + for (int month = 1; month <= 12; ++month) { + QDateTime dateTime; + dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), month, 1)); + dateTime.setTime(QTime(12, 34, 56, 789)); + + QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch()); + series->append(set); + } + } else { + QCandlestickSeries *s = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0)); + for (int i = 0; i < s->candlestickSets().count(); ++i) { + QCandlestickSet *set = randomSet(s->candlestickSets().at(i)->timestamp()); + series->append(set); + } + } + + m_chart->addSeries(series); + + updateAxes(); + if (!series->attachedAxes().contains(m_axisX)) + series->attachAxis(m_axisX); + if (!series->attachedAxes().contains(m_axisY)) + series->attachAxis(m_axisY); +} + +void MainWidget::removeSeries() +{ + if (m_chart->series().isEmpty()) { + qDebug() << "Create a series first"; + return; + } + + if (m_chart->series().count() == 1) + detachModelMapper(); + + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().last()); + m_chart->removeSeries(series); + delete series; + series = nullptr; +} + +void MainWidget::removeAllSeries() +{ + if (m_chart->series().isEmpty()) { + qDebug() << "Create a series first"; + return; + } + + detachModelMapper(); + + m_chart->removeAllSeries(); +} + +void MainWidget::addSet() +{ + if (m_chart->series().isEmpty()) { + qDebug() << "Create a series first"; + return; + } + + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + + QDateTime dateTime; + if (series->count()) { + dateTime.setMSecsSinceEpoch(series->candlestickSets().last()->timestamp()); + dateTime = dateTime.addMonths(1); + } else { + dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), 1, 1)); + dateTime.setTime(QTime(12, 34, 56, 789)); + } + + QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch()); + series->append(set); + } + + updateAxes(); +} + +void MainWidget::insertSet() +{ + if (m_chart->series().isEmpty()) { + qDebug() << "Create a series first"; + return; + } + + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + + QDateTime dateTime; + if (series->count()) { + dateTime.setMSecsSinceEpoch(series->candlestickSets().first()->timestamp()); + dateTime = dateTime.addMonths(-1); + } else { + dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), 1, 1)); + dateTime.setTime(QTime(12, 34, 56, 789)); + } + + QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch()); + series->insert(0, set); + } + + updateAxes(); +} + +void MainWidget::removeSet() +{ + if (m_chart->series().isEmpty()) { + qDebug() << "Create a series first"; + return; + } + + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + if (series->candlestickSets().isEmpty()) + qDebug() << "Create a set first"; + else + series->remove(series->candlestickSets().last()); + } + + updateAxes(); +} + +void MainWidget::removeAllSets() +{ + if (m_chart->series().isEmpty()) { + qDebug() << "Create a series first"; + return; + } + + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + if (series->candlestickSets().isEmpty()) + qDebug() << "Create a set first"; + else + series->clear(); + } + + updateAxes(); +} + +void MainWidget::changeMaximumColumnWidth(double width) +{ + m_maximumColumnWidth = width; + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + series->setMaximumColumnWidth(m_maximumColumnWidth); + } +} + +void MainWidget::changeMinimumColumnWidth(double width) +{ + m_minimumColumnWidth = width; + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + series->setMinimumColumnWidth(m_minimumColumnWidth); + } +} + +void MainWidget::bodyOutlineVisibleToggled(bool visible) +{ + m_bodyOutlineVisible = visible; + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + series->setBodyOutlineVisible(m_bodyOutlineVisible); + } +} + +void MainWidget::capsVisibleToggled(bool visible) +{ + m_capsVisible = visible; + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + series->setCapsVisible(m_capsVisible); + } +} + +void MainWidget::changeBodyWidth(double width) +{ + m_bodyWidth = width; + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + series->setBodyWidth(m_bodyWidth); + } +} + +void MainWidget::changeCapsWidth(double width) +{ + m_capsWidth = width; + foreach (QAbstractSeries *s, m_chart->series()) { + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s); + series->setCapsWidth(m_capsWidth); + } +} + +void MainWidget::customIncreasingColorToggled(bool custom) +{ + m_customIncreasingColor = custom; + + if (m_chart->series().isEmpty()) + return; + + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0)); + if (series) { + QColor color = m_customIncreasingColor ? QColor(Qt::green) : QColor(); + series->setIncreasingColor(color); + } +} + +void MainWidget::customDecreasingColorToggled(bool custom) +{ + m_customDecreasingColor = custom; + + if (m_chart->series().isEmpty()) + return; + + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0)); + if (series) { + QColor color = m_customDecreasingColor ? QColor(Qt::red) : QColor(); + series->setDecreasingColor(color); + } +} + +void MainWidget::antialiasingToggled(bool enabled) +{ + m_chartView->setRenderHint(QPainter::Antialiasing, enabled); +} + +void MainWidget::animationToggled(bool enabled) +{ + if (enabled) + m_chart->setAnimationOptions(QChart::SeriesAnimations); + else + m_chart->setAnimationOptions(QChart::NoAnimation); +} + +void MainWidget::legendToggled(bool visible) +{ + m_chart->legend()->setVisible(visible); + if (visible) + m_chart->legend()->setAlignment(Qt::AlignBottom); +} + +void MainWidget::titleToggled(bool visible) +{ + if (visible) + m_chart->setTitle(QStringLiteral("Candlestick Chart")); + else + m_chart->setTitle(QString()); +} + +void MainWidget::changeChartTheme(int themeIndex) +{ + if (themeIndex < QChart::ChartThemeLight || themeIndex > QChart::ChartThemeQt) { + qDebug() << "Invalid chart theme index:" << themeIndex; + return; + } + + m_chart->setTheme((QChart::ChartTheme)(themeIndex)); +} + +void MainWidget::changeAxisX(int axisXIndex) +{ + if (m_axisX) { + m_chart->removeAxis(m_axisX); + delete m_axisX; + } + + switch (axisXIndex) { + case 0: + m_axisX = new QBarCategoryAxis(); + break; + case 1: + m_axisX = new QDateTimeAxis(); + break; + case 2: + m_axisX = new QValueAxis(); + break; + default: + qDebug() << "Invalid axis x index:" << axisXIndex; + return; + } + + m_chart->addAxis(m_axisX, Qt::AlignBottom); + + updateAxes(); + + foreach (QAbstractSeries *series, m_chart->series()) + series->attachAxis(m_axisX); +} + +void MainWidget::attachModelMapper() +{ + if (m_hModelMapper->series()) { + qDebug() << "Model mapper is already attached"; + return; + } + + if (m_chart->series().isEmpty()) + addSeries(); + + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0)); + Q_ASSERT(series); + series->setName(QStringLiteral("SWMM")); // Series With Model Mapper + + CustomTableModel *model = qobject_cast<CustomTableModel *>(m_hModelMapper->model()); + foreach (QCandlestickSet *set, series->candlestickSets()) + model->addRow(set); + + m_hModelMapper->setFirstCandlestickSetRow(0); + m_hModelMapper->setLastCandlestickSetRow(model->rowCount() - 1); + m_hModelMapper->setSeries(series); +} + +void MainWidget::detachModelMapper() +{ + if (!m_hModelMapper->series()) + return; + + QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_hModelMapper->series()); + Q_ASSERT(series); + series->setName(QStringLiteral("S1")); + + m_hModelMapper->setSeries(nullptr); + m_hModelMapper->setFirstCandlestickSetRow(-1); + m_hModelMapper->setLastCandlestickSetRow(-1); + + CustomTableModel *model = qobject_cast<CustomTableModel *>(m_hModelMapper->model()); + model->clearRows(); +} diff --git a/tests/manual/candlesticktester/mainwidget.h b/tests/manual/candlesticktester/mainwidget.h new file mode 100644 index 00000000..4044515a --- /dev/null +++ b/tests/manual/candlesticktester/mainwidget.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef MAINWIDGET_H +#define MAINWIDGET_H + +#include <QtCharts/QChartView> +#include <QtWidgets/QWidget> + +QT_BEGIN_NAMESPACE +class QGridLayout; +QT_END_NAMESPACE + +QT_CHARTS_BEGIN_NAMESPACE +class QCandlestickSet; +class QHCandlestickModelMapper; +QT_CHARTS_END_NAMESPACE + +QT_CHARTS_USE_NAMESPACE + +class MainWidget : public QWidget +{ + Q_OBJECT + +public: + explicit MainWidget(QWidget *parent = nullptr); + ~MainWidget(); + +private: + QGridLayout *createSeriesControlsLayout(); + QGridLayout *createSetsControlsLayout(); + QGridLayout *createCandlestickControlsLayout(); + QGridLayout *createMiscellaneousControlsLayout(); + QGridLayout *createModelMapperControlsLayout(); + + qreal randomValue(int min, int max) const; + QCandlestickSet *randomSet(qreal timestamp); + + void updateAxes(); + +private slots: + void addSeries(); + void removeSeries(); + void removeAllSeries(); + void addSet(); + void insertSet(); + void removeSet(); + void removeAllSets(); + void changeMaximumColumnWidth(double width); + void changeMinimumColumnWidth(double width); + void bodyOutlineVisibleToggled(bool visible); + void capsVisibleToggled(bool visible); + void changeBodyWidth(double width); + void changeCapsWidth(double width); + void customIncreasingColorToggled(bool custom); + void customDecreasingColorToggled(bool custom); + void antialiasingToggled(bool enabled); + void animationToggled(bool enabled); + void legendToggled(bool visible); + void titleToggled(bool visible); + void changeChartTheme(int themeIndex); + void changeAxisX(int axisXIndex); + void attachModelMapper(); + void detachModelMapper(); + +private: + QChart *m_chart; + QChartView *m_chartView; + QAbstractAxis *m_axisX; + QAbstractAxis *m_axisY; + qreal m_maximumColumnWidth; + qreal m_minimumColumnWidth; + bool m_bodyOutlineVisible; + bool m_capsVisible; + qreal m_bodyWidth; + qreal m_capsWidth; + bool m_customIncreasingColor; + bool m_customDecreasingColor; + QHCandlestickModelMapper *m_hModelMapper; +}; + +#endif // MAINWIDGET_H diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro index 25545aeb..0e02e8c2 100644 --- a/tests/manual/manual.pro +++ b/tests/manual/manual.pro @@ -2,7 +2,8 @@ TEMPLATE = subdirs SUBDIRS += \ presenterchart \ polarcharttest \ - boxplottester + boxplottester \ + candlesticktester contains(QT_CONFIG, opengl) { SUBDIRS += chartwidgettest \ |