Add stock chart example for canvas item
authorCharles Yin <charles.yin@nokia.com>
Tue, 12 Jul 2011 06:49:15 +0000 (16:49 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 12 Jul 2011 06:50:14 +0000 (08:50 +0200)
Change-Id: I72aacfcb72b90d4d18f95cef3038a169c991a336
Reviewed-on: http://codereview.qt.nokia.com/1480
Reviewed-by: Charles Yin <charles.yin@nokia.com>
examples/declarative/canvas/stockchart/main.cpp [new file with mode: 0644]
examples/declarative/canvas/stockchart/model.cpp [new file with mode: 0644]
examples/declarative/canvas/stockchart/model.h [new file with mode: 0644]
examples/declarative/canvas/stockchart/stock.qml [new file with mode: 0644]
examples/declarative/canvas/stockchart/stockchart.pro [new file with mode: 0644]
examples/declarative/canvas/stockchart/stockchart.qrc [new file with mode: 0644]

diff --git a/examples/declarative/canvas/stockchart/main.cpp b/examples/declarative/canvas/stockchart/main.cpp
new file mode 100644 (file)
index 0000000..fd38318
--- /dev/null
@@ -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 <QtGui/QApplication>
+#include <qdeclarative.h>
+#include <QSGView>
+#include "model.h"
+
+int main(int argc, char ** argv)
+{
+    QApplication app(argc, argv);
+
+    qmlRegisterType<StockModel>("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 (file)
index 0000000..e518779
--- /dev/null
@@ -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 <QtCore/QUrl>
+#include <QtCore/QDate>
+#include <QtCore/QList>
+#include <QtCore/QStringList>
+#include <QtCore/QDebug>
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkReply>
+
+StockModel::StockModel(QObject *parent)
+    : QAbstractListModel(parent)
+    , _startDate(QDate(1995, 4, 25))
+    , _endDate(QDate::currentDate())
+    , _dataCycle(StockModel::Daily)
+    , _manager(0)
+    , _updating(false)
+{
+    QHash<int, QByteArray> 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 (file)
index 0000000..fde99de
--- /dev/null
@@ -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 <QtCore/QAbstractListModel>
+#include <QtCore/QDate>
+
+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<StockPrice> _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 (file)
index 0000000..15f6064
--- /dev/null
@@ -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 (file)
index 0000000..84f2a28
--- /dev/null
@@ -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 (file)
index 0000000..a67f5d6
--- /dev/null
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+    <file>stock.qml</file>
+</qresource>
+</RCC>
+