In this section, we leave our clock application for a while and start another one: a weather forecast application. This section focuses on handling data. Our previous code kept data in properties and in JavaScript variables. This is only sufficient for small and simple applications. Sooner or later you will need to deal with larger sets of data.
Qt Quick implements the known model-view architecture and provides a handy set of APIs for this. There is a selection of models which keep and, if needed, acquire data. View elements read model items and render each of them with the help of a delegate in a specific way. For example, as a grid or as a list.
Qt Quick models are very simple since they are based on the concept of lists. The three kinds of models that are used the most are:
See the Models and Data Handling section in the “QML Elements” [http://qt-project.org/doc/qt-4.8/qdeclarativeelements.html] article for a full list of model related items. There are also some advanced approaches which are discussed in the “QML Data Models” [http://qt-project.org/doc/qt-4.8/qdeclarativemodels.html] article in Qt Documentation.
We are going to use XmlListModel [http://qt-project.org/doc/qt-4.8/qml-xmllistmodel.html] and take a look at a few examples where an int and an array are used as models.
Our weather forecast application uses Google weather APIs to get the data.
Note
Google weather APIs are not announced as a regular internet service yet.
With these APIs, you can make a query on the web and receive weather data in XML as a response. As this is a very common way of data provisioning, Qt Quick provides a dedicated model for it: XmlListModel [http://qt-project.org/doc/qt-4.8/qml-xmllistmodel.html].
XmlListModel [http://qt-project.org/doc/qt-4.8/qml-xmllistmodel.html] uses XPath and XQuery (see this article in Wikipedia [http://en.wikipedia.org/wiki/XPath]) to read the data delivered as XML. XmlListModel [http://qt-project.org/doc/qt-4.8/qml-xmllistmodel.html] uses XmlRole [http://qt-project.org/doc/qt-4.8/qml-xmlrole.html] to create model items for selected XML tree nodes. Let’s see how this works.
The query URL is formatted like this:
http://www.google.com/ig/api?weather=[LOCATION]&hl=[LANGUAGE]
It returns the current weather conditions and a forecast for the next four days. If LOCATION is set as Munich and LANGUAGE is set as English, it looks like this:
http://www.google.com/ig/api?weather=Munich&hl=en
It returns the following XML output:
<?xml version="1.0" ?>
<xml_api_reply version="1">
<weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0">
<forecast_information>
<city data="Munich, Bavaria" />
<postal_code data="Munich" />
<latitude_e6 data="" />
<longitude_e6 data="" />
<forecast_date data="2012-02-22" />
<current_date_time data="1970-01-01 00:00:00 +0000" />
<unit_system data="US" />
</forecast_information>
<current_conditions>
<condition data="Clear" />
<temp_f data="39" />
<temp_c data="4" />
<humidity data="Humidity: 56%" />
<icon data="/ig/images/weather/sunny.gif" />
<wind_condition data="Wind: E at 8 mph" />
</current_conditions>
<forecast_conditions>
<day_of_week data="Wed" />
<low data="27" />
<high data="43" />
<icon data="/ig/images/weather/sunny.gif" />
<condition data="Clear" />
</forecast_conditions>
<forecast_conditions>
<day_of_week data="Thu" />
<low data="36" />
<high data="43" />
<icon data="/ig/images/weather/chance_of_rain.gif" />
<condition data="Chance of Rain" />
</forecast_conditions>
<forecast_conditions>
<day_of_week data="Fri" />
<low data="36" />
<high data="54" />
<icon data="/ig/images/weather/sunny.gif" />
<condition data="Clear" />
</forecast_conditions>
<forecast_conditions>
<day_of_week data="Sat" />
<low data="34" />
<high data="48" />
<icon data="/ig/images/weather/chance_of_rain.gif" />
<condition data="Chance of Rain" />
</forecast_conditions>
</weather>
</xml_api_reply>
A model which queries and processes this data looks like this:
import QtQuick 1.1
Item {
id: root
property string location: "Munich"
property string baseURL: "http://www.google.com"
property string dataURL: "/ig/api?weather="
// some other values: "de", "es", "fi", "fr", "it", "ru"
property string language: "en"
XmlListModel {
id: weatherModelCurrent
source: baseURL + dataURL + location + "&hl=" + language
query: "/xml_api_reply/weather/current_conditions"
XmlRole { name: "condition"; query: "condition/@data/string()" }
XmlRole { name: "temp_f"; query: "temp_f/@data/string()" }
XmlRole { name: "humidity"; query: "humidity/@data/string()" }
XmlRole { name: "icon_url"; query: "icon/@data/string()" }
XmlRole { name: "wind_condition"; query: "wind_condition/@data/string()" }
}
XmlListModel {
id: weatherModelForecast
source: baseURL + dataURL + location + "&hl=" + language
query: "/xml_api_reply/weather/forecast_conditions"
XmlRole { name: "day_of_week"; query: "day_of_week/@data/string()" }
XmlRole { name: "low"; query: "low/@data/string()" }
XmlRole { name: "high"; query: "high/@data/string()" }
XmlRole { name: "icon_url"; query: "icon/@data/string()" }
XmlRole { name: "condition"; query: "condition/@data/string()" }
}
}
If you take a closer look at the code inside the XmlRole [http://qt-project.org/doc/qt-4.8/qml-xmlrole.html] elements, you will notice that they basically create model items with property-value pairs by mapping them to the specified nodes in the XML tree starting at the node specified in query. Like Image [http://qt-project.org/doc/qt-4.8/qml-image.html] and Font [http://qt-project.org/doc/qt-4.8/qml-font.html], XmlListModel [http://qt-project.org/doc/qt-4.8/qml-xmllistmodel.html] provides status and progress properties, which can be used to track the loading progress and catch the errors. Additionally, there is a reload() method which forces the model to query the URL again and load updated data. We will use this later to make sure that the weather forecast is always up-to-date.
Now we need to visualize the weather data collected in our models. There are various ways to do this in Qt Quick. Most visualization elements are inherited from Flickable [http://qt-project.org/doc/qt-4.8/qml-flickalbe.html]:
These elements act like view ports and use delegate elements to draw each model item. Views expect a fixed size to be set via height and width. The content is shown inside that specified area and can be “flicked” (by default, up and down):
import QtQuick 1.1
ListView {
width: 150; height: 50
model: ["one", "two", "three", "four", "five"] // or just a number, e.g 10
delegate: Text { text: "Index: " + model.index + ", Data: " + model.modelData }
}
This is how it looks on the screen:
The best use case for views is when a large number of model items has to be displayed. Views provide a built-in scrolling or flicking functionality to support ergonomic representation of large data sets. There are also some performance reasons for this as views only load items that become visible and not the entire set of them.
Advanced Use of Views
Views provide rich functionality and can be used to create pretty sophisticated UIs. If you are interested, consider reading the The Qt Quick Carousel Tutorial [http://qt-project.org/wiki/Qt_Quick_Carousel]
If you have a small number of model items that has to be placed one after the other in a certain order, it makes more sense to use a Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html]. A Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html] creates specified elements for each item in the model. These elements must be placed on the screen by a positioner, for example, Column [http://qt-project.org/doc/qt-4.8/qml-column.html], Grid [http://qt-project.org/doc/qt-4.8/qml-grid.hrml], and so on. The above example can be modified to use a Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html]:
import QtQuick 1.1
Column {
Repeater {
model: ["one", "two", "three", "four", "five"] // or just a number, e.g 10
Text { text: "Index: " + model.index + ", Data: " + model.modelData }
}
}
This is how it looks on the screen:
Note that all items are now visible even though the size of the containing element Column is not specified. Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html] calculates the size of the elements and Column [http://qt-project.org/doc/qt-4.8/qml-column.html] resizes accordingly. Check the “Presenting Data with Views” article [http://qt-project.org/doc/qt-4.8/qml-views.html] article in Qt Documentation for more details.
We will finish our application by adding two visualization elements, each of which uses its own delegate. We need separate delegates as the current weather conditions data and the forecast data have different structures, which we would like to present in different ways.
But what should be used as visualization elements? It is possible with either a view a or with a Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html]. The weatherModelForecast items are displayed by a GridView [http://qt-project.org/doc/qt-4.8/qml-gridview.html] and it looks like this:
The same items displayed by a Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html] looks like this:
The weatherModelCurrent contains just one item. Due to this, a Repeater [http://qt-project.org/doc/qt-4.8/qml-repeater.html] is sufficient for displaying it and we keep this approach. The complete source code of the application:
(Weather/weather.qml in qt_quick_app_dev_intro_src.zip, see Downloads section)
import QtQuick 1.1
Item {
id: root
property string location: "Munich"
property bool tempInC: true
property string baseURL: "http://www.google.com"
property string dataURL: "/ig/api?weather="
// some other values: "de", "es", "fi", "fr", "it", "ru"
property string language: "en"
width: 300
height: 700
function f2C (tempInF) {
return (5/9*(tempInF - 32)).toFixed(0)
}
XmlListModel {
id: weatherModelCurrent
source: baseURL + dataURL + location + "&hl=" + language
query: "/xml_api_reply/weather/current_conditions"
XmlRole { name: "condition"; query: "condition/@data/string()" }
XmlRole { name: "temp_f"; query: "temp_f/@data/string()" }
XmlRole { name: "humidity"; query: "humidity/@data/string()" }
XmlRole { name: "icon_url"; query: "icon/@data/string()" }
XmlRole { name: "wind_condition"; query: "wind_condition/@data/string()" }
}
Component {
id: currentConditionDelegate
Column {
Text { text: qsTr("Today"); font.bold: true }
Text { text: model.condition }
Image { source: baseURL + model.icon_url }
Text { text: model.temp_f + " F° / " + f2C (model.temp_f) + " C°" }
Text { text: model.humidity }
Text { text: model.wind_condition }
}
}
XmlListModel {
id: weatherModelForecast
source: baseURL + dataURL + location + "&hl=" + language
query: "/xml_api_reply/weather/forecast_conditions"
XmlRole { name: "day_of_week"; query: "day_of_week/@data/string()" }
XmlRole { name: "low"; query: "low/@data/string()" }
XmlRole { name: "high"; query: "high/@data/string()" }
XmlRole { name: "icon_url"; query: "icon/@data/string()" }
XmlRole { name: "condition"; query: "condition/@data/string()" }
}
Component {
id: forecastConditionDelegate
Column {
spacing: 2
Text { text: model.day_of_week; font.bold: true }
Text { text: model.condition }
Image { source: baseURL + model.icon_url }
Text { text: qsTr("Lows: ") +
model.low +
" F° / "
+ f2C (model.low) + " C°"}
Text { text: qsTr("Highs: ") +
model.high +
" F° / " +
f2C (model.high) + " C°"}
}
}
Column {
id: allWeather
anchors.centerIn: parent
anchors.margins: 10
spacing: 10
Repeater {
id: currentReportList
model: weatherModelCurrent
delegate: currentConditionDelegate
}
/* we can use a GridView...*/
GridView {
id: forecastReportList
width: 220
height: 220
cellWidth: 110; cellHeight: 110
model: weatherModelForecast
delegate: forecastConditionDelegate
}
/**/
/* ..a Repeater
Repeater {
id: forecastReportList
model: weatherModelForecast
delegate: forecastConditionDelegate
}
*/
}
MouseArea {
anchors.fill: parent
onClicked: Qt.quit()
}
}
We do not need this feature in our application, but it is important to mention. In the current version, Qt Quick does not provide direct access to the local file system unless you hard-code a name of the file you would like to load.
A FolderListModel C++ plug-in is provided as a lab project in Qt 4.7.4 and higher to provide access to the file system. See “FolderListModel - a C++ model plugin” article [http://qt-project.org/doc/qt-4.8/src-imports-folderlistmodel.html] in Qt Documentation for details. An earlier version of this plugin is used to develop a text editor in a getting started tutorial [http://qt-project.org/doc/qt-4.8/gettingstartedqml.html].
What’s Next?
In the next chapter, we start to combine the clock and the weather forecast features into one application. We make components based on the code we have developed so far and use these components to compose the final application.