From 082c2938489cb7aad32b0552517ee68e82bcde30 Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Tue, 12 Jul 2011 16:49:15 +1000 Subject: [PATCH] Add stock chart example for canvas item Change-Id: I72aacfcb72b90d4d18f95cef3038a169c991a336 Reviewed-on: http://codereview.qt.nokia.com/1480 Reviewed-by: Charles Yin --- examples/declarative/canvas/stockchart/main.cpp | 61 +++++ examples/declarative/canvas/stockchart/model.cpp | 239 ++++++++++++++++++++ examples/declarative/canvas/stockchart/model.h | 127 +++++++++++ examples/declarative/canvas/stockchart/stock.qml | 113 +++++++++ .../declarative/canvas/stockchart/stockchart.pro | 14 ++ .../declarative/canvas/stockchart/stockchart.qrc | 6 + 6 files changed, 560 insertions(+), 0 deletions(-) create mode 100644 examples/declarative/canvas/stockchart/main.cpp create mode 100644 examples/declarative/canvas/stockchart/model.cpp create mode 100644 examples/declarative/canvas/stockchart/model.h create mode 100644 examples/declarative/canvas/stockchart/stock.qml create mode 100644 examples/declarative/canvas/stockchart/stockchart.pro create mode 100644 examples/declarative/canvas/stockchart/stockchart.qrc diff --git a/examples/declarative/canvas/stockchart/main.cpp b/examples/declarative/canvas/stockchart/main.cpp new file mode 100644 index 0000000..fd38318 --- /dev/null +++ b/examples/declarative/canvas/stockchart/main.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include "model.h" + +int main(int argc, char ** argv) +{ + QApplication app(argc, argv); + + qmlRegisterType("StockChart", 1, 0, "StockModel"); + + QSGView view; + view.setResizeMode(QSGView::SizeRootObjectToView); + view.setSource(QUrl("qrc:stock.qml")); + + view.show(); + view.raise(); + + return app.exec(); +} + diff --git a/examples/declarative/canvas/stockchart/model.cpp b/examples/declarative/canvas/stockchart/model.cpp new file mode 100644 index 0000000..e518779 --- /dev/null +++ b/examples/declarative/canvas/stockchart/model.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "model.h" + +#include +#include +#include +#include +#include + +#include +#include + +StockModel::StockModel(QObject *parent) + : QAbstractListModel(parent) + , _startDate(QDate(1995, 4, 25)) + , _endDate(QDate::currentDate()) + , _dataCycle(StockModel::Daily) + , _manager(0) + , _updating(false) +{ + QHash roles; + roles[StockModel::DateRole] = "date"; + roles[StockModel::SectionRole] = "year"; + roles[StockModel::OpenPriceRole] = "openPrice"; + roles[StockModel::ClosePriceRole] = "closePrice"; + roles[StockModel::HighPriceRole] = "highPrice"; + roles[StockModel::LowPriceRole] = "lowPrice"; + roles[StockModel::VolumeRole] = "volume"; + roles[StockModel::AdjustedPriceRole] = "adjustedPrice"; + setRoleNames(roles); + + connect(this, SIGNAL(stockNameChanged()), SLOT(requestData())); + connect(this, SIGNAL(startDateChanged()), SLOT(requestData())); + connect(this, SIGNAL(endDateChanged()), SLOT(requestData())); + connect(this, SIGNAL(dataCycleChanged()), SLOT(requestData())); + + _manager = new QNetworkAccessManager(this); + connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(update(QNetworkReply*))); + +} + +int StockModel::rowCount(const QModelIndex & parent) const { + Q_UNUSED(parent); + return _prices.count(); +} + +void StockModel::requestData() +{ + if (!_updating) { + _updating = true; + QMetaObject::invokeMethod(this, "doRequest", Qt::QueuedConnection); + } +} + +void StockModel::doRequest() +{ + /* + Fetch stock data from yahoo finance: + url: http://ichart.finance.yahoo.com/table.csv?s=NOK&a=5&b=11&c=2010&d=7&e=23&f=2010&g=d&ignore=.csv + s:stock name/id, a:start day, b:start month, c:start year default: 25 April 1995, oldest c= 1962 + d:end day, e:end month, f:end year, default:today (data only available 3 days before today) + g:data cycle(d daily, w weekly, m monthly, v Dividend) + */ + if (_manager && !_stockName.isEmpty() && _endDate > _startDate) { + QString query("http://ichart.finance.yahoo.com/table.csv?s=%1&a=%2&b=%3&c=%4&d=%5&e=%6&f=%7&g=%8&ignore=.csv"); + query = query.arg(_stockName) + .arg(_startDate.day()).arg(_startDate.month()).arg(_startDate.year()) + .arg(_endDate.day()).arg(_endDate.month()).arg(_endDate.year()) + .arg(dataCycleString()); + + qDebug() << "request stock data:" << query; + _manager->get(QNetworkRequest(QUrl(query))); + } +} + +void StockModel::update(QNetworkReply *reply) +{ + _updating = false; + + if (reply) { + if (reply->error() == QNetworkReply::NoError) { + beginResetModel(); + _prices.clear(); + + while (!reply->atEnd()) { + QString line = reply->readLine(); + QStringList fields = line.split(','); + + //data format:Date,Open,High,Low,Close,Volume,Adjusted close price + //example: 2011-06-24,6.03,6.04,5.88,5.88,20465200,5.88 + if (fields.size() == 7) { + StockPrice price; + price.date = QDate::fromString(fields[0], Qt::ISODate); + price.openPrice = fields[1].toFloat(); + price.highPrice = fields[2].toFloat(); + price.lowPrice = fields[3].toFloat(); + price.closePrice = fields[4].toFloat(); + price.volume = fields[5].toInt(); + price.adjustedPrice = fields[6].toFloat(); + _prices.prepend(price); + } + } + qDebug() << "get stock data successfully, total:" << _prices.count() << "records."; + } else { + qDebug() << "get stock data failed:" << reply->errorString(); + } + reply->deleteLater(); + endResetModel(); + } +} + +QVariant StockModel::data(const QModelIndex & index, int role) const { + if (index.row() < 0 || index.row() > _prices.count()) + return QVariant(); + + const StockPrice &price = _prices[index.row()]; + if (role == StockModel::DateRole) + return price.date; + else if (role == StockModel::OpenPriceRole) + return price.openPrice; + else if (role == StockModel::ClosePriceRole) + return price.closePrice; + else if (role == StockModel::HighPriceRole) + return price.highPrice; + else if (role == StockModel::LowPriceRole) + return price.lowPrice; + else if (role == StockModel::AdjustedPriceRole) + return price.adjustedPrice; + else if (role == StockModel::VolumeRole) + return price.volume; + else if (role == StockModel::SectionRole) + return price.date.year(); + return QVariant(); +} + +QString StockModel::stockName() const +{ + return _stockName; +} +void StockModel::setStockName(const QString& name) +{ + if (_stockName != name) { + _stockName = name; + emit stockNameChanged(); + } +} + +QDate StockModel::startDate() const +{ + return _startDate; +} +void StockModel::setStartDate(const QDate& date) +{ + if (_startDate.isValid() && _startDate != date) { + _startDate = date; + emit startDateChanged(); + } +} + +QDate StockModel::endDate() const +{ + return _endDate; +} +void StockModel::setEndDate(const QDate& date) +{ + if (_endDate.isValid() && _endDate != date) { + _endDate = date; + emit endDateChanged(); + } +} + +StockModel::StockDataCycle StockModel::dataCycle() const +{ + return _dataCycle; +} + +QString StockModel::dataCycleString() const +{ + switch (_dataCycle) { + case StockModel::Daily: + return QString('d'); + break; + case StockModel::Weekly: + return QString('w'); + case StockModel::Monthly: + return QString('m'); + case StockModel::Dividend: + return QString('v'); + } + + return QString('d'); +} + + +void StockModel::setDataCycle(StockModel::StockDataCycle cycle) +{ + if (_dataCycle != cycle) { + _dataCycle = cycle; + emit dataCycleChanged(); + } +} diff --git a/examples/declarative/canvas/stockchart/model.h b/examples/declarative/canvas/stockchart/model.h new file mode 100644 index 0000000..fde99de --- /dev/null +++ b/examples/declarative/canvas/stockchart/model.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +struct StockPrice +{ + QDate date; + qreal openPrice; + qreal closePrice; + qreal highPrice; + qreal lowPrice; + qint32 volume; + qreal adjustedPrice; +}; +class QNetworkReply; +class QNetworkAccessManager; +class StockModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QString stockName READ stockName WRITE setStockName NOTIFY stockNameChanged) + Q_PROPERTY(QDate startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) + Q_PROPERTY(QDate endDate READ endDate WRITE setEndDate NOTIFY endDateChanged) + Q_PROPERTY(StockDataCycle dataCycle READ dataCycle WRITE setDataCycle NOTIFY dataCycleChanged) + + Q_ENUMS(StockDataCycle) +public: + enum StockDataCycle { + Daily, + Weekly, + Monthly, + Dividend + }; + + enum StockModelRoles { + DateRole = Qt::UserRole + 1, + SectionRole, + OpenPriceRole, + ClosePriceRole, + HighPriceRole, + LowPriceRole, + VolumeRole, + AdjustedPriceRole + }; + + StockModel(QObject *parent = 0); + + QString stockName() const; + void setStockName(const QString& name); + + QDate startDate() const; + void setStartDate(const QDate& date); + + QDate endDate() const; + void setEndDate(const QDate& date); + + StockDataCycle dataCycle() const; + void setDataCycle(StockDataCycle cycle); + + int rowCount(const QModelIndex & parent = QModelIndex()) const; + + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + +signals: + void stockNameChanged(); + void startDateChanged(); + void endDateChanged(); + void dataCycleChanged(); + +public slots: + void requestData(); + +private slots: + void doRequest(); + void update(QNetworkReply* reply); +private: + QString dataCycleString() const; + QList _prices; + QString _stockName; + QDate _startDate; + QDate _endDate; + StockDataCycle _dataCycle; + QNetworkAccessManager* _manager; + bool _updating; +}; + + + + diff --git a/examples/declarative/canvas/stockchart/stock.qml b/examples/declarative/canvas/stockchart/stock.qml new file mode 100644 index 0000000..15f6064 --- /dev/null +++ b/examples/declarative/canvas/stockchart/stock.qml @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 +import StockChart 1.0 + +Rectangle { + id:container + width: 700; height: 800 + StockModel { + id:stockModel + stockName: "NOK" + } + // Define a highlight with customised movement between items. + Component { + id: highlightBar + Rectangle { + width: 1000; height: 50 + color: "#FFFF88" + y: view.currentItem.y; + Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } } + } + } + // The delegate for each section header + Component { + id: sectionHeading + Rectangle { + width: container.width + height: childrenRect.height + color: "lightsteelblue" + + Text { + text:stockModel.stockName + "(" + section + ")" + "Open\t High\t Low\t Close\t Volume\t Adjusted" + horizontalAlignment:Text.AlignHCenter + font.bold: true + font.underline:true + } + } + } + ListView { + id:view + width: parent.width + anchors.fill: parent + highlightFollowsCurrentItem: false + focus: true + keyNavigationWraps: true + opacity: 0.8 + highlightRangeMode: ListView.StrictlyEnforceRange + spacing:1 + model: stockModel + highlight: Rectangle { color: "lightsteelblue" } + section.property: "year" + section.criteria: ViewSection.FullString + section.delegate: sectionHeading + delegate: Text { text: Qt.formatDate(date, "yyyy-MM-dd") + "\t " + Math.round(openPrice*100)/100 + "\t " + Math.round(highPrice*100)/100 + "\t " + Math.round(lowPrice*100)/100 + "\t " + Math.round(closePrice*100)/100 + "\t " + volume + "\t " + Math.round(adjustedPrice*100)/100 } + + + property bool beginning:false + + Component.onCompleted: { + view.positionViewAtBeginning(); + } + + MouseArea { + anchors.fill: parent + onDoubleClicked: { + if (view.beginning) { + view.positionViewAtBeginning(); + } else { + view.positionViewAtEnd(); + } + view.beginning = !view.beginning; + } + } + } +} + diff --git a/examples/declarative/canvas/stockchart/stockchart.pro b/examples/declarative/canvas/stockchart/stockchart.pro new file mode 100644 index 0000000..84f2a28 --- /dev/null +++ b/examples/declarative/canvas/stockchart/stockchart.pro @@ -0,0 +1,14 @@ +TEMPLATE = app + +QT += declarative + +RESOURCES += stockchart.qrc + +HEADERS = model.h +SOURCES = main.cpp model.cpp + +macx: CONFIG -= app_bundle +CONFIG += console + +OTHER_FILES += \ + stock.qml diff --git a/examples/declarative/canvas/stockchart/stockchart.qrc b/examples/declarative/canvas/stockchart/stockchart.qrc new file mode 100644 index 0000000..a67f5d6 --- /dev/null +++ b/examples/declarative/canvas/stockchart/stockchart.qrc @@ -0,0 +1,6 @@ + + + stock.qml + + + -- 1.7.2.5