From 0a9c296731dd01bcf7d7c700a04917620c0ca6da Mon Sep 17 00:00:00 2001 From: Charles Yin Date: Wed, 13 Jul 2011 17:07:06 +1000 Subject: more on canvas stock chart example. Change-Id: I7e92414d397b8a0bfdd514c9045677cd00422263 Reviewed-on: http://codereview.qt.nokia.com/1627 Reviewed-by: Qt Sanity Bot Reviewed-by: Charles Yin --- .../canvas/stockchart/contents/Stocks.qml | 107 ++++ examples/declarative/canvas/stockchart/main.cpp | 6 +- examples/declarative/canvas/stockchart/model.cpp | 49 +- examples/declarative/canvas/stockchart/model.h | 58 ++- examples/declarative/canvas/stockchart/stock.qml | 579 +++++++++++++++++++-- .../declarative/canvas/stockchart/stockchart.pro | 3 +- .../declarative/canvas/stockchart/stockchart.qrc | 1 + 7 files changed, 732 insertions(+), 71 deletions(-) create mode 100644 examples/declarative/canvas/stockchart/contents/Stocks.qml (limited to 'examples') diff --git a/examples/declarative/canvas/stockchart/contents/Stocks.qml b/examples/declarative/canvas/stockchart/contents/Stocks.qml new file mode 100644 index 0000000000..7593a0ccee --- /dev/null +++ b/examples/declarative/canvas/stockchart/contents/Stocks.qml @@ -0,0 +1,107 @@ +import QtQuick 2.0 + +ListModel { + id:stocks + //Data from : http://en.wikipedia.org/wiki/NASDAQ-100 + + ListElement {name:"Activision Blizzard"; stockId:"ATVI"} + ListElement {name:"Adobe Systems Incorporated"; stockId:"ADBE"} + ListElement {name:"Akamai Technologies, Inc"; stockId:"AKAM"} + ListElement {name:"Alexion Pharmaceuticals"; stockId:"ALXN"} + ListElement {name:"Altera Corporation"; stockId:"ALTR"} + ListElement {name:"Amazon.com, Inc."; stockId:"AMZN"} + ListElement {name:"Amgen Inc."; stockId:"AMGN"} + ListElement {name:"Apollo Group, Inc."; stockId:"APOL"} + ListElement {name:"Apple Inc."; stockId:"AAPL"} + ListElement {name:"Applied Materials, Inc."; stockId:"AMAT"} + ListElement {name:"Autodesk, Inc."; stockId:"ADSK"} + ListElement {name:"Automatic Data Processing, Inc."; stockId:"ADP"} + ListElement {name:"Baidu.com, Inc."; stockId:"BIDU"} + ListElement {name:"Bed Bath & Beyond Inc."; stockId:"BBBY"} + ListElement {name:"Biogen Idec, Inc"; stockId:"BIIB"} + ListElement {name:"BMC Software, Inc."; stockId:"BMC"} + ListElement {name:"Broadcom Corporation"; stockId:"BRCM"} + ListElement {name:"C. H. Robinson Worldwide, Inc."; stockId:"CHRW"} + ListElement {name:"CA, Inc."; stockId:"CA"} + ListElement {name:"Celgene Corporation"; stockId:"CELG"} + ListElement {name:"Cephalon, Inc."; stockId:"CEPH"} + ListElement {name:"Cerner Corporation"; stockId:"CERN"} + ListElement {name:"Check Point Software Technologies Ltd."; stockId:"CHKP"} + ListElement {name:"Cisco Systems, Inc."; stockId:"CSCO"} + ListElement {name:"Citrix Systems, Inc."; stockId:"CTXS"} + ListElement {name:"Cognizant Technology Solutions Corporation"; stockId:"CTSH"} + ListElement {name:"Comcast Corporation"; stockId:"CMCSA"} + ListElement {name:"Costco Wholesale Corporation"; stockId:"COST"} + ListElement {name:"Ctrip.com International, Ltd."; stockId:"CTRP"} + ListElement {name:"Dell Inc."; stockId:"DELL"} + ListElement {name:"DENTSPLY International Inc."; stockId:"XRAY"} + ListElement {name:"DirecTV"; stockId:"DTV"} + ListElement {name:"Dollar Tree, Inc."; stockId:"DLTR"} + ListElement {name:"eBay Inc."; stockId:"EBAY"} + ListElement {name:"Electronic Arts Inc."; stockId:"ERTS"} + ListElement {name:"Expedia, Inc."; stockId:"EXPE"} + ListElement {name:"Expeditors International of Washington, Inc."; stockId:"EXPD"} + ListElement {name:"Express Scripts, Inc."; stockId:"ESRX"} + ListElement {name:"F5 Networks, Inc."; stockId:"FFIV"} + ListElement {name:"Fastenal Company"; stockId:"FAST"} + ListElement {name:"First Solar, Inc."; stockId:"FSLR"} + ListElement {name:"Fiserv, Inc."; stockId:"FISV"} + ListElement {name:"Flextronics International Ltd."; stockId:"FLEX"} + ListElement {name:"FLIR Systems, Inc."; stockId:"FLIR"} + ListElement {name:"Garmin Ltd."; stockId:"GRMN"} + ListElement {name:"Gilead Sciences, Inc."; stockId:"GILD"} + ListElement {name:"Google Inc."; stockId:"GOOG"} + ListElement {name:"Green Mountain Coffee Roasters, Inc."; stockId:"GMCR"} + ListElement {name:"Henry Schein, Inc."; stockId:"HSIC"} + ListElement {name:"Illumina, Inc."; stockId:"ILMN"} + ListElement {name:"Infosys Technologies"; stockId:"INFY"} + ListElement {name:"Intel Corporation"; stockId:"INTC"} + ListElement {name:"Intuit, Inc."; stockId:"INTU"} + ListElement {name:"Intuitive Surgical Inc."; stockId:"ISRG"} + ListElement {name:"Joy Global Inc."; stockId:"JOYG"} + ListElement {name:"KLA Tencor Corporation"; stockId:"KLAC"} + ListElement {name:"Lam Research Corporation"; stockId:"LRCX"} + ListElement {name:"Liberty Media Corporation, Interactive Series A"; stockId:"LINTA"} + ListElement {name:"Life Technologies Corporation"; stockId:"LIFE"} + ListElement {name:"Linear Technology Corporation"; stockId:"LLTC"} + ListElement {name:"Marvell Technology Group, Ltd."; stockId:"MRVL"} + ListElement {name:"Mattel, Inc."; stockId:"MAT"} + ListElement {name:"Maxim Integrated Products"; stockId:"MXIM"} + ListElement {name:"Microchip Technology Incorporated"; stockId:"MCHP"} + ListElement {name:"Micron Technology, Inc."; stockId:"MU"} + ListElement {name:"Microsoft Corporation"; stockId:"MSFT"} + ListElement {name:"Mylan, Inc."; stockId:"MYL"} + ListElement {name:"NetApp, Inc."; stockId:"NTAP"} + ListElement {name:"Netflix, Inc."; stockId:"NFLX"} + ListElement {name:"News Corporation, Ltd."; stockId:"NWSA"} + ListElement {name:"NII Holdings, Inc."; stockId:"NIHD"} + ListElement {name:"NVIDIA Corporation"; stockId:"NVDA"} + ListElement {name:"O'Reilly Automotive, Inc."; stockId:"ORLY"} + ListElement {name:"Oracle Corporation"; stockId:"ORCL"} + ListElement {name:"PACCAR Inc."; stockId:"PCAR"} + ListElement {name:"Paychex, Inc."; stockId:"PAYX"} + ListElement {name:"Priceline.com, Incorporated"; stockId:"PCLN"} + ListElement {name:"Qiagen N.V."; stockId:"QGEN"} + ListElement {name:"QUALCOMM Incorporated"; stockId:"QCOM"} + ListElement {name:"Research in Motion Limited"; stockId:"RIMM"} + ListElement {name:"Ross Stores Inc."; stockId:"ROST"} + ListElement {name:"SanDisk Corporation"; stockId:"SNDK"} + ListElement {name:"Seagate Technology Holdings"; stockId:"STX"} + ListElement {name:"Sears Holdings Corporation"; stockId:"SHLD"} + ListElement {name:"Sigma-Aldrich Corporation"; stockId:"SIAL"} + ListElement {name:"Staples Inc."; stockId:"SPLS"} + ListElement {name:"Starbucks Corporation"; stockId:"SBUX"} + ListElement {name:"Stericycle, Inc"; stockId:"SRCL"} + ListElement {name:"Symantec Corporation"; stockId:"SYMC"} + ListElement {name:"Teva Pharmaceutical Industries Ltd."; stockId:"TEVA"} + ListElement {name:"Urban Outfitters, Inc."; stockId:"URBN"} + ListElement {name:"VeriSign, Inc."; stockId:"VRSN"} + ListElement {name:"Vertex Pharmaceuticals"; stockId:"VRTX"} + ListElement {name:"Virgin Media, Inc."; stockId:"VMED"} + ListElement {name:"Vodafone Group, plc."; stockId:"VOD"} + ListElement {name:"Warner Chilcott, Ltd."; stockId:"WCRX"} + ListElement {name:"Whole Foods Market, Inc."; stockId:"WFM"} + ListElement {name:"Wynn Resorts Ltd."; stockId:"WYNN"} + ListElement {name:"Xilinx, Inc."; stockId:"XLNX"} + ListElement {name:"Yahoo! Inc."; stockId:"YHOO"} +} diff --git a/examples/declarative/canvas/stockchart/main.cpp b/examples/declarative/canvas/stockchart/main.cpp index fd383189b5..e10f287022 100644 --- a/examples/declarative/canvas/stockchart/main.cpp +++ b/examples/declarative/canvas/stockchart/main.cpp @@ -48,12 +48,12 @@ int main(int argc, char ** argv) QApplication app(argc, argv); qmlRegisterType("StockChart", 1, 0, "StockModel"); - + qmlRegisterType("StockChart", 1, 0, "StockPrice"); QSGView view; - view.setResizeMode(QSGView::SizeRootObjectToView); + view.setResizeMode(QSGView::SizeViewToRootObject); view.setSource(QUrl("qrc:stock.qml")); - view.show(); + view.showFullScreen(); view.raise(); return app.exec(); diff --git a/examples/declarative/canvas/stockchart/model.cpp b/examples/declarative/canvas/stockchart/model.cpp index e5187793aa..5c1e13807a 100644 --- a/examples/declarative/canvas/stockchart/model.cpp +++ b/examples/declarative/canvas/stockchart/model.cpp @@ -82,6 +82,15 @@ int StockModel::rowCount(const QModelIndex & parent) const { 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) { @@ -118,6 +127,11 @@ void StockModel::update(QNetworkReply *reply) if (reply) { if (reply->error() == QNetworkReply::NoError) { beginResetModel(); + + foreach (StockPrice* p, _prices) { + p->deleteLater(); + } + _prices.clear(); while (!reply->atEnd()) { @@ -127,14 +141,14 @@ void StockModel::update(QNetworkReply *reply) //data format:Date,Open,High,Low,Close,Volume,Adjusted close price //example: 2011-06-24,6.03,6.04,5.88,5.88,20465200,5.88 if (fields.size() == 7) { - StockPrice price; - price.date = QDate::fromString(fields[0], Qt::ISODate); - price.openPrice = fields[1].toFloat(); - price.highPrice = fields[2].toFloat(); - price.lowPrice = fields[3].toFloat(); - price.closePrice = fields[4].toFloat(); - price.volume = fields[5].toInt(); - price.adjustedPrice = fields[6].toFloat(); + 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); } } @@ -144,6 +158,7 @@ void StockModel::update(QNetworkReply *reply) } reply->deleteLater(); endResetModel(); + emit dataChanged(QModelIndex(), QModelIndex()); } } @@ -151,23 +166,23 @@ 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()]; + const StockPrice* price = _prices[index.row()]; if (role == StockModel::DateRole) - return price.date; + return price->date(); else if (role == StockModel::OpenPriceRole) - return price.openPrice; + return price->openPrice(); else if (role == StockModel::ClosePriceRole) - return price.closePrice; + return price->closePrice(); else if (role == StockModel::HighPriceRole) - return price.highPrice; + return price->highPrice(); else if (role == StockModel::LowPriceRole) - return price.lowPrice; + return price->lowPrice(); else if (role == StockModel::AdjustedPriceRole) - return price.adjustedPrice; + return price->adjustedPrice(); else if (role == StockModel::VolumeRole) - return price.volume; + return price->volume(); else if (role == StockModel::SectionRole) - return price.date.year(); + return price->date().year(); return QVariant(); } diff --git a/examples/declarative/canvas/stockchart/model.h b/examples/declarative/canvas/stockchart/model.h index fde99de223..c7a0235021 100644 --- a/examples/declarative/canvas/stockchart/model.h +++ b/examples/declarative/canvas/stockchart/model.h @@ -40,16 +40,54 @@ #include #include -struct StockPrice +class StockPrice : public QObject { - QDate date; - qreal openPrice; - qreal closePrice; - qreal highPrice; - qreal lowPrice; - qint32 volume; - qreal adjustedPrice; + 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 @@ -107,13 +145,13 @@ signals: public slots: void requestData(); - + StockPrice* stockPriceAtIndex(int idx) const; private slots: void doRequest(); void update(QNetworkReply* reply); private: QString dataCycleString() const; - QList _prices; + QList _prices; QString _stockName; QDate _startDate; QDate _endDate; diff --git a/examples/declarative/canvas/stockchart/stock.qml b/examples/declarative/canvas/stockchart/stock.qml index 15f6064fef..4117932c0a 100644 --- a/examples/declarative/canvas/stockchart/stock.qml +++ b/examples/declarative/canvas/stockchart/stock.qml @@ -39,74 +39,573 @@ ****************************************************************************/ import QtQuick 2.0 import StockChart 1.0 +import "contents" Rectangle { id:container - width: 700; height: 800 + width: 1920; height: 1440 StockModel { id:stockModel - stockName: "NOK" - } - // Define a highlight with customised movement between items. - Component { - id: highlightBar - Rectangle { - width: 1000; height: 50 - color: "#FFFF88" - y: view.currentItem.y; - Behavior on y { SpringAnimation { spring: 2; damping: 0.1 } } + dataCycle: StockModel.Daily + onDataChanged: { + if (view.viewType == "chart") { + canvas.requestPaint(); + } } + property string description:""; } - // The delegate for each section header - Component { - id: sectionHeading - Rectangle { - width: container.width - height: childrenRect.height - color: "lightsteelblue" - Text { - text:stockModel.stockName + "(" + section + ")" + "Open\t High\t Low\t Close\t Volume\t Adjusted" - horizontalAlignment:Text.AlignHCenter - font.bold: true - font.underline:true + Stocks {id:stocks} + + Rectangle { + id: header + width: parent.width + height: childrenRect.height + color: "steelblue" + + Text { + id:t + font.pointSize:15 + horizontalAlignment:Text.AlignHCenter + font.bold: true + font.underline:true + } + + function updateCurrent(price) + { + if (price !== undefined) { + t.text =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 + " ADJ:" + + Math.round(price.adjustedPrice*100)/100; } } } + ListView { - id:view + id:stockList width: parent.width - anchors.fill: parent - highlightFollowsCurrentItem: false + height: container.height + anchors.bottom: container.bottom focus: true keyNavigationWraps: true - opacity: 0.8 - highlightRangeMode: ListView.StrictlyEnforceRange spacing:1 + opacity: 1 + model: 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"; + } + 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 - highlight: Rectangle { color: "lightsteelblue" } - section.property: "year" - section.criteria: ViewSection.FullString - section.delegate: sectionHeading - delegate: Text { text: Qt.formatDate(date, "yyyy-MM-dd") + "\t " + Math.round(openPrice*100)/100 + "\t " + Math.round(highPrice*100)/100 + "\t " + Math.round(lowPrice*100)/100 + "\t " + Math.round(closePrice*100)/100 + "\t " + volume + "\t " + Math.round(adjustedPrice*100)/100 } + 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); - property bool beginning:false - Component.onCompleted: { - view.positionViewAtBeginning(); + 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") { +// container.width = 700; +// container.height = 800; + 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; + //view.height = 750; + // comment.opacity = 0; + + } else if (viewType == "chart") { +// container.width = 1600; +// container.height = 800; + view.orientation = ListView.Horizontal; + view.delegate = chartDelegate; + //comment.opacity = 0.6; + var dataCycle = "Daily"; + + if (stockModel.dataCycle === StockModel.Weekly) + dataCycle = "Weekly"; + else if (stockModel.dataCycle === StockModel.Monthly) + dataCycle = "Monthly"; + + comment.text = stockModel.description + "\n" + + Qt.formatDate(stockModel.startDate, "yyyy-MM-dd") + " - " + + Qt.formatDate(stockModel.endDate, "yyyy-MM-dd") + " " + dataCycle; + + view.opacity = 1; + view.height = 30 + + canvas.opacity = 1; + 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.beginning) { - view.positionViewAtBeginning(); + if (view.viewType == "list") + view.viewType = "chart"; + else + view.viewType = "list"; + } + } + } + + + + Canvas { + id:canvas + anchors.top : header.bottom + anchors.bottom : view.top + width:container.width; + opacity:0 + + 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 { +// id:priceAxis +// x:25 +// y:25 +// font.pointSize: 15 +// color:"yellow" +// opacity: 0.7 +// focus: false +// } +// Text { +// id:volumeAxis +// x:canvas.width - 25 +// y:25 +// font.pointSize: 15 +// color:"yellow" +// opacity: 0.7 +// } + + 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 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; + for (var i = 0; i < points.length; 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 { - view.positionViewAtEnd(); + ctx.lineTo(x+w/2, y); } - view.beginning = !view.beginning; } + ctx.stroke(); + } + + function drawKLine(ctx, from, to, points, highest) + { + ctx.globalAlpha = 0.4; + ctx.lineWidth = 2; + for (var i = 0; i < points.length; 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); + 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(); + + for (var i = 0; i < points.length; 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); + } + } + + 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.reset(); + + 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); } } } diff --git a/examples/declarative/canvas/stockchart/stockchart.pro b/examples/declarative/canvas/stockchart/stockchart.pro index 84f2a286ab..7101c4c14d 100644 --- a/examples/declarative/canvas/stockchart/stockchart.pro +++ b/examples/declarative/canvas/stockchart/stockchart.pro @@ -11,4 +11,5 @@ macx: CONFIG -= app_bundle CONFIG += console OTHER_FILES += \ - stock.qml + stock.qml \ + contents/Stocks.qml diff --git a/examples/declarative/canvas/stockchart/stockchart.qrc b/examples/declarative/canvas/stockchart/stockchart.qrc index a67f5d6437..68702ab7a1 100644 --- a/examples/declarative/canvas/stockchart/stockchart.qrc +++ b/examples/declarative/canvas/stockchart/stockchart.qrc @@ -1,6 +1,7 @@ stock.qml + contents/Stocks.qml -- cgit v1.2.3