From 288e11388d2c5f393c177b4ecb2145919cbb511a Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Fri, 13 Jul 2012 16:28:44 +1000 Subject: [PATCH] Rewrite stockchart demo Change-Id: Ie931d116f3ac16245d56b8c1539e5c4f2dc490f0 Reviewed-by: Yunqiao Yin --- examples/demos/stockchart/README | 5 - .../stockchart/com/nokia/StockChartExample/qmldir | 1 - .../{contents/ToolBar.qml => content/Button.qml} | 40 +- .../ScrollBar.qml => content/DatePicker.qml} | 102 ++- examples/demos/stockchart/content/StockChart.qml | 324 +++++++++ .../Stocks.qml => content/StockListModel.qml} | 0 .../TitleBar.qml => content/StockListView.qml} | 61 +- examples/demos/stockchart/content/StockModel.qml | 160 +++++ .../demos/stockchart/content/StockSettings.qml | 317 +++++++++ examples/demos/stockchart/content/StockView.qml | 129 ++++ .../content/images/icon-calendar-anim.png | Bin 0 -> 1088 bytes .../stockchart/content/images/icon-calendar.png | Bin 0 -> 884 bytes .../demos/stockchart/content/images/icon-items.png | Bin 0 -> 887 bytes .../stockchart/content/images/icon-settings.png | Bin 0 -> 1066 bytes examples/demos/stockchart/content/images/logo.png | Bin 0 -> 4266 bytes .../stockchart/content/images/stock-selected.png | Bin 0 -> 6480 bytes .../stockchart/content/images/wheel-touch.png | Bin 0 -> 4767 bytes examples/demos/stockchart/content/images/wheel.png | Bin 0 -> 36223 bytes examples/demos/stockchart/contents/images/quit.png | Bin 2369 -> 0 bytes .../demos/stockchart/contents/images/stripes.png | Bin 257 -> 0 bytes .../stockchart/{contents/Button.qml => main.qml} | 92 +-- examples/demos/stockchart/model.cpp | 255 -------- examples/demos/stockchart/model.h | 166 ----- examples/demos/stockchart/plugin.cpp | 60 -- examples/demos/stockchart/stock.qml | 726 --------------------- examples/demos/stockchart/stockchart.pro | 20 - examples/demos/stockchart/stockchart.qmlproject | 16 - 27 files changed, 1112 insertions(+), 1362 deletions(-) delete mode 100644 examples/demos/stockchart/README delete mode 100644 examples/demos/stockchart/com/nokia/StockChartExample/qmldir rename examples/demos/stockchart/{contents/ToolBar.qml => content/Button.qml} (79%) rename examples/demos/stockchart/{contents/ScrollBar.qml => content/DatePicker.qml} (51%) create mode 100644 examples/demos/stockchart/content/StockChart.qml rename examples/demos/stockchart/{contents/Stocks.qml => content/StockListModel.qml} (100%) rename examples/demos/stockchart/{contents/TitleBar.qml => content/StockListView.qml} (65%) create mode 100644 examples/demos/stockchart/content/StockModel.qml create mode 100644 examples/demos/stockchart/content/StockSettings.qml create mode 100644 examples/demos/stockchart/content/StockView.qml create mode 100644 examples/demos/stockchart/content/images/icon-calendar-anim.png create mode 100644 examples/demos/stockchart/content/images/icon-calendar.png create mode 100644 examples/demos/stockchart/content/images/icon-items.png create mode 100644 examples/demos/stockchart/content/images/icon-settings.png create mode 100644 examples/demos/stockchart/content/images/logo.png create mode 100644 examples/demos/stockchart/content/images/stock-selected.png create mode 100644 examples/demos/stockchart/content/images/wheel-touch.png create mode 100644 examples/demos/stockchart/content/images/wheel.png delete mode 100755 examples/demos/stockchart/contents/images/quit.png delete mode 100755 examples/demos/stockchart/contents/images/stripes.png rename examples/demos/stockchart/{contents/Button.qml => main.qml} (60%) delete mode 100644 examples/demos/stockchart/model.cpp delete mode 100644 examples/demos/stockchart/model.h delete mode 100644 examples/demos/stockchart/plugin.cpp delete mode 100644 examples/demos/stockchart/stock.qml delete mode 100644 examples/demos/stockchart/stockchart.pro delete mode 100644 examples/demos/stockchart/stockchart.qmlproject diff --git a/examples/demos/stockchart/README b/examples/demos/stockchart/README deleted file mode 100644 index 2652866..0000000 --- a/examples/demos/stockchart/README +++ /dev/null @@ -1,5 +0,0 @@ -To run: - - make install - QML_IMPORT_PATH=$PWD qmlscene stock.qml - diff --git a/examples/demos/stockchart/com/nokia/StockChartExample/qmldir b/examples/demos/stockchart/com/nokia/StockChartExample/qmldir deleted file mode 100644 index 4c60e55..0000000 --- a/examples/demos/stockchart/com/nokia/StockChartExample/qmldir +++ /dev/null @@ -1 +0,0 @@ -plugin qmlstockchartexampleplugin diff --git a/examples/demos/stockchart/contents/ToolBar.qml b/examples/demos/stockchart/content/Button.qml similarity index 79% rename from examples/demos/stockchart/contents/ToolBar.qml rename to examples/demos/stockchart/content/Button.qml index 7ae7391..325bd8f 100644 --- a/examples/demos/stockchart/contents/ToolBar.qml +++ b/examples/demos/stockchart/content/Button.qml @@ -40,30 +40,20 @@ import QtQuick 2.0 -Item { - id: toolbar - - property variant labels - signal buttonClicked(int index) - - BorderImage { - source: "images/titlebar.sci" - width: parent.width - height: parent.height + 14 - y: -7 +Rectangle { + id:button + property bool buttonEnabled:true + radius:5 + width:25 + height:25 + color:buttonEnabled ? "steelblue" : "gray" + MouseArea { + anchors.fill:parent + onClicked: { + if (buttonEnabled) + buttonEnabled = false; + else + buttonEnabled = true; } - - Row { - y: 3 - anchors.horizontalCenter: parent.horizontalCenter - Repeater { - model: toolbar.labels - delegate: - Button { - text: modelData - onClicked: toolbar.buttonClicked(model.index) - } - } - } - + } } diff --git a/examples/demos/stockchart/contents/ScrollBar.qml b/examples/demos/stockchart/content/DatePicker.qml similarity index 51% rename from examples/demos/stockchart/contents/ScrollBar.qml rename to examples/demos/stockchart/content/DatePicker.qml index 98b8efe..a85d4f2 100644 --- a/examples/demos/stockchart/contents/ScrollBar.qml +++ b/examples/demos/stockchart/content/DatePicker.qml @@ -3,7 +3,7 @@ ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** -** This file is part of the Qt Mobility Components. +** 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: @@ -40,35 +40,83 @@ import QtQuick 2.0 -Item { - id: scrollBar - // The properties that define the scrollbar's state. - // position and pageSize are in the range 0.0 - 1.0. They are relative to the - // height of the page, i.e. a pageSize of 0.5 means that you can see 50% - // of the height of the view. - // orientation can be either 'Vertical' or 'Horizontal' - property real position - property real pageSize - property string orientation : "Vertical" - property alias bgColor: background.color - property alias fgColor: thumb.color +Rectangle { + id:root + color:"transparent" + width:300 + height:45 + property var _monthNames : [ "JAN", "FEB", "MAR", "APR", "MAY", "JUN","JUL", "AUG", "SEP", "OCT", "NOV", "DEC" ]; + property var date:new Date() + + onDateChanged: { + month.text = root._monthNames[root.date.getMonth()] + day.text = date.getDate(); + year.text = date.getFullYear(); + } + Row { + spacing:20 + anchors.fill:parent + + Rectangle { + height:root.height + width:root.width/3 - 20 + color:"#272822" + radius:5 + + TextInput { + id:month + anchors.centerIn:parent + color:"#ecc089" + font.pointSize:25 + font.bold:true + text:root._monthNames[root.date.getMonth()] + onAccepted : { + for (var i = 0; i < 12; i++) { + if (text === root._monthNames[i]) { + root.date.setMonth(i); + root.date = root.date; + return; + } + } + root.date = root.date; + } + } + } - // A light, semi-transparent background Rectangle { - id: background - radius: orientation == 'Vertical' ? (width/2 - 1) : (height/2 - 1) - color: "white"; opacity: 0.3 - anchors.fill: parent + height:root.height + width:root.width/3 - 20 + color:"#272822" + radius:5 + + TextInput { + id:day + anchors.centerIn:parent + color:"#ecc089" + font.pointSize:25 + font.bold:true + text:root.date.getDate() + validator:IntValidator {bottom:1; top:31} + onAccepted: { root.date.setDate(text); root.date = root.date;} + } } - // Size the bar to the required size, depending upon the orientation. + Rectangle { - id: thumb - opacity: 0.7 - color: "black" - radius: orientation == 'Vertical' ? (width/2 - 1) : (height/2 - 1) - x: orientation == 'Vertical' ? 1 : (scrollBar.position * (scrollBar.width-2) + 1) - y: orientation == 'Vertical' ? (scrollBar.position * (scrollBar.height-2) + 1) : 1 - width: orientation == 'Vertical' ? (parent.width-2) : (scrollBar.pageSize * (scrollBar.width-2)) - height: orientation == 'Vertical' ? (scrollBar.pageSize * (scrollBar.height-2)) : (parent.height-2) + height:root.height + width:root.width/3 - 20 + color:"#272822" + radius:5 + + TextInput { + id:year + anchors.centerIn:parent + color:"#ecc089" + font.pointSize:25 + font.bold:true + text:root.date.getFullYear() + validator:IntValidator {bottom:1995; top:(new Date()).getFullYear()} + onAccepted:{ root.date.setFullYear(text); root.date = root.date;} + } } + } } diff --git a/examples/demos/stockchart/content/StockChart.qml b/examples/demos/stockchart/content/StockChart.qml new file mode 100644 index 0000000..9dc1ea3 --- /dev/null +++ b/examples/demos/stockchart/content/StockChart.qml @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +Rectangle { + id:chart + width:320 + height:320 + color:"transparent" + property var _months : [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] + property var stockModel:null + property var startDate + property var endDate + property var settings + + function update() { + if (settings.chartType === "year") + chart.startDate = new Date(chart.endDate.getFullYear() - 1, chart.endDate.getMonth(), chart.endDate.getDate()); + else if (settings.chartType === "month") + chart.startDate = new Date(chart.endDate.getFullYear() , chart.endDate.getMonth() -1, chart.endDate.getDate()) + else if (settings.chartType === "week") + chart.startDate = new Date(chart.endDate.getFullYear() , chart.endDate.getMonth(), chart.endDate.getDate() - 7) + else + chart.startDate = new Date(1995, 3, 25) + + canvas.requestPaint(); + } + + + Text { + id:fromDate + color:"#6a5b44" + width:50 + font.pointSize: 10 + wrapMode: Text.WordWrap + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.top:parent.top + text:_months[startDate.getMonth()] + "\n" + startDate.getFullYear() + } + + Text { + id:toDate + color:"#6a5b44" + font.pointSize: 10 + width:50 + wrapMode: Text.WordWrap + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.top:parent.top + text:_months[endDate.getMonth()] + "\n" + endDate.getFullYear() + } + + Canvas { + id:canvas + anchors.top : toDate.bottom + width:parent.width + anchors.bottom: parent.bottom + + renderTarget: Canvas.FramebufferObject + property int frames:first + property int mouseX:0; + property int mouseY:0; + property int mousePressedX:0; + property int mousePressedY:0; + property int movedY:0 + property real scaleX:1.0; + property real scaleY:1.0; + property int first:0; + property int last:0; + +// MouseArea { +// anchors.fill: parent + +// onPositionChanged: { +// if (mouse.modifiers & Qt.ControlModifier) { +// if (canvas.mouseX == 0 && canvas.mouseY == 0) { +// canvas.mouseX = mouse.x; +// canvas.mouseY = mouse.y; +// } +// } else{ +// var w = (view.width/view.count)*canvas.scaleX; + +// //canvas.movedY += Math.round((canvas.mousePressedY - mouse.y)/2); + +// var movedX = Math.round((canvas.mousePressedX - mouse.x)/w); +// if (movedX != 0 || canvas.movedY != 0) { +// if (canvas.first + movedX >= 0 && canvas.last + movedX < view.count) { +// canvas.first += movedX; +// canvas.last += movedX; +// } +// canvas.requestPaint(); +// } +// } +// } + +// onPressed: { +// canvas.mousePressedX = mouse.x; +// canvas.mousePressedY = mouse.y; +// } + +// onReleased : { +// if (mouse.modifiers & Qt.ControlModifier) { +// var sx = mouse.x - canvas.mouseX; +// var sy = canvas.mouseY - mouse.y; + +// if (Math.abs(sx) < 50) sx = 0; +// if (Math.abs(sy) < 50) sy = 0; + +// if (sx > 0) +// canvas.scaleX *= sx/100 +1; +// else +// canvas.scaleX *= 1/(-sx/100 + 1); + +// if (sy > 0) +// canvas.scaleY *= sy/100 +1; +// else +// canvas.scaleY *= 1/(-sy/100 + 1); + +// if (canvas.scaleX < 1) +// canvas.scaleX = 1; + +// //console.log("scaleX:" + canvas.scaleX + ", scaleY:" + canvas.scaleY); + +// canvas.first = Math.round(view.currentIndex - view.currentIndex / canvas.scaleX); +// canvas.last = Math.round(view.currentIndex + (view.count - view.currentIndex) / canvas.scaleX); + +// canvas.mouseX = 0; +// canvas.mouseY = 0; +// canvas.mousePressedX = 0; +// canvas.mousePressedY = 0; +// canvas.requestPaint(); +// } +// } +// } + + function drawBackground(ctx) { + ctx.save(); + ctx.fillStyle = "#272822" + ctx.fillRect(0, 0, canvas.width, canvas.height) + ctx.strokeStyle = "#423a2f" + ctx.beginPath(); + for (var i = 0; i < 10; i++) { + ctx.moveTo(0, i * (canvas.height/10.0)); + ctx.lineTo(canvas.width, i * (canvas.height/10.0)); + } + + for (i = 0; i < 12; i++) { + ctx.moveTo(i * (canvas.width/12.0), 0); + ctx.lineTo(i * (canvas.width/12.0), canvas.height); + } + ctx.stroke(); + + ctx.strokeStyle = "#5c7a37" + ctx.beginPath(); + ctx.moveTo(8 * (canvas.width/12.0), 0); + ctx.lineTo(8 * (canvas.width/12.0), canvas.height); + ctx.stroke(); + + ctx.restore(); + } + + function drawPrice(ctx, from, to, color, price, points, highest) + { + ctx.save(); + ctx.globalAlpha = 0.7; + ctx.strokeStyle = color; + ctx.lineWidth = 1; + ctx.beginPath(); + + var w = canvas.width/points.length; + var end = points.length; + for (var i = 0; i < end; i++) { + var x = points[i].x; + var y = points[i][price]; + y = canvas.height * y/highest; + if (i == 0) { + ctx.moveTo(x+w/2, y); + } else { + ctx.lineTo(x+w/2, y); + } + } + ctx.stroke(); + ctx.restore(); + } + + function drawKLine(ctx, from, to, points, highest) + { + ctx.save(); + ctx.globalAlpha = 0.4; + ctx.lineWidth = 2; + var end = points.length; + for (var i = 0; i < end; i++) { + var x = points[i].x; + var open = canvas.height * points[i].open/highest - canvas.movedY; + var close = canvas.height * points[i].close/highest - canvas.movedY; + var high = canvas.height * points[i].high/highest - canvas.movedY; + var low = canvas.height * points[i].low/highest - canvas.movedY; + + var top, bottom; + if (close <= open) { + ctx.fillStyle = Qt.rgba(1, 0, 0, 1); + ctx.strokeStyle = Qt.rgba(1, 0, 0, 1); + top = close; + bottom = open; + } else { + ctx.fillStyle = Qt.rgba(0, 1, 0, 1); + ctx.strokeStyle = Qt.rgba(0, 1, 0, 1); + top = open; + bottom = close; + } + + var w1, w2; + w1 = canvas.width/points.length; + w2 = w1 > 10 ? w1/2 : w1; + + ctx.fillRect(x + (w1 - w2)/2, top, w2, bottom - top); + ctx.beginPath(); + ctx.moveTo(x+w1/2, high); + ctx.lineTo(x+w1/2, low); + ctx.stroke(); + } + ctx.restore(); + } + + function drawVolume(ctx, from, to, color, price, points, highest) + { + ctx.save(); + ctx.fillStyle = color; + ctx.globalAlpha = 0.6; + ctx.strokeStyle = Qt.rgba(0.8, 0.8, 0.8, 1); + ctx.lineWidth = 1; + + var end = points.length; + for (var i = 0; i < end; i++) { + var x = points[i].x; + var y = points[i][price]; + y = canvas.height * (1 - y/highest); + ctx.fillRect(x, y, canvas.width/points.length, canvas.height - y); + } + ctx.restore(); + } + + onPaint: { + var ctx = canvas.getContext("2d"); + + ctx.globalCompositeOperation = "source-over"; + ctx.lineWidth = 1; + + drawBackground(ctx); + if (!stockModel.ready) + return; + + last = stockModel.indexOf(chart.endDate) + first = last - (chart.endDate.getTime() - chart.startDate.getTime())/86400000; + console.log("painting... first:" + first + ", last:" + last); + + var highestPrice = stockModel.highestPrice; + var highestVolume = stockModel.highestVolume; + console.log("highest price:" + highestPrice + ", highest volume:" + highestVolume) + var points = []; + for (var i = 0; i <= last - first; i++) { + var price = stockModel.get(i + first); + points.push({ + x: i*canvas.width/(last-first+1), + open: price.open, + close: price.close, + high:price.high, + low:price.low, + volume:price.volume + }); + } + if (settings.drawHighPrice) + drawPrice(ctx, first, last, settings.highColor,"high", points, highestPrice); + if (settings.drawLowPrice) + drawPrice(ctx, first, last, settings.lowColor,"low", points, highestPrice); + if (settings.drawOpenPrice) + drawPrice(ctx, first, last,settings.openColor,"open", points, highestPrice); + if (settings.drawClosePrice) + drawPrice(ctx, first, last, settings.closeColor,"close", points, highestPrice); + if (settings.drawVolume) + drawVolume(ctx, first, last, settings.volumeColor,"volume", points, highestVolume); + if (settings.drawKLine) + drawKLine(ctx, first, last, points, highestPrice); + } +} +} diff --git a/examples/demos/stockchart/contents/Stocks.qml b/examples/demos/stockchart/content/StockListModel.qml similarity index 100% rename from examples/demos/stockchart/contents/Stocks.qml rename to examples/demos/stockchart/content/StockListModel.qml diff --git a/examples/demos/stockchart/contents/TitleBar.qml b/examples/demos/stockchart/content/StockListView.qml similarity index 65% rename from examples/demos/stockchart/contents/TitleBar.qml rename to examples/demos/stockchart/content/StockListView.qml index 28edda2..1dd442d 100644 --- a/examples/demos/stockchart/contents/TitleBar.qml +++ b/examples/demos/stockchart/content/StockListView.qml @@ -40,31 +40,48 @@ import QtQuick 2.0 -Item { - id: titleBar - property string title: "" +Rectangle { + id:root + width:320 + height:480 + color:"#423A2F" - BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + property string currentStockId:"" + property string currentStockName:"" - Image { - id: quitButton - anchors.left: parent.left//; anchors.leftMargin: 0 - anchors.verticalCenter: parent.verticalCenter - source: "images/quit.png" - MouseArea { - anchors.fill: parent - onClicked: Qt.quit() - } + ListView { + id:view + anchors.fill:parent + keyNavigationWraps:true + focus:true + snapMode: ListView.SnapToItem + model:StockListModel{} + + onCurrentIndexChanged: { + root.currentStockId = model.get(currentIndex).stockId + root.currentStockName = model.get(currentIndex).name + console.log("current stock:" + root.currentStockId + " - " + root.currentStockName) } - Text { - id: categoryText - anchors { - left: quitButton.right; right: parent.right; //leftMargin: 10; rightMargin: 10 - verticalCenter: parent.verticalCenter + delegate:Rectangle { + height:30 + width:parent.width + color:"transparent" + MouseArea { + anchors.fill: parent + onClicked:view.currentIndex = index + } + + Text { + anchors.verticalCenter : parent.top + anchors.verticalCenterOffset : 15 + color:index == view.currentIndex ? "#ECC089" : "#A58963" + font.pointSize:12 + font.bold:true + text:" " + stockId + " - " + name } - elide: Text.ElideLeft - text: title - font.bold: true; font.pointSize: 20; color: "White"; style: Text.Raised; styleColor: "Black" } -} + + highlight:Image {height:30; width:parent.width; source:"images/stock-selected.png"} + } +} \ No newline at end of file diff --git a/examples/demos/stockchart/content/StockModel.qml b/examples/demos/stockchart/content/StockModel.qml new file mode 100644 index 0000000..72afe28 --- /dev/null +++ b/examples/demos/stockchart/content/StockModel.qml @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 + +ListModel { + id:model + property string stockId:"" + property string stockName:"" + property var startDate + property var endDate + property string stockDataCycle:"d" + property bool ready:false + property real stockPrice:0.0 + property real stockPriceChanged:0.0 + property real highestPrice:0 + property real highestVolume:0 + + signal dataReady + + function indexOf(date) { + var end = new Date(model.get(0).date) + var start = new Date(model.get(model.count - 1).date) + if (end <= date) + return model.count -1; + + if (start >= date) + return 0; + + for (var i = 0; i < model.count; i++) { + var d = new Date(model.get(i).date) + if ( d === date) + return i; + } + return -1; + } + + function requestUrl() { + if (stockId === "") + return; + + if (startDate === undefined) + startDate = new Date(1995, 3, 25) //default: 25 April 1995 + + if (endDate === undefined) + endDate = new Date(); //today + + if (stockDataCycle !== "d" && stockDataCycle !== "w" && stockDataCycle !== "m") + stockDataCycle = "d"; + + /* + 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) + */ + var request = "http://ichart.finance.yahoo.com/table.csv?"; + request += "s=" + stockId; + request += "&a=" + startDate.getDate(); + request += "&b=" + startDate.getMonth(); + request += "&c=" + startDate.getYear(); + request += "&d=" + endDate.getDate(); + request += "&e=" + endDate.getMonth(); + request += "&f=" + endDate.getYear(); + request += "&g=" + stockDataCycle; + request += "&ignore=.csv"; + return request; + } + + function createStockPrice(r) { + if (highestPrice < r[2]) + highestPrice = r[2]; + if (highestVolume < r[5]) + highestVolume = r[5]; + return { + "date": r[0], + "open":r[1], + "high":r[2], + "low":r[3], + "close":r[4], + "volume":r[5], + "adjusted":r[6] + }; + } + + function updateStock() { + var xhr = new XMLHttpRequest; + + var req = requestUrl(); + console.log("getting " + req + " ....."); + + xhr.open("GET", req); + + + model.ready = false; + model.clear(); + var i = 1; //skip the first line + xhr.onreadystatechange = function() { + if (xhr.readyState === XMLHttpRequest.LOADING || xhr.readyState === XMLHttpRequest.DONE) { + var records = xhr.responseText.split('\n'); + console.log("Updating:" + (records.length - i) + " records for " + model.stockId + " from:" + model.startDate + " to " + model.endDate) + + for (;i < records.length; i++ ) { + var r = records[i].split(','); + if (r.length === 7) + model.append(createStockPrice(r)); + } + + if (xhr.readyState === XMLHttpRequest.DONE) { + if (model.count > 0) { + console.log("done, total:" + model.count); + model.ready = true; + model.stockPrice = model.get(0).adjusted + model.stockPriceChanged = Math.round((model.stockPrice - model.get(2).adjusted) * 100) / 100 + model.dataReady(); //emit signal + } + } + } + } + xhr.send() + } +} diff --git a/examples/demos/stockchart/content/StockSettings.qml b/examples/demos/stockchart/content/StockSettings.qml new file mode 100644 index 0000000..656830d --- /dev/null +++ b/examples/demos/stockchart/content/StockSettings.qml @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 + +Rectangle { + id:root + width:320 + height:480 + color:"#423A2F" + property var startDate : startDatePicker.date; + property var endDate : endDatePicker.date; + + property bool drawHighPrice:highButton.buttonEnabled + property bool drawLowPrice:lowButton.buttonEnabled + property bool drawOpenPrice:openButton.buttonEnabled + property bool drawClosePrice:closeButton.buttonEnabled + property bool drawVolume:volumeButton.buttonEnabled + property bool drawKLine:klineButton.buttonEnabled + + property color highColor:Qt.rgba(1, 0, 0, 1) + property color lowColor:Qt.rgba(0, 1, 0, 1) + property color openColor:Qt.rgba(0, 0, 1, 1) + property color closeColor:"#ecc088" + property color volumeColor:Qt.rgba(0.3, 0.5, 0.7, 1) + + property string chartType:"year" + Image { + id:logo + source:"images/logo.png" + anchors.horizontalCenter : parent.horizontalCenter + anchors.top:parent.top + anchors.topMargin:15 + } + + Text { + id:startDateText + text:"START DATE:" + color:"#76644A" + font.pointSize:15 + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:logo.bottom + anchors.topMargin:20 + } + + DatePicker { + id:startDatePicker + anchors.left:parent.left + anchors.leftMargin:30 + anchors.top:startDateText.bottom + anchors.topMargin:15 + date : new Date(1995, 3, 25) + } + + Text { + id:endDateText + text:"END DATE:" + color:"#76644A" + font.pointSize:15 + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:startDatePicker.bottom + anchors.topMargin:20 + } + + DatePicker { + id:endDatePicker + anchors.left:parent.left + anchors.leftMargin:30 + anchors.top:endDateText.bottom + anchors.topMargin:15 + } + + Text { + id:drawOptionsText + text:"DRAW OPTIONS:" + color:"#76644A" + font.pointSize:15 + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:endDatePicker.bottom + anchors.topMargin:20 + } + Column { + id:drawOptions + anchors.top:drawOptionsText.bottom + anchors.topMargin: 20 + anchors.left: parent.left + anchors.leftMargin: 30 + spacing:2 + Row{ + spacing:10 + Text { + text:"High " + color:"#76644A" + font.pointSize:15 + } + + Button { + id:highButton + buttonEnabled:false + } + + Text { + text:"Low " + color:"#76644A" + font.pointSize:15 + } + + Button { + id:lowButton + buttonEnabled:false + } + + Text { + text:"Open " + color:"#76644A" + font.pointSize:15 + } + Button { + id:openButton + buttonEnabled:false + } + } + Row{ + spacing:10 + Text { + text:"Close " + color:"#76644A" + font.pointSize:15 + } + Button { + id:closeButton + buttonEnabled:true + } + Text { + text:"Volume" + color:"#76644A" + font.pointSize:15 + } + Button { + id:volumeButton + buttonEnabled:true + } + Text { + text:"K Line" + color:"#76644A" + font.pointSize:15 + } + Button { + id:klineButton + buttonEnabled:false + } + } + } + + + Text { + id:chartTypeText + text:"CHART TYPE:" + color:"#76644A" + font.pointSize:15 + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:drawOptions.bottom + anchors.topMargin:20 + } + Row { + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.top : chartTypeText.bottom + anchors.topMargin: 20 + spacing:10 + Rectangle { + id:yearView + width:70 + height:30 + radius:10 + color:"steelblue" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.fill: parent + font.pointSize: 15 + text:"YEAR" + } + MouseArea { + anchors.fill: parent + onClicked: { + if (root.chartType != "year") { + root.chartType = "year"; + yearView.color = "steelblue" + monthView.color = "gray" + weekView.color = "gray" + allView.color = "gray" + } + } + } + } + Rectangle { + id:monthView + width:70 + radius:10 + height:30 + color:"gray" + Text { + anchors.fill: parent + anchors.horizontalCenter: parent.horizontalCenter + font.pointSize: 15 + color:"#ecc089" + text:"MONTH" + } + MouseArea { + anchors.fill: parent + onClicked: { + if (root.chartType != "month") { + root.chartType = "month"; + yearView.color = "gray" + monthView.color = "steelblue" + weekView.color = "gray" + allView.color = "gray" + } + } + } + + } + Rectangle { + id:weekView + height:30 + width:70 + radius:10 + color:"gray" + Text { + anchors.fill: parent + anchors.horizontalCenter: parent.horizontalCenter + font.pointSize: 15 + color:"#ecc089" + text:"WEEK" + } + MouseArea { + anchors.fill: parent + onClicked: { + if (root.chartType != "week") { + root.chartType = "week"; + yearView.color = "gray" + monthView.color = "gray" + weekView.color = "steelblue" + allView.color = "gray" + } + } + } + } + Rectangle { + id:allView + width:70 + radius:10 + height:30 + color:"gray" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.fill: parent + font.pointSize: 15 + color:"#ecc089" + text:"ALL" + } + MouseArea { + anchors.fill: parent + onClicked: { + if (root.chartType != "all") { + root.chartType = "all"; + yearView.color = "gray" + monthView.color = "gray" + weekView.color = "gray" + allView.color = "steelblue" + } + } + } + } + } +} diff --git a/examples/demos/stockchart/content/StockView.qml b/examples/demos/stockchart/content/StockView.qml new file mode 100644 index 0000000..7c5ff92 --- /dev/null +++ b/examples/demos/stockchart/content/StockView.qml @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +Rectangle { + id:root + width:320 + height:480 + color:"#423A2F" + property var stock:null + property var stocklist:null + property var settings:null + signal listViewClicked + signal settingsClicked + + function update() { + chart.endDate = settings.endDate + chart.update() + } + + Rectangle { + color:"#272822" + anchors.fill:parent + radius:20 + + Image { + source:"images/icon-items.png" + anchors.left:parent.left + anchors.leftMargin:10 + anchors.top:parent.top + anchors.topMargin:10 + MouseArea { + anchors.fill:parent + onClicked:listViewClicked() + } + } + Image { + source:"images/icon-settings.png" + anchors.right:parent.right + anchors.rightMargin:10 + anchors.top:parent.top + anchors.topMargin:10 + MouseArea { + anchors.fill:parent + onClicked:settingsClicked() + } + } + + Text { + id:desc + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:parent.top + anchors.topMargin:40 + color:"#564c3A" + font.pointSize:15 + text:root.stock.stockId + " - " + root.stock.stockName + } + + Text { + id:price + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:desc.bottom + anchors.topMargin:5 + color:"#ECC089" + font.pointSize:30 + text:root.stock.stockPrice + } + + Text { + id:priceChange + anchors.left:parent.left + anchors.leftMargin:20 + anchors.top:price.bottom + anchors.topMargin:5 + color: root.stock.stockPriceChanged < 0 ? "#A43D3D" : "#679B3A" + font.pointSize:25 + text: root.stock.stockPriceChanged + " (" + Math.abs(Math.round(root.stock.stockPriceChanged/(root.stock.stockPrice - root.stock.stockPriceChanged) * 100))/100 +"%)" + } + + StockChart { + id:chart + anchors.bottom: parent.bottom + anchors.top : priceChange.bottom + anchors.topMargin: 30 + width:parent.width + stockModel:root.stock + settings:root.settings + } + } +} diff --git a/examples/demos/stockchart/content/images/icon-calendar-anim.png b/examples/demos/stockchart/content/images/icon-calendar-anim.png new file mode 100644 index 0000000000000000000000000000000000000000..c5164d51832721293d76c8632b012badf9196048 GIT binary patch literal 1088 zcmV-G1i$-k7RCwC#S5I#nMI3#z|Gajb#ExAo zIieiOwfPEwK=jZP2M`4z^(*zj2~i1wgt$QpqM{(AegLRaA%P)9jT0o`458~9dc6RD{v3h~fwzfAm30!k8!EdruzAUeLu}yF=gsL3at*Lr4-qpPzjn9*Njl zYrJPJ2;BSqo)nr6r{z%XF1%hB2QC#SnV{$MBw|ESgkTWTG9o>Vxgc=o&aYAk0oU>1 zxDE!X%8Eb_eX1T3u(Eg@OV}9;g<=soI}6LoygUNi+uO3qbXZ$k6SMAF|88A&sdT&J zr8epM1l;da=`i&-Ad$^vu$0duleLl0wHGq4O3re$Jjahb_uLTCCQ5jYOr_3PJOtcWMUr!gli{PfGu^4+)J znM&gGn_tS^ody+@C=?d4us~JK41wyjiP|ENBnj=3q|eLbgzhWE=`FNz|Gq4(tfXso z{QLV&&Nf=D4t&3ZK@`iYSFRi(p=$98Swfl~Lb7=G~=*fnMAY~M%GCt z>UJgqwOUPjo{!x}yJ)#>0(a47?D_1FAv1b0zkmwwD_J|oS}?{wp6ND8Pztne=`c*H ztcek1ncjbJ1jBeTV-az~g&O5*5R4QrMYSpV?{X^AX0H~+=X&SSI)J_ve@J)pR} zjB@z`n@etL%!RzO9I=K1st@0VF;ZD7yvb8^a=1JUnP~~HzI)~ge0%Gb^r>QFw??}- zh>`&ti;Y({qwIk;1*beglc|Lw_1aA1%(|S03K3$7rQs}drU&uziPH)?I?bTH_ zoFd!1w$k=s z_0Vc5Vm|~^@!X4|Ab9a#@Z>=)ReBQ7q80R@9=v$yUr>r7D1w5A9{eZ>Jv9G-XoVy+ zdAsY(?k4ZOGq;;fJm33qjJ*xA_h`7UVLN0Dx| zp!WCit6szQlV_M68%12NTP*r%=W|{(tn=;hQ_PKzA#OC_7Yc~AHrxhHtr#Fwaf~>Y zn46iw)X^FS2L~ZZwxgSvwkbPUXL4c!<#L&HV!-o3j)&|NMPL9T)oMag!j9cp#@@U4 z@E6aZ_TeMs{2au~^(?-~+GQ?=50#B9i3yMBBgZ2lJOr_Q8ObI`5aY|rDvARGsBdhd zL~G;CTO1x4v3!ffYnD6q+_|PR-g^WrAtFuePz1_Q1tq~W4G}dwhIu8u&`{ONu)`qQ z8R5N31*SM#zRwW^EC(-s|KyAH%d5A~qL1VH($YHGeL&uL+J%^=pi_!zY7QFit&7k@ zs?j{96nG|}wn6?mx{Hg!JcFGWAkTe8ppt>xQE1&$j`?2OKjXFALkojx3^-amHo!AO zQ>S5dPo*%BWkfg~?U*Er946l-6mbMnc7Y%Tlx=`CxJc!QWiJX1ChxNbl*%M49Ziz& z$2Kbwyxa7ucp!eWJ)%_6jMAM3Gk} zNF=?JDZg|Xb}Gn)s$FRI6Jfss^wBFC0}VPTl7S$MP%8be&pOlbK@b|J8?cm*lstbW z6yk)=Bel1!d-kHMXADgOJ2ffqvw;DcMYoDReXY5n8Z`I+2rvM1v8|GF+Su;^0000< KMNUMnLSTY@@SMf~ literal 0 HcmV?d00001 diff --git a/examples/demos/stockchart/content/images/icon-items.png b/examples/demos/stockchart/content/images/icon-items.png new file mode 100644 index 0000000000000000000000000000000000000000..f951aa50093e25196f2e1684b5e962b2d2cd307e GIT binary patch literal 887 zcmV--1Bm>IP)!ypORgzZOxrr1E_RP5Nc`zZQB?h8q(Tp@Q z%y{O%|Nj5)gOlgEc+MfkGmjSl&kn#_w)yV;&Vx0_aYU_F6I)wb{C042b1f#5G{SPR zH$P6koIX7RQcB_bK4*ZhhiRG~8m)tMt7&Bb^wRS@JY}f061mBRHYPN9t^-1@W8Cw6 z0c#zj4Hk>7jxk28LxNnDeha`2Om0F22Bsq{y#H4WIKl~)(n4!ptWA?IH2LabSPEd8 zY_nOf*HHm=BIzil3IHWD0MS`U(#^}T*=*i_8LXSOeVsH%*5E+IXgX%{~ChZ z+uJ<=IAT)K_|)r}%9S4fDcJ}J!ZiELNw6x@)QSP_=zafo882m^9J_C~5AZl3+L;x7&CeA<5cRK~ND%yY*TRvDCY~xEiiihA=#Y(LLad zssNxf##qXg))7wZFEjpV8J5X}?$>u+FNSA_q;)a*9)JMlINQHC|GAkC&|_vb^#A}` z<8d+*pJiuYJgAy7vI+q9f7g}aqNj`1V8bLCy0B`}%Ey}z>bhvXCMe~XAz&{k!|@P= z!`}+w#sDZRY(8walwQ`j6(J?EG{aO8fJClrma`7^-@YxYm@+D2um00 z+=sV)814c@TryF1$0uOGqnw7xC<&mD1p}E<$Jp8dumSVK14{_efNNl3KqlBj7>3ua zSK&qgSkp-|>ioLC7U?8I8Ja%m-AA*DYc;=MN4k(|S!8|;qg@y$BeB&4)7hvDNjsWO zfH_2M_u%c&^W7phN{zTnJllM7BO<(e3;^h=B3QW{zx*@$?*RN3U;reJ=k5d{v(Eqk N002ovPDHLkV1j3ulQsYV literal 0 HcmV?d00001 diff --git a/examples/demos/stockchart/content/images/icon-settings.png b/examples/demos/stockchart/content/images/icon-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..222d998289a9208c8e353262a328d9f906d8c1c4 GIT binary patch literal 1066 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 M07*qoM6N<$f`q{cjQ{`u literal 0 HcmV?d00001 diff --git a/examples/demos/stockchart/content/images/stock-selected.png b/examples/demos/stockchart/content/images/stock-selected.png new file mode 100644 index 0000000000000000000000000000000000000000..5629796b9cfe55b6e3b2cbcffb63ff3187be4685 GIT binary patch literal 6480 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 literal 0 HcmV?d00001 diff --git a/examples/demos/stockchart/content/images/wheel.png b/examples/demos/stockchart/content/images/wheel.png new file mode 100644 index 0000000000000000000000000000000000000000..470a675b35fadd8d32eb8b4ec79613680c7f892a GIT binary patch literal 36223 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 literal 0 HcmV?d00001 diff --git a/examples/demos/stockchart/contents/images/quit.png b/examples/demos/stockchart/contents/images/quit.png deleted file mode 100755 index 5bda1b6e0d0effbabf53172040f0ed2eea78e34a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2369 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 diff --git a/examples/demos/stockchart/contents/images/stripes.png b/examples/demos/stockchart/contents/images/stripes.png deleted file mode 100755 index 9f36727ea424cd0da94bd5a7cee4082447275eeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgga4pBxq@gGdLS%5-~o-U3d5|`(m z-N<{$K*aT;^vADLPxXoJET5>DG2xP-Q1*siVN6aag0$kQzm$|0%E+I; z#Y!4I)=g1)z4*}Q$iB}`v_QR diff --git a/examples/demos/stockchart/contents/Button.qml b/examples/demos/stockchart/main.qml similarity index 60% rename from examples/demos/stockchart/contents/Button.qml rename to examples/demos/stockchart/main.qml index ab9ce35..e3cdedc 100644 --- a/examples/demos/stockchart/contents/Button.qml +++ b/examples/demos/stockchart/main.qml @@ -39,49 +39,63 @@ ****************************************************************************/ import QtQuick 2.0 +import "./content" -Item { - id: container +ListView { + id:root + width:320 + height:480 + snapMode:ListView.SnapOneItem + focus:false + orientation : ListView.Horizontal + boundsBehavior : Flickable.StopAtBounds + currentIndex : 1 - signal clicked - - property string text - width: buttonText.width + 28 - height: buttonText.height + 14 - - BorderImage { - id: buttonImage - source: "images/toolbutton.sci" - width: container.width - 10 - height: container.height + StockModel { + id:stock + stockId:listView.currentStockId + stockName: listView.currentStockName + startDate: settings.startDate + endDate:settings.endDate + onStockIdChanged: updateStock() + onStartDateChanged: updateStock() + onEndDateChanged: updateStock() + onDataReady: { + root.currentIndex = 1 + stockView.update() } - BorderImage { - id: pressed - opacity: 0 - source: "images/toolbutton.sci" - width: container.width - 10 - height: container.height + } + + model: VisualItemModel { + StockListView { + id:listView + width:root.width + height:root.height } - MouseArea { - id: mouseRegion - anchors.fill: buttonImage - onClicked: { container.clicked(); } + + StockView { + id:stockView + width:root.width + height:root.height + stocklist : listView + settings : settings + stock: stock + + onListViewClicked:root.currentIndex = 0 + onSettingsClicked:root.currentIndex = 2 } - Text { - id: buttonText - color: "white" - anchors.centerIn: buttonImage - font.bold: true - font.pointSize: 15 - text: container.text - style: Text.Raised - styleColor: "black" + + StockSettings { + id:settings + width:root.width + height:root.height + onDrawHighPriceChanged: stockView.update() + onDrawLowPriceChanged: stockView.update() + onDrawOpenPriceChanged: stockView.update() + onDrawClosePriceChanged: stockView.update() + onDrawVolumeChanged: stockView.update() + onDrawKLineChanged: stockView.update() + onChartTypeChanged: stockView.update() } - states: [ - State { - name: "Pressed" - when: mouseRegion.pressed == true - PropertyChanges { target: pressed; opacity: 1 } - } - ] + } } diff --git a/examples/demos/stockchart/model.cpp b/examples/demos/stockchart/model.cpp deleted file mode 100644 index 42eb2d5..0000000 --- a/examples/demos/stockchart/model.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** 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(); -} - -StockPrice* StockModel::stockPriceAtIndex(int idx) const -{ - if (idx >=0 && idx < _prices.size()) { - return _prices[idx]; - } - return 0; -} - - -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; - QNetworkReply* reply = _manager->get(QNetworkRequest(QUrl(query))); - connect(reply, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(downloadProgress(qint64,qint64))); - } -} - -void StockModel::update(QNetworkReply *reply) -{ - _updating = false; - - if (reply) { - if (reply->error() == QNetworkReply::NoError) { - beginResetModel(); - - foreach (StockPrice* p, _prices) { - p->deleteLater(); - } - - _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 = new StockPrice(this); - price->setDate(QDate::fromString(fields[0], Qt::ISODate)); - price->setOpenPrice(fields[1].toFloat()); - price->setHighPrice(fields[2].toFloat()); - price->setLowPrice(fields[3].toFloat()); - price->setClosePrice(fields[4].toFloat()); - price->setVolume(fields[5].toInt()); - price->setAdjustedPrice(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(); - emit dataChanged(QModelIndex(), QModelIndex()); - } -} - -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/demos/stockchart/model.h b/examples/demos/stockchart/model.h deleted file mode 100644 index 95e6f48..0000000 --- a/examples/demos/stockchart/model.h +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** 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 - -class StockPrice : public QObject -{ - Q_OBJECT - Q_PROPERTY(QDate date READ date) - Q_PROPERTY(qreal openPrice READ openPrice) - Q_PROPERTY(qreal closePrice READ closePrice) - Q_PROPERTY(qreal highPrice READ highPrice) - Q_PROPERTY(qreal lowPrice READ lowPrice) - Q_PROPERTY(qint32 volume READ volume) - Q_PROPERTY(qreal adjustedPrice READ adjustedPrice) -public: - - StockPrice(QObject *parent = 0) - : QObject(parent) - , _openPrice(-1) - , _closePrice(-1) - , _highPrice(-1) - , _lowPrice(-1) - , _volume(-1) - , _adjustedPrice(-1) - { - } - QDate date() const {return _date;} - qreal openPrice() const {return _openPrice; } - qreal closePrice() const {return _closePrice;} - qreal highPrice() const {return _highPrice;} - qreal lowPrice() const{return _lowPrice;} - qreal adjustedPrice() const{return _adjustedPrice;} - qint32 volume() const{return _volume;} - - void setDate(const QDate& date){_date = date;} - void setOpenPrice(qreal price){_openPrice = price;} - void setClosePrice(qreal price){_closePrice = price;} - void setHighPrice(qreal price){_highPrice = price;} - void setLowPrice(qreal price){_lowPrice = price;} - void setAdjustedPrice(qreal price) {_adjustedPrice = price;} - void setVolume(qint32 volume) {_volume = volume;} - -private: - 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(); - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); - -public slots: - void requestData(); - StockPrice* stockPriceAtIndex(int idx) const; -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/demos/stockchart/plugin.cpp b/examples/demos/stockchart/plugin.cpp deleted file mode 100644 index 3b354e2..0000000 --- a/examples/demos/stockchart/plugin.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** 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" - -class QStockChartExampleQmlPlugin : public QQmlExtensionPlugin -{ - Q_OBJECT -public: - void registerTypes(const char *uri) - { - Q_ASSERT(uri == QLatin1String("com.nokia.StockChartExample")); - qmlRegisterType(uri, 1, 0, "StockModel"); - qmlRegisterType(uri, 1, 0, "StockPrice"); - } -}; - -#include "plugin.moc" - -Q_EXPORT_PLUGIN2(qmlstockchartexampleplugin, QStockChartExampleQmlPlugin); diff --git a/examples/demos/stockchart/stock.qml b/examples/demos/stockchart/stock.qml deleted file mode 100644 index 819625c..0000000 --- a/examples/demos/stockchart/stock.qml +++ /dev/null @@ -1,726 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** 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 com.nokia.StockChartExample 1.0 -import "./contents" - -Rectangle { - id:container - width: 360; height: 600 - color: "#343434"; - Image { source: "contents/images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 1 } - - - TitleBar { - id: titleBar - width: parent.width - anchors.top : container.top - height: 40 - opacity: 0.9 - } - - StockModel { - id:stockModel - dataCycle: StockModel.Daily - function dataCycleName() { - if (dataCycle === StockModel.Weekly) - return "Weekly"; - else if (dataCycle === StockModel.Monthly) - return "Monthly"; - return "Daily"; - } - - onDataChanged: { - if (view.viewType == "chart") { - canvas.requestPaint(); - } - } - onDownloadProgress: { - if (bytesReceived == bytesTotal && bytesTotal != -1) { - progress.opacity = 0; - } else { - progress.opacity = 0.8; - progress.text = "downloading " + stockModel.dataCycleName() + " data ..."+ Math.round(bytesReceived/1000) + " KB"; - } - } - - property string description:""; - } - - Stocks {id:stocks} - - Rectangle { - id: header - width: parent.width - height: 20 - color: "steelblue" - opacity: 0 - Row { - spacing: 2 - Text { - id:t - font.pointSize:15 - horizontalAlignment:Text.AlignHCenter - font.bold: true - font.underline:true - } - Rectangle { - height:20 - width:50 - Text {text:"Stock list"; font.pointSize:15; font.bold: true} - } - } - } - - ListView { - id:stockList - width: parent.width - anchors.bottom: container.bottom - anchors.top : titleBar.bottom - focus: true - keyNavigationWraps: true - spacing:1 - opacity: 1 - model: stocks - - Component.onCompleted: opacity = 0.9; - onOpacityChanged: { - titleBar.title = "Top 100 NASDAQ stocks" - } - - - delegate : Rectangle { - height: 30 - width: view.width - color: { - if (ListView.isCurrentItem) - return focus ? "lightyellow" : "pink"; - return index % 2 == 0 ? "lightblue" : "lightsteelblue" - } - Text { - font.pointSize:20 - text: index + ". " + stockId + " \t(" + name + ")"; - } - MouseArea { - anchors.fill: parent; - onDoubleClicked: { - stockList.opacity = 0; - stockModel.stockName = stockId; - stockModel.description = "NASDAQ:" + stockId + " (" + name + ")"; - view.opacity = 1; - view.viewType = "chart"; - canvas.opacity = 0.7; - } - onClicked: stockList.currentIndex = index - }//mousearea - }//delegate - } - - ListView { - id:view - width: container.width - height: container.height - 50 - anchors.bottom: container.bottom - focus: true - keyNavigationWraps: true - - spacing:1 - opacity: 0 - model: stockModel - highlightFollowsCurrentItem: false - highlightRangeMode: ListView.StrictlyEnforceRange - preferredHighlightBegin:50 - preferredHighlightEnd : height - 50 - highlight: listHighlight - - //header : Text {} - delegate: listDelegate - snapMode: ListView.SnapToItem - - property string viewType : "list" - property int topIndex:indexAt(0,contentY); - property int bottomIndex:indexAt(0, contentY+height); - - onCountChanged: { - - titleBar.title = stockModel.description + " " + Qt.formatDate(stockModel.startDate, "yyyy-MM-dd") + " - " + - Qt.formatDate(stockModel.endDate, "yyyy-MM-dd") + " " + stockModel.dataCycleName() + - " records:" + view.count; - - } - - Component { - id: listDelegate - Rectangle { - height: 20 - width: view.width - border.color: "lightsteelblue" - border.width: 1 - color: { - if (ListView.isCurrentItem) - return focus ? "lightyellow" : "pink"; - - return index % 2 == 0 ? "lightblue" : "lightsteelblue" - } - Text { - font.pointSize:13 - text: index + ". " + 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; - } - MouseArea {anchors.fill: parent; onClicked: view.currentIndex = index} - } - } - - Component { - id: chartDelegate - Rectangle { - height: 20 - width: view.width/view.count * canvas.scaleX - border.color: "lightsteelblue" - border.width: 1 - color: { - if (ListView.isCurrentItem) - return focus ? "lightyellow" : "pink"; - - return index % 2 == 0 ? "lightblue" : "lightsteelblue" - } - - Text { - anchors.bottom: parent.bottom - font.pointSize: { - if (parent.width <= 4) - return 1; - if (parent.width <= 50) - return parent.width/4; - return 15; - } - horizontalAlignment:Text.AlignHCenter - verticalAlignment:Text.AlignBottom - text:font.pointSize > 1 ? Qt.formatDate(date, "d/M/yy") : "" - } - MouseArea {anchors.fill: parent; onClicked: view.currentIndex = index} - } - } - - Component { - id:chartHighlight - Rectangle { radius: 5; width:40; height: 20; color: "lightsteelblue" } - } - - Component { - id:listHighlight - Rectangle { radius: 5; width:container.width; height: 20; color: "lightsteelblue" } - } - - - - - onViewTypeChanged : { - if (viewType == "list") { - view.orientation = ListView.Vertical; - view.delegate = listDelegate; -// view.section.property = "year"; -// view.section.criteria = ViewSection.FullString; -// view.section.delegate = sectionHeading; - view.highlight = listHighlight; - view.opacity = 1; - canvas.opacity = 0; - // comment.opacity = 0; - - } else if (viewType == "chart") { - view.orientation = ListView.Horizontal; - view.delegate = chartDelegate; - //comment.opacity = 0.6; - - view.opacity = 1; - view.height = 30 - - canvas.opacity = 0.7; - canvas.requestPaint(); - } else { - viewType = "list"; - } - } - - - onCurrentIndexChanged: { - //header.updateCurrent(stockModel.stockPriceAtIndex(view.currentIndex)); - if (viewType == "chart") { - canvas.first = Math.round(view.currentIndex - view.currentIndex / canvas.scaleX); - canvas.last = Math.round(view.currentIndex + (view.count - view.currentIndex) / canvas.scaleX); - - canvas.requestPaint(); - } - } - onContentYChanged: { // keep "current" item visible - topIndex = indexAt(0,contentY); - bottomIndex = indexAt(0, contentY+height); - - if (topIndex != -1 && currentIndex <= topIndex) - currentIndex = topIndex+1; - else if (bottomIndex != -1 && currentIndex >= bottomIndex) - currentIndex = bottomIndex-1; - if (viewType == "chart") - canvas.requestPaint(); - } - - onContentXChanged: { // keep "current" item visible - topIndex = indexAt(contentX,0); - bottomIndex = indexAt(contentX+width, 0); - - if (topIndex != -1 && currentIndex <= topIndex) - currentIndex = topIndex+1; - else if (bottomIndex != -1 && currentIndex >= bottomIndex) - currentIndex = bottomIndex-1; - if (viewType == "chart") - canvas.requestPaint(); - } - - MouseArea { - anchors.fill: parent - onDoubleClicked: { - if (view.viewType == "list") - view.viewType = "chart"; - else - view.viewType = "list"; - } - } - } - - - - Canvas { - id:canvas - anchors.top : titleBar.bottom - anchors.bottom : view.top - width:container.width; - opacity:0 - renderTarget: Canvas.Image - renderStrategy: Canvas.Immediate - property bool running:false - property int frames:first - property int mouseX:0; - property int mouseY:0; - property int mousePressedX:0; - property int mousePressedY:0; - property int movedY:0 - property real scaleX:1.0; - property real scaleY:1.0; - property int first:0; - property int last:view.count - 1; - - onOpacityChanged: { - if (opacity > 0) - requestPaint(); - } - Text { - id:comment - x:100 - y:50 - font.pointSize: 20 - color:"white" - opacity: 0.7 - focus:false - text: stockModel.description - function updateCurrent(price) - { - if (price !== undefined) { - text =stockModel.description + "\n" - + Qt.formatDate(price.date, "yyyy-MM-dd") + " OPEN:" - + Math.round(price.openPrice*100)/100 + " HIGH:" - + Math.round(price.highPrice*100)/100 + " LOW:" - + Math.round(price.lowPrice*100)/100 + " CLOSE:" - + Math.round(price.closePrice*100)/100 + " VOLUME:" - + price.volume; - } - } - } - - Text { - id:priceAxis - x:25 - y:25 - font.pointSize: 15 - color:"yellow" - opacity: 0.7 - focus: false - } - Text { - id:volumeAxis - x:canvas.width - 200 - y:25 - font.pointSize: 15 - color:"yellow" - opacity: 0.7 - } - - Rectangle { - id:progress - x:canvas.width/2 - 100 - y:canvas.height/2 - width:childrenRect.width - height: childrenRect.height - opacity: 0 - color:"white" - property string text; - Text { - text:parent.text - font.pointSize: 20 - } - } - - Button { - id:runButton - text:"Run this chart" - y:0 - x:canvas.width/2 - 50 - opacity: 0.5 - onClicked: { - if (canvas.running) { - canvas.running = false; - canvas.frames = canvas.first; - canvas.requestPaint(); - text = "Run this chart"; - comment.text = stockModel.description; - } else { - text = " Stop running "; - canvas.runChart(); - } - } - } - Button { - id:returnButton - text:"Stocks" - y:0 - anchors.left : runButton.right - anchors.leftMargin : 20 - opacity: 0.5 - onClicked: { - stockList.opacity = 1; - canvas.opacity = 0; - } - } - PinchArea { - anchors.fill: parent - onPinchUpdated : { - var current = pinch.center; - var scale = pinch.scale; - console.log("center:" + pinch.center + " scale:" + pinch.scale); - //canvas.requestPaint(); - } - } - - MouseArea { - anchors.fill: parent - - onDoubleClicked: { - if (stockModel.dataCycle == StockModel.Daily) - stockModel.dataCycle = StockModel.Weekly; - else if (stockModel.dataCycle == StockModel.Weekly) - stockModel.dataCycle = StockModel.Monthly; - else - stockModel.dataCycle = StockModel.Daily; - } - - onPositionChanged: { - if (mouse.modifiers & Qt.ControlModifier) { - if (canvas.mouseX == 0 && canvas.mouseY == 0) { - canvas.mouseX = mouse.x; - canvas.mouseY = mouse.y; - } - } else{ - var w = (view.width/view.count)*canvas.scaleX; - - //canvas.movedY += Math.round((canvas.mousePressedY - mouse.y)/2); - - var movedX = Math.round((canvas.mousePressedX - mouse.x)/w); - if (movedX != 0 || canvas.movedY != 0) { - if (canvas.first + movedX >= 0 && canvas.last + movedX < view.count) { - canvas.first += movedX; - canvas.last += movedX; - } - canvas.requestPaint(); - } - } - } - - onPressed: { - canvas.mousePressedX = mouse.x; - canvas.mousePressedY = mouse.y; - } - - onReleased : { - if (mouse.modifiers & Qt.ControlModifier) { - var sx = mouse.x - canvas.mouseX; - var sy = canvas.mouseY - mouse.y; - - if (Math.abs(sx) < 50) sx = 0; - if (Math.abs(sy) < 50) sy = 0; - - if (sx > 0) - canvas.scaleX *= sx/100 +1; - else - canvas.scaleX *= 1/(-sx/100 + 1); - - if (sy > 0) - canvas.scaleY *= sy/100 +1; - else - canvas.scaleY *= 1/(-sy/100 + 1); - - if (canvas.scaleX < 1) - canvas.scaleX = 1; - - //console.log("scaleX:" + canvas.scaleX + ", scaleY:" + canvas.scaleY); - - canvas.first = Math.round(view.currentIndex - view.currentIndex / canvas.scaleX); - canvas.last = Math.round(view.currentIndex + (view.count - view.currentIndex) / canvas.scaleX); - - canvas.mouseX = 0; - canvas.mouseY = 0; - canvas.mousePressedX = 0; - canvas.mousePressedY = 0; - canvas.requestPaint(); - } - } - } - - function runChart() { - canvas.running = true; - requestPaint(); - } - - function showPriceAt(x) { - var w = (view.width/view.count)*canvas.scaleX; - //header.updateCurrent(stockModel.stockPriceAtIndex(canvas.first + Math.round(x/w))); - //console.log("x:" + x + " w:" + w + " index:" + (canvas.first + Math.round(x/w))); - } - - function drawPrice(ctx, from, to, color, price, points, highest) - { - ctx.globalAlpha = 0.7; - ctx.strokeStyle = color; - ctx.lineWidth = 1; - ctx.beginPath(); - - //price x axis - priceAxis.text = "price:" + Math.round(highest); - ctx.font = "bold 12px sans-serif"; - - ctx.strokeText("price", 25, 25); - for (var j = 1; j < 30; j++) { - var val = (highest * j) / 30; - val = canvas.height * (1 - val/highest); - ctx.beginPath(); - - ctx.moveTo(10, val); - if (j % 5) - ctx.lineTo(15, val); - else - ctx.lineTo(20, val); - ctx.stroke(); - } - - ctx.beginPath(); - ctx.moveTo(10, 0); - ctx.lineTo(10, canvas.height); - ctx.stroke(); - - - var w = canvas.width/points.length; - var end = canvas.running? canvas.frames - canvas.first :points.length; - for (var i = 0; i < end; i++) { - var x = points[i].x; - var y = points[i][price]; - y += canvas.movedY; - - y = canvas.height * (1 - y/highest); - if (i == 0) { - ctx.moveTo(x+w/2, y); - } else { - ctx.lineTo(x+w/2, y); - } - } - ctx.stroke(); - } - - function drawKLine(ctx, from, to, points, highest) - { - ctx.globalAlpha = 0.4; - ctx.lineWidth = 2; - var end = canvas.running? canvas.frames - canvas.first :points.length; - for (var i = 0; i < end; i++) { - var x = points[i].x; - var open = canvas.height * (1 - points[i].open/highest) - canvas.movedY; - var close = canvas.height * (1 - points[i].close/highest) - canvas.movedY; - var high = canvas.height * (1 - points[i].high/highest) - canvas.movedY; - var low = canvas.height * (1 - points[i].low/highest) - canvas.movedY; - - var top, bottom; - if (close <= open) { - ctx.fillStyle = Qt.rgba(1, 0, 0, 1); - ctx.strokeStyle = Qt.rgba(1, 0, 0, 1); - top = close; - bottom = open; - } else { - ctx.fillStyle = Qt.rgba(0, 1, 0, 1); - ctx.strokeStyle = Qt.rgba(0, 1, 0, 1); - top = open; - bottom = close; - } - - var w1, w2; - w1 = canvas.width/points.length; - w2 = w1 > 10 ? w1/2 : w1; - - ctx.fillRect(x + (w1 - w2)/2, top, w2, bottom - top); - ctx.beginPath(); - ctx.moveTo(x+w1/2, high); - ctx.lineTo(x+w1/2, low); - ctx.stroke(); - } - ctx.globalAlpha = 1; - - } - - function drawVolume(ctx, from, to, color, price, points, highest) - { - ctx.fillStyle = color; - ctx.globalAlpha = 0.6; - ctx.strokeStyle = Qt.rgba(0.8, 0.8, 0.8, 1); - ctx.lineWidth = 1; - - //volume x axis - volumeAxis.text = "volume:" + Math.round(highest/(1000*100)) + "M"; - for (var j = 1; j < 30; j++) { - var val = (highest * j) / 30; - val = canvas.height * (1 - val/highest); - ctx.beginPath(); - if (j % 5) - ctx.moveTo(canvas.width - 15, val); - else - ctx.moveTo(canvas.width - 20, val); - ctx.lineTo(canvas.width - 10, val); - ctx.stroke(); - } - - ctx.beginPath(); - ctx.moveTo(canvas.width - 10, 0); - ctx.lineTo(canvas.width - 10, canvas.height); - ctx.stroke(); - - var end = canvas.running? canvas.frames - canvas.first :points.length; - for (var i = 0; i < end; i++) { - var x = points[i].x; - var y = points[i][price]; - y = canvas.height * (1 - y/highest); - ctx.fillRect(x, y, canvas.width/points.length, canvas.height - y); - } - } -/* - onPainted : { - if (canvas.running) { - if (frames >= last) { - canvas.running = false; - canvas.frames = first; - runButton.text = "Run this chart"; - comment.text = stockModel.description; - requestPaint(); - } else { - frames += Math.round(view.count / 100); - if (frames > last) frames = last; - var price = stockModel.stockPriceAtIndex(frames); - if (price) { - comment.updateCurrent(price); - } - - requestPaint(); - } - } - } -*/ - onPaint: { - if (view.currentIndex <= 0) - first = 0; - if (last >= view.count) - last = view.count - 1; - - //console.log("painting... first:" + first + ", last:" + last + " current:" + view.currentIndex); - var ctx = canvas.getContext("2d"); - ctx.save(); - - ctx.globalCompositeOperation = "source-over"; - ctx.lineWidth = 1; - ctx.lineJoin = "round"; - ctx.fillStyle = "rgba(0,0,0,0)"; - - ctx.fillRect(0, 0, canvas.width, canvas.height); - - - - var highestPrice = 500/canvas.scaleY; - var highestValume = 600 * 1000 * 1000/canvas.scaleY; - var points = []; - for (var i = 0; i <= last - first; i++) { - var price = stockModel.stockPriceAtIndex(i + first); - points.push({ - x: i*canvas.width/(last-first+1), - open: price.openPrice, - close: price.closePrice, - high:price.highPrice, - low:price.lowPrice, - volume:price.volume - }); - } - - drawPrice(ctx, first, last, Qt.rgba(1, 0, 0, 1),"high", points, highestPrice); - drawPrice(ctx, first, last, Qt.rgba(0, 1, 0, 1),"low", points, highestPrice); - drawPrice(ctx, first, last, Qt.rgba(0, 0, 1, 1),"open", points, highestPrice); - drawPrice(ctx, first, last, Qt.rgba(0.5, 0.5, 0.5, 1),"close", points, highestPrice); - drawVolume(ctx, first, last, Qt.rgba(0.3, 0.5, 0.7, 1),"volume", points, highestValume); - drawKLine(ctx, first, last, points, highestPrice); - ctx.restore(); - } - } -} diff --git a/examples/demos/stockchart/stockchart.pro b/examples/demos/stockchart/stockchart.pro deleted file mode 100644 index e368746..0000000 --- a/examples/demos/stockchart/stockchart.pro +++ /dev/null @@ -1,20 +0,0 @@ -TEMPLATE = lib -CONFIG += qt plugin -QT += qml network - -DESTDIR = com/nokia/StockChartExample -TARGET = qmlstockchartexampleplugin - -SOURCES += model.cpp plugin.cpp -HEADERS += model.h -qdeclarativesources.files += \ - com/nokia/StockChartExample/qmldir \ - stock.qml - -qdeclarativesources.path += $$[QT_INSTALL_EXAMPLES]/qtdeclarative/qml/plugins/com/nokia/StockChartExample - -sources.files += stockchart.pro model.h model.cpp plugin.cpp README -sources.path += $$[QT_INSTALL_EXAMPLES]/qtdeclarative/qml/plugins -target.path += $$[QT_INSTALL_EXAMPLES]/qtdeclarative/qml/plugins/com/nokia/StockChartExample - -INSTALLS += qdeclarativesources sources target diff --git a/examples/demos/stockchart/stockchart.qmlproject b/examples/demos/stockchart/stockchart.qmlproject deleted file mode 100644 index 5f2b909..0000000 --- a/examples/demos/stockchart/stockchart.qmlproject +++ /dev/null @@ -1,16 +0,0 @@ -import QmlProject 1.1 - -Project { - mainFile: "stock.qml" - - /* Include .qml, .js, and image files from current directory and subdirectories */ - QmlFiles { - directory: "." - } - JavaScriptFiles { - directory: "." - } - ImageFiles { - directory: "." - } -} \ No newline at end of file -- 2.7.4