1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the examples of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:BSD$
10 ** You may use this file under the terms of the BSD license as follows:
12 ** "Redistribution and use in source and binary forms, with or without
13 ** modification, are permitted provided that the following conditions are
15 ** * Redistributions of source code must retain the above copyright
16 ** notice, this list of conditions and the following disclaimer.
17 ** * Redistributions in binary form must reproduce the above copyright
18 ** notice, this list of conditions and the following disclaimer in
19 ** the documentation and/or other materials provided with the
21 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22 ** the names of its contributors may be used to endorse or promote
23 ** products derived from this software without specific prior written
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
39 ****************************************************************************/
41 import com.nokia.StockChartExample 1.0
46 width: 360; height: 600
48 Image { source: "contents/images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 1 }
54 anchors.top : container.top
61 dataCycle: StockModel.Daily
62 function dataCycleName() {
63 if (dataCycle === StockModel.Weekly)
65 else if (dataCycle === StockModel.Monthly)
71 if (view.viewType == "chart") {
72 canvas.requestPaint();
76 if (bytesReceived == bytesTotal && bytesTotal != -1) {
79 progress.opacity = 0.8;
80 progress.text = "downloading " + stockModel.dataCycleName() + " data ..."+ Math.round(bytesReceived/1000) + " KB";
84 property string description:"";
100 horizontalAlignment:Text.AlignHCenter
107 Text {text:"Stock list"; font.pointSize:15; font.bold: true}
115 anchors.bottom: container.bottom
116 anchors.top : titleBar.bottom
118 keyNavigationWraps: true
123 Component.onCompleted: opacity = 0.9;
125 titleBar.title = "Top 100 NASDAQ stocks"
129 delegate : Rectangle {
133 if (ListView.isCurrentItem)
134 return focus ? "lightyellow" : "pink";
135 return index % 2 == 0 ? "lightblue" : "lightsteelblue"
139 text: index + ". " + stockId + " \t(" + name + ")";
142 anchors.fill: parent;
144 stockList.opacity = 0;
145 stockModel.stockName = stockId;
146 stockModel.description = "NASDAQ:" + stockId + " (" + name + ")";
148 view.viewType = "chart";
149 canvas.opacity = 0.7;
151 onClicked: stockList.currentIndex = index
158 width: container.width
159 height: container.height - 50
160 anchors.bottom: container.bottom
162 keyNavigationWraps: true
167 highlightFollowsCurrentItem: false
168 highlightRangeMode: ListView.StrictlyEnforceRange
169 preferredHighlightBegin:50
170 preferredHighlightEnd : height - 50
171 highlight: listHighlight
174 delegate: listDelegate
175 snapMode: ListView.SnapToItem
177 property string viewType : "list"
178 property int topIndex:indexAt(0,contentY);
179 property int bottomIndex:indexAt(0, contentY+height);
183 titleBar.title = stockModel.description + " " + Qt.formatDate(stockModel.startDate, "yyyy-MM-dd") + " - " +
184 Qt.formatDate(stockModel.endDate, "yyyy-MM-dd") + " " + stockModel.dataCycleName() +
185 " records:" + view.count;
194 border.color: "lightsteelblue"
197 if (ListView.isCurrentItem)
198 return focus ? "lightyellow" : "pink";
200 return index % 2 == 0 ? "lightblue" : "lightsteelblue"
204 text: index + ". " + Qt.formatDate(date, "yyyy-MM-dd")
205 + "\t " + Math.round(openPrice*100)/100
206 + "\t " + Math.round(highPrice*100)/100
207 + "\t " + Math.round(lowPrice*100)/100
208 + "\t " + Math.round(closePrice*100)/100
209 + "\t " + volume + "\t "
210 + Math.round(adjustedPrice*100)/100;
212 MouseArea {anchors.fill: parent; onClicked: view.currentIndex = index}
220 width: view.width/view.count * canvas.scaleX
221 border.color: "lightsteelblue"
224 if (ListView.isCurrentItem)
225 return focus ? "lightyellow" : "pink";
227 return index % 2 == 0 ? "lightblue" : "lightsteelblue"
231 anchors.bottom: parent.bottom
233 if (parent.width <= 4)
235 if (parent.width <= 50)
236 return parent.width/4;
239 horizontalAlignment:Text.AlignHCenter
240 verticalAlignment:Text.AlignBottom
241 text:font.pointSize > 1 ? Qt.formatDate(date, "d/M/yy") : ""
243 MouseArea {anchors.fill: parent; onClicked: view.currentIndex = index}
249 Rectangle { radius: 5; width:40; height: 20; color: "lightsteelblue" }
254 Rectangle { radius: 5; width:container.width; height: 20; color: "lightsteelblue" }
260 onViewTypeChanged : {
261 if (viewType == "list") {
262 view.orientation = ListView.Vertical;
263 view.delegate = listDelegate;
264 // view.section.property = "year";
265 // view.section.criteria = ViewSection.FullString;
266 // view.section.delegate = sectionHeading;
267 view.highlight = listHighlight;
270 // comment.opacity = 0;
272 } else if (viewType == "chart") {
273 view.orientation = ListView.Horizontal;
274 view.delegate = chartDelegate;
275 //comment.opacity = 0.6;
280 canvas.opacity = 0.7;
281 canvas.requestPaint();
288 onCurrentIndexChanged: {
289 //header.updateCurrent(stockModel.stockPriceAtIndex(view.currentIndex));
290 if (viewType == "chart") {
291 canvas.first = Math.round(view.currentIndex - view.currentIndex / canvas.scaleX);
292 canvas.last = Math.round(view.currentIndex + (view.count - view.currentIndex) / canvas.scaleX);
294 canvas.requestPaint();
297 onContentYChanged: { // keep "current" item visible
298 topIndex = indexAt(0,contentY);
299 bottomIndex = indexAt(0, contentY+height);
301 if (topIndex != -1 && currentIndex <= topIndex)
302 currentIndex = topIndex+1;
303 else if (bottomIndex != -1 && currentIndex >= bottomIndex)
304 currentIndex = bottomIndex-1;
305 if (viewType == "chart")
306 canvas.requestPaint();
309 onContentXChanged: { // keep "current" item visible
310 topIndex = indexAt(contentX,0);
311 bottomIndex = indexAt(contentX+width, 0);
313 if (topIndex != -1 && currentIndex <= topIndex)
314 currentIndex = topIndex+1;
315 else if (bottomIndex != -1 && currentIndex >= bottomIndex)
316 currentIndex = bottomIndex-1;
317 if (viewType == "chart")
318 canvas.requestPaint();
324 if (view.viewType == "list")
325 view.viewType = "chart";
327 view.viewType = "list";
336 anchors.top : titleBar.bottom
337 anchors.bottom : view.top
338 width:container.width;
341 renderTarget: Canvas.Image
342 property bool running:false
343 property int frames:first
344 property int mouseX:0;
345 property int mouseY:0;
346 property int mousePressedX:0;
347 property int mousePressedY:0;
348 property int movedY:0
349 property real scaleX:1.0;
350 property real scaleY:1.0;
351 property int first:0;
352 property int last:view.count - 1;
366 text: stockModel.description
367 function updateCurrent(price)
369 if (price !== undefined) {
370 text =stockModel.description + "\n"
371 + Qt.formatDate(price.date, "yyyy-MM-dd") + " OPEN:"
372 + Math.round(price.openPrice*100)/100 + " HIGH:"
373 + Math.round(price.highPrice*100)/100 + " LOW:"
374 + Math.round(price.lowPrice*100)/100 + " CLOSE:"
375 + Math.round(price.closePrice*100)/100 + " VOLUME:"
401 x:canvas.width/2 - 100
403 width:childrenRect.width
404 height: childrenRect.height
407 property string text;
416 text:"Run this chart"
418 x:canvas.width/2 - 50
421 if (canvas.running) {
422 canvas.running = false;
423 canvas.frames = canvas.first;
424 canvas.requestPaint();
425 text = "Run this chart";
426 comment.text = stockModel.description;
428 text = " Stop running ";
437 anchors.left : runButton.right
438 anchors.leftMargin : 20
441 stockList.opacity = 1;
448 var current = pinch.center;
449 var scale = pinch.scale;
450 console.log("center:" + pinch.center + " scale:" + pinch.scale);
451 //canvas.requestPaint();
459 if (stockModel.dataCycle == StockModel.Daily)
460 stockModel.dataCycle = StockModel.Weekly;
461 else if (stockModel.dataCycle == StockModel.Weekly)
462 stockModel.dataCycle = StockModel.Monthly;
464 stockModel.dataCycle = StockModel.Daily;
468 if (mouse.modifiers & Qt.ControlModifier) {
469 if (canvas.mouseX == 0 && canvas.mouseY == 0) {
470 canvas.mouseX = mouse.x;
471 canvas.mouseY = mouse.y;
474 var w = (view.width/view.count)*canvas.scaleX;
476 //canvas.movedY += Math.round((canvas.mousePressedY - mouse.y)/2);
478 var movedX = Math.round((canvas.mousePressedX - mouse.x)/w);
479 if (movedX != 0 || canvas.movedY != 0) {
480 if (canvas.first + movedX >= 0 && canvas.last + movedX < view.count) {
481 canvas.first += movedX;
482 canvas.last += movedX;
484 canvas.requestPaint();
490 canvas.mousePressedX = mouse.x;
491 canvas.mousePressedY = mouse.y;
495 if (mouse.modifiers & Qt.ControlModifier) {
496 var sx = mouse.x - canvas.mouseX;
497 var sy = canvas.mouseY - mouse.y;
499 if (Math.abs(sx) < 50) sx = 0;
500 if (Math.abs(sy) < 50) sy = 0;
503 canvas.scaleX *= sx/100 +1;
505 canvas.scaleX *= 1/(-sx/100 + 1);
508 canvas.scaleY *= sy/100 +1;
510 canvas.scaleY *= 1/(-sy/100 + 1);
512 if (canvas.scaleX < 1)
515 //console.log("scaleX:" + canvas.scaleX + ", scaleY:" + canvas.scaleY);
517 canvas.first = Math.round(view.currentIndex - view.currentIndex / canvas.scaleX);
518 canvas.last = Math.round(view.currentIndex + (view.count - view.currentIndex) / canvas.scaleX);
522 canvas.mousePressedX = 0;
523 canvas.mousePressedY = 0;
524 canvas.requestPaint();
529 function runChart() {
530 canvas.running = true;
534 function showPriceAt(x) {
535 var w = (view.width/view.count)*canvas.scaleX;
536 //header.updateCurrent(stockModel.stockPriceAtIndex(canvas.first + Math.round(x/w)));
537 //console.log("x:" + x + " w:" + w + " index:" + (canvas.first + Math.round(x/w)));
540 function drawPrice(ctx, from, to, color, price, points, highest)
542 ctx.globalAlpha = 0.7;
543 ctx.strokeStyle = color;
548 priceAxis.text = "price:" + Math.round(highest);
549 ctx.font = "bold 12px sans-serif";
551 ctx.strokeText("price", 25, 25);
552 for (var j = 1; j < 30; j++) {
553 var val = (highest * j) / 30;
554 val = canvas.height * (1 - val/highest);
567 ctx.lineTo(10, canvas.height);
571 var w = canvas.width/points.length;
572 var end = canvas.running? canvas.frames - canvas.first :points.length;
573 for (var i = 0; i < end; i++) {
575 var y = points[i][price];
578 y = canvas.height * (1 - y/highest);
580 ctx.moveTo(x+w/2, y);
582 ctx.lineTo(x+w/2, y);
588 function drawKLine(ctx, from, to, points, highest)
590 ctx.globalAlpha = 0.4;
592 var end = canvas.running? canvas.frames - canvas.first :points.length;
593 for (var i = 0; i < end; i++) {
595 var open = canvas.height * (1 - points[i].open/highest) - canvas.movedY;
596 var close = canvas.height * (1 - points[i].close/highest) - canvas.movedY;
597 var high = canvas.height * (1 - points[i].high/highest) - canvas.movedY;
598 var low = canvas.height * (1 - points[i].low/highest) - canvas.movedY;
602 ctx.fillStyle = Qt.rgba(1, 0, 0, 1);
603 ctx.strokeStyle = Qt.rgba(1, 0, 0, 1);
607 ctx.fillStyle = Qt.rgba(0, 1, 0, 1);
608 ctx.strokeStyle = Qt.rgba(0, 1, 0, 1);
614 w1 = canvas.width/points.length;
615 w2 = w1 > 10 ? w1/2 : w1;
617 ctx.fillRect(x + (w1 - w2)/2, top, w2, bottom - top);
619 ctx.moveTo(x+w1/2, high);
620 ctx.lineTo(x+w1/2, low);
627 function drawVolume(ctx, from, to, color, price, points, highest)
629 ctx.fillStyle = color;
630 ctx.globalAlpha = 0.6;
631 ctx.strokeStyle = Qt.rgba(0.8, 0.8, 0.8, 1);
635 volumeAxis.text = "volume:" + Math.round(highest/(1000*100)) + "M";
636 for (var j = 1; j < 30; j++) {
637 var val = (highest * j) / 30;
638 val = canvas.height * (1 - val/highest);
641 ctx.moveTo(canvas.width - 15, val);
643 ctx.moveTo(canvas.width - 20, val);
644 ctx.lineTo(canvas.width - 10, val);
649 ctx.moveTo(canvas.width - 10, 0);
650 ctx.lineTo(canvas.width - 10, canvas.height);
653 var end = canvas.running? canvas.frames - canvas.first :points.length;
654 for (var i = 0; i < end; i++) {
656 var y = points[i][price];
657 y = canvas.height * (1 - y/highest);
658 ctx.fillRect(x, y, canvas.width/points.length, canvas.height - y);
663 if (canvas.running) {
664 if (frames >= last) {
665 canvas.running = false;
666 canvas.frames = first;
667 runButton.text = "Run this chart";
668 comment.text = stockModel.description;
671 frames += Math.round(view.count / 100);
672 if (frames > last) frames = last;
673 var price = stockModel.stockPriceAtIndex(frames);
675 comment.updateCurrent(price);
684 if (view.currentIndex <= 0)
686 if (last >= view.count)
687 last = view.count - 1;
689 //console.log("painting... first:" + first + ", last:" + last + " current:" + view.currentIndex);
690 var ctx = canvas.getContext("2d");
693 ctx.globalCompositeOperation = "source-over";
695 ctx.lineJoin = "round";
696 ctx.fillStyle = "rgba(0,0,0,0)";
698 ctx.fillRect(0, 0, canvas.width, canvas.height);
702 var highestPrice = 500/canvas.scaleY;
703 var highestValume = 600 * 1000 * 1000/canvas.scaleY;
705 for (var i = 0; i <= last - first; i++) {
706 var price = stockModel.stockPriceAtIndex(i + first);
708 x: i*canvas.width/(last-first+1),
709 open: price.openPrice,
710 close: price.closePrice,
711 high:price.highPrice,
717 drawPrice(ctx, first, last, Qt.rgba(1, 0, 0, 1),"high", points, highestPrice);
718 drawPrice(ctx, first, last, Qt.rgba(0, 1, 0, 1),"low", points, highestPrice);
719 drawPrice(ctx, first, last, Qt.rgba(0, 0, 1, 1),"open", points, highestPrice);
720 drawPrice(ctx, first, last, Qt.rgba(0.5, 0.5, 0.5, 1),"close", points, highestPrice);
721 drawVolume(ctx, first, last, Qt.rgba(0.3, 0.5, 0.7, 1),"volume", points, highestValume);
722 drawKLine(ctx, first, last, points, highestPrice);