From bad1d525be80691b60e5bd2070bc6c05dd2623b0 2012-05-10 11:35:33 From: Tero Ahola Date: 2012-05-10 11:35:33 Subject: [PATCH] Initial support for x-axis labels in QML api --- diff --git a/demos/qmlchart/qml/qmlchart/View3.qml b/demos/qmlchart/qml/qmlchart/View3.qml index c1a2b9c..857ad3a 100644 --- a/demos/qmlchart/qml/qmlchart/View3.qml +++ b/demos/qmlchart/qml/qmlchart/View3.qml @@ -29,6 +29,8 @@ Rectangle { anchors.fill: parent theme: ChartView.ChartThemeHighContrast legend: ChartView.LegendTop + axisXLabels: ["0", "2000", "1", "2001", "2", "2002", "3", "2003", "4", "2004", "5", "2005", + "6", "2006", "7", "2007", "8", "2008", "9", "2009", "10", "2010", "11", "2011"] AreaSeries { name: "Swedish" diff --git a/demos/qmlweather/main.cpp b/demos/qmlweather/main.cpp index ee23454..6141bf2 100644 --- a/demos/qmlweather/main.cpp +++ b/demos/qmlweather/main.cpp @@ -19,6 +19,8 @@ ****************************************************************************/ #include +#include +#include #include "qmlapplicationviewer.h" Q_DECL_EXPORT int main(int argc, char *argv[]) @@ -27,8 +29,15 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) QmlApplicationViewer viewer; viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto); + QString appKey; + if (argc > 1) { + appKey = argv[1]; + qDebug() << "App key for worldweatheronline.com:" << appKey; + } else { + qWarning() << "No app key for worldweatheronline.com given. Using static test data instead of live data."; + } + viewer.rootContext()->setContextProperty("weatherAppKey", appKey); viewer.setSource(QUrl("qrc:/qml/qmlweather/main.qml")); viewer.showExpanded(); - return app->exec(); } diff --git a/demos/qmlweather/qml/qmlweather/main.qml b/demos/qmlweather/qml/qmlweather/main.qml index 954f041..9636e02 100644 --- a/demos/qmlweather/qml/qmlweather/main.qml +++ b/demos/qmlweather/qml/qmlweather/main.qml @@ -31,23 +31,21 @@ Rectangle { anchors.bottom: weatherImageRow.top anchors.left: parent.left anchors.right: parent.right + title: "Weather forecast" axisX.min: 0 - axisX.max: 6 + axisX.max: 4 axisY.min: 0 - axisY.max: 50 - - // TODO: implement categories -// AxisCategory { axis: AxisCategory.AxisX; value: 0.0; label: "" } -// AxisCategory { axis: AxisCategory.AxisX; value: 1.0; label: "Today" } -// AxisCategory { axis: AxisCategory.AxisX; value: 2.0; label: "Tomorrow" } -// AxisCategory { axis: AxisCategory.AxisX; value: 3.0; label: "Day after tomorrow" } + axisY.max: 0 + legend: ChartView.LegendTop LineSeries { model: maxModel + name: "Max. temperature" } LineSeries { model: minModel + name: "Min. temperature" } // TODO: use a single base model with mappings instead of two separate xy models @@ -80,33 +78,46 @@ Rectangle { } Component.onCompleted: { - // TODO: use live data instead of hard coded example data - // in case an application key was defined for this demo app -// var xhr = new XMLHttpRequest; -// var appKey = ""; -// xhr.open("GET", "http://free.worldweatheronline.com/feed/weather.ashx?q=Jyv%c3%a4skyl%c3%a4,Finland&format=json&num_of_days=5&key=" + appKey"); -// xhr.onreadystatechange = function() { -// if (xhr.readyState == XMLHttpRequest.DONE) { -// var a = JSON.parse(xhr.responseText); -// console.log("a: " + a); -// console.log("response: " + xhr.responseText); -// for (var b in a) { -// var o = a[b]; -// console.log("o: " + o); -//// model.append({id: o.id, name: o.name, duration: o.duration}); -// } -// } -// } -// xhr.send(); - - var responseText = "{ \"data\": { \"current_condition\": [ {\"cloudcover\": \"10\", \"humidity\": \"61\", \"observation_time\": \"06:26 AM\", \"precipMM\": \"0.0\", \"pressure\": \"1022\", \"temp_C\": \"6\", \"temp_F\": \"43\", \"visibility\": \"10\", \"weatherCode\": \"113\", \"weatherDesc\": [ {\"value\": \"Sunny\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png\" } ], \"winddir16Point\": \"SE\", \"winddirDegree\": \"140\", \"windspeedKmph\": \"7\", \"windspeedMiles\": \"4\" } ], \"request\": [ {\"query\": \"Jyvaskyla, Finland\", \"type\": \"City\" } ], \"weather\": [ {\"date\": \"2012-05-09\", \"precipMM\": \"0.4\", \"tempMaxC\": \"14\", \"tempMaxF\": \"57\", \"tempMinC\": \"7\", \"tempMinF\": \"45\", \"weatherCode\": \"116\", \"weatherDesc\": [ {\"value\": \"Partly Cloudy\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0002_sunny_intervals.png\" } ], \"winddir16Point\": \"S\", \"winddirDegree\": \"179\", \"winddirection\": \"S\", \"windspeedKmph\": \"20\", \"windspeedMiles\": \"12\" }, {\"date\": \"2012-05-10\", \"precipMM\": \"2.4\", \"tempMaxC\": \"13\", \"tempMaxF\": \"55\", \"tempMinC\": \"8\", \"tempMinF\": \"46\", \"weatherCode\": \"266\", \"weatherDesc\": [ {\"value\": \"Light drizzle\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0017_cloudy_with_light_rain.png\" } ], \"winddir16Point\": \"SW\", \"winddirDegree\": \"219\", \"winddirection\": \"SW\", \"windspeedKmph\": \"21\", \"windspeedMiles\": \"13\" }, {\"date\": \"2012-05-11\", \"precipMM\": \"11.1\", \"tempMaxC\": \"15\", \"tempMaxF\": \"59\", \"tempMinC\": \"7\", \"tempMinF\": \"44\", \"weatherCode\": \"266\", \"weatherDesc\": [ {\"value\": \"Light drizzle\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0017_cloudy_with_light_rain.png\" } ], \"winddir16Point\": \"SSW\", \"winddirDegree\": \"200\", \"winddirection\": \"SSW\", \"windspeedKmph\": \"20\", \"windspeedMiles\": \"12\" }, {\"date\": \"2012-05-12\", \"precipMM\": \"2.8\", \"tempMaxC\": \"7\", \"tempMaxF\": \"44\", \"tempMinC\": \"2\", \"tempMinF\": \"35\", \"weatherCode\": \"317\", \"weatherDesc\": [ {\"value\": \"Light sleet\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0021_cloudy_with_sleet.png\" } ], \"winddir16Point\": \"NW\", \"winddirDegree\": \"311\", \"winddirection\": \"NW\", \"windspeedKmph\": \"24\", \"windspeedMiles\": \"15\" }, {\"date\": \"2012-05-13\", \"precipMM\": \"0.4\", \"tempMaxC\": \"6\", \"tempMaxF\": \"42\", \"tempMinC\": \"2\", \"tempMinF\": \"35\", \"weatherCode\": \"116\", \"weatherDesc\": [ {\"value\": \"Partly Cloudy\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0002_sunny_intervals.png\" } ], \"winddir16Point\": \"WNW\", \"winddirDegree\": \"281\", \"winddirection\": \"WNW\", \"windspeedKmph\": \"21\", \"windspeedMiles\": \"13\" } ] }}"; - var a = JSON.parse(responseText); - - for (var i in a.data.weather) { - var weatherObj = a.data.weather[i]; - maxModel.append([Number(i) + 1, weatherObj.tempMaxC]); - minModel.append([Number(i) + 1, weatherObj.tempMinC]); + if (weatherAppKey != "") { + var xhr = new XMLHttpRequest; + xhr.open("GET", "http://free.worldweatheronline.com/feed/weather.ashx?q=Jyv%c3%a4skyl%c3%a4,Finland&format=json&num_of_days=5&key=" + weatherAppKey); + xhr.onreadystatechange = function() { + if (xhr.readyState == XMLHttpRequest.DONE) { + var a = JSON.parse(xhr.responseText); + parseWeatherData(a); + } + } + xhr.send(); + } else { + // No app key for worldweatheronline.com given by the user -> use static data + var responseText = "{ \"data\": { \"current_condition\": [ {\"cloudcover\": \"10\", \"humidity\": \"61\", \"observation_time\": \"06:26 AM\", \"precipMM\": \"0.0\", \"pressure\": \"1022\", \"temp_C\": \"6\", \"temp_F\": \"43\", \"visibility\": \"10\", \"weatherCode\": \"113\", \"weatherDesc\": [ {\"value\": \"Sunny\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png\" } ], \"winddir16Point\": \"SE\", \"winddirDegree\": \"140\", \"windspeedKmph\": \"7\", \"windspeedMiles\": \"4\" } ], \"request\": [ {\"query\": \"Jyvaskyla, Finland\", \"type\": \"City\" } ], \"weather\": [ {\"date\": \"2012-05-09\", \"precipMM\": \"0.4\", \"tempMaxC\": \"14\", \"tempMaxF\": \"57\", \"tempMinC\": \"7\", \"tempMinF\": \"45\", \"weatherCode\": \"116\", \"weatherDesc\": [ {\"value\": \"Partly Cloudy\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0002_sunny_intervals.png\" } ], \"winddir16Point\": \"S\", \"winddirDegree\": \"179\", \"winddirection\": \"S\", \"windspeedKmph\": \"20\", \"windspeedMiles\": \"12\" }, {\"date\": \"2012-05-10\", \"precipMM\": \"2.4\", \"tempMaxC\": \"13\", \"tempMaxF\": \"55\", \"tempMinC\": \"8\", \"tempMinF\": \"46\", \"weatherCode\": \"266\", \"weatherDesc\": [ {\"value\": \"Light drizzle\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0017_cloudy_with_light_rain.png\" } ], \"winddir16Point\": \"SW\", \"winddirDegree\": \"219\", \"winddirection\": \"SW\", \"windspeedKmph\": \"21\", \"windspeedMiles\": \"13\" }, {\"date\": \"2012-05-11\", \"precipMM\": \"11.1\", \"tempMaxC\": \"15\", \"tempMaxF\": \"59\", \"tempMinC\": \"7\", \"tempMinF\": \"44\", \"weatherCode\": \"266\", \"weatherDesc\": [ {\"value\": \"Light drizzle\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0017_cloudy_with_light_rain.png\" } ], \"winddir16Point\": \"SSW\", \"winddirDegree\": \"200\", \"winddirection\": \"SSW\", \"windspeedKmph\": \"20\", \"windspeedMiles\": \"12\" }, {\"date\": \"2012-05-12\", \"precipMM\": \"2.8\", \"tempMaxC\": \"7\", \"tempMaxF\": \"44\", \"tempMinC\": \"2\", \"tempMinF\": \"35\", \"weatherCode\": \"317\", \"weatherDesc\": [ {\"value\": \"Light sleet\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0021_cloudy_with_sleet.png\" } ], \"winddir16Point\": \"NW\", \"winddirDegree\": \"311\", \"winddirection\": \"NW\", \"windspeedKmph\": \"24\", \"windspeedMiles\": \"15\" }, {\"date\": \"2012-05-13\", \"precipMM\": \"0.4\", \"tempMaxC\": \"6\", \"tempMaxF\": \"42\", \"tempMinC\": \"2\", \"tempMinF\": \"35\", \"weatherCode\": \"116\", \"weatherDesc\": [ {\"value\": \"Partly Cloudy\" } ], \"weatherIconUrl\": [ {\"value\": \"http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0002_sunny_intervals.png\" } ], \"winddir16Point\": \"WNW\", \"winddirDegree\": \"281\", \"winddirection\": \"WNW\", \"windspeedKmph\": \"21\", \"windspeedMiles\": \"13\" } ] }}"; + var a = JSON.parse(responseText); + parseWeatherData(a); + } + } + + function parseWeatherData(weatherData) { + for (var i in weatherData.data.weather) { + var weatherObj = weatherData.data.weather[i]; + + // Add min and max temperature values into models used by series + maxModel.append([Number(i), weatherObj.tempMaxC]); + minModel.append([Number(i), weatherObj.tempMinC]); weatherImageModel.append({"imageSource":weatherObj.weatherIconUrl[0].value}); + + // Update scale of the chart + while (chartView.axisY.min >= Number(weatherObj.tempMinC)) + chartView.axisY.min = chartView.axisY.min - 10; + while (chartView.axisY.max <= Number(weatherObj.tempMaxC)) + chartView.axisY.max = chartView.axisY.max + 10; + + // Set the x-axis labels to the dates of the forecast + // TODO: the API could probably be more intuitive.. + // Now it takes an array of strings: chartView.axisXLabels = ["value1", "label1", "value2", "label2", ...] + var xLabels = chartView.axisXLabels; + xLabels[Number(i) * 2] = i; + xLabels[(Number(i) * 2) + 1] = weatherObj.date.substring(5, 10); + chartView.axisXLabels = xLabels; } } diff --git a/qmlplugin/declarativechart.cpp b/qmlplugin/declarativechart.cpp index 657dde9..af8b3f0 100644 --- a/qmlplugin/declarativechart.cpp +++ b/qmlplugin/declarativechart.cpp @@ -143,6 +143,31 @@ QAxis *DeclarativeChart::axisY() return m_chart->axisY(); } +QVariantList DeclarativeChart::axisXLabels() +{ + QVariantList labels; + foreach (qreal value, m_chart->axisX()->categories()->values()) { +// qDebug() << "Label for" << value << "is" << m_chart->axisX()->categories()->label(value); + labels.append(value); + labels.append(m_chart->axisX()->categories()->label(value)); + } + return labels; +} + +void DeclarativeChart::setAxisXLabels(QVariantList list) +{ + QVariant value(QVariant::Invalid); + foreach (QVariant element, list) { + if (value.isValid() && element.type() == QVariant::String) { + m_chart->axisX()->categories()->insert(value.toDouble(), element.toString()); + value = QVariant(QVariant::Invalid); + } else { + if (element.canConvert(QVariant::Double)) + value = element; + } + } +} + #include "moc_declarativechart.cpp" QTCOMMERCIALCHART_END_NAMESPACE diff --git a/qmlplugin/declarativechart.h b/qmlplugin/declarativechart.h index deede98..b122074 100644 --- a/qmlplugin/declarativechart.h +++ b/qmlplugin/declarativechart.h @@ -40,6 +40,8 @@ class DeclarativeChart : public QDeclarativeItem Q_PROPERTY(ChartLegend legend READ legend WRITE setLegend) Q_PROPERTY(QAxis *axisX READ axisX) Q_PROPERTY(QAxis *axisY READ axisY) + // TODO: how to define axis labels? This is not very convenient + Q_PROPERTY(QVariantList axisXLabels READ axisXLabels WRITE setAxisXLabels) Q_ENUMS(ChartLegend) public: @@ -72,6 +74,8 @@ public: ChartLegend legend(); QAxis *axisX(); QAxis *axisY(); + QVariantList axisXLabels(); + void setAxisXLabels(QVariantList list); public: // Extending QChart with DeclarativeChart is not possible because QObject does not support