×
Namespaces

Variants
Actions

Ski Resorts Webcams

From Nokia Developer Wiki
Jump to: navigation, search

Ski Resorts Webcams is a simple Qt Quick application made for viewing ski resorts webcams images. This article explains how the Ski Resorts Webcams app was created and the main aspects of its design.

Article Metadata
Tested with
Devices(s): N8, 808 PureView, 701, 700, C7, 500, E7...
Compatibility
Platform(s): Any Symbian^3 (PR1, Anna, Belle) + Meego Harmattan devices, Qt 4.7.4, Qt Quick 1.1
Article
Created: Pipould (09 Mar 2012)
Last edited: hamishwillee (11 Oct 2012)

Contents

Introduction

Iconstationswebcams.png
Ski Resorts Webcams is a simple Qt Quick application made for viewing ski resorts webcams images.

The main goal when creating this app was to create a "internet image viewer" with a data file independent of the Qt App. The application is made of various parts who provide functions to the user.

Note.pngNote: I wrote this application from scratch (this is my first Qt Application ever), using only Qml and Qt Quick components. It took me a weekend to write the first version and I am now focusing on improving it as much as I can. I did not "learn" Qt, I mainly used basic examples and my knowledge of programing for this app. I hope this wiki will give you some nice ideas about how to use Qt Quick in a clever and simple way.

When browsing on different lists of examples I saw a very interesting "Windows Phone 7" style app.I tried to reproduce the same and adapted it to reach the result I wanted. The app if fully made of QML and Javascript. Each part of this Wiki shows an overview of the process I followed in order to create a part of the app. Please watch the video (inside the video part) in order to have a real overview of how I used Qt Quick components to make the app !

A word about design

When I first saw Symbian/Nokia Belle, I was really seduced by the design. Coming from an old N80, I owned several devices and very often the design of each application was very different. When Qt Quick was first released, I was instantly interested by this new possibility. Making a bet with a friend, I told him it was possible to create a "WP7" like application under Qt, make it smooth and fast for Symbian devices. I ended up by making the simplest application possible, without many sub-menus and complex navigation. The goal was to make a fast, simple and nice looking application, in the respect of Nokia Belle UI style. You can see the result the video.

Note.pngNote: The default font of the app is Nokia Pure but it will work without any problem with Nokia Sans. If you have custom fonts (such as the one in the screenshots of this wiki), I am not responsible in case of bugs with my app.

The core

The core of the app is made of one VisualItemModel who holds 3 rectangles and some intel to switch between them. Each of the 3 rectangles are composed of small rectangles which contains all the items such as the text and so on. There're lists made of a VisualItemModel with ListModel as model combined with a ListView in order to show the items. Here is the entire code of the first "Rectangle" (the countries menu):

VisualItemModel {
id: itemModel
// Countries list
Rectangle {
id: stationrect
width: main.width*2; height: view.height
color: "#00000000"
visible:true
 
Text {
id: headingText
anchors.leftMargin: 10
clip: false
anchors.fill: parent
wrapMode: Text.NoWrap
font { family:fontName; pointSize: 31}
smooth: true
style: Text.Raised
opacity:1
color: "white"
z:2
}
 
Text {id: countriesText; color: textColor; text: "Countries"
font {family:fontName; bold: false; pointSize: 20}
smooth: true
style: Text.Raised
anchors.leftMargin: 10
anchors {top: parent.top; left:parent.left; topMargin:110}
opacity: 0
z:2}
 
Rectangle {
id: rectangle1
x: 0
width: main.width*2;
height: 180
color: "#000"
opacity: 1
z:0
Image{
height: main.height
opacity: 0.400
source: "backgroundtop.jpg"
anchors.fill:parent
}
}
 
Rectangle {
z:-2
color: "#00000000"
 
anchors{ left:parent.left; right:parent.right; bottom:parent.bottom; top:parent.top
leftMargin: 10; rightMargin:10; bottomMargin:10; topMargin:200 }
 
VisualDataModel {
id: countryItemModel
model :ListModel {
id: countryModel
}
 
delegate:
Rectangle{
width: countryText.width+10; height: countryText.height+10
color: "#00000000"
id:countryItem
Text{id:countryText
smooth: true
style: Text.Raised
x:10
font{family:fontName; bold: true ;pointSize: 12} color: textColor
text: pays
}
MouseArea{
anchors.fill: parent
onClicked: {countryList.currentIndex = countryList.indexAt(countryItem.x, countryItem.y)}
onDoubleClicked: {view.currentIndex=1}
}
}
}
 
ListView {
id: countryList
boundsBehavior: Flickable.DragAndOvershootBounds
anchors.fill:parent
model: countryItemModel
orientation: ListView.Vertical
flickDeceleration: 2000
cacheBuffer: 1000
highlightFollowsCurrentItem: true
highlight: Rectangle { color: "#00000000"; border {color:"#ffffff"; width:2} radius: 5}
currentIndex: -1
 
onCurrentIndexChanged: {
if(currentIndex!==-1)
{
highlightItem.width=currentItem.width+5;
if(ImageLoader.countryloaded[currentIndex]===0){
Parser.parsepays(currentIndex);
}
ImageLoader.createAlbums(ImageLoader.nompays[currentIndex]);
ImageLoader.createAlbumName(ImageLoader.nompays[currentIndex]);
}
}
}
}
}
...

In order to "feed the beast", I choose to use javascript functions to "push" the data into the list, I regrouped all the javascript function in a file, so that the very fluent and not buggy Qml would not be spoiled by buggy javascripts functions.

Here is the result for the countries list:

function createPays()
{
for(var i=0; i<nompays.length; i++) {
countryModel.append({"pays":nompays[i]});
}
}

Basic, but it works well and I'm satisfied with the performance so far. The swapping between the items are not shown here but some examples are available on the net, so it should not be difficult for you to find it.

The result is shown here :

Scr000041.jpg


Stations: Made of a Rectangle with a picture for the background (trick to hide the list scrolling) + TextElement


Countries / Ads : TextElement/Inneractive






Countries list: ListView inside Rectangle







Back/Quit,Favourite Country,Enjoy streaming music,More: Toolbar

All that gives a nice "Windows Phone 7" like browsing, I have been really happy with the result but I have to say that I did not spend most of my time on this part of the app.

Please note that the font is depended on your system font (my N8 is a slightly tweaked one so this is not the basic font).

Feeding the core (XML Parsing)

Here comes the part with which I had to do the hardest job. In order to make the application interesting for everyone of us, I choose to make it so that I could update the data without release a new version on the store. To succeed, I store a XML file "somewhere on the Internet" and every-time the app is launched, a HTTP request is sent to get the XML file and parse it in order to store the data received.

For the full version, this represents a 3 thousands lines file. I also use a DTD file in order to check the data integrity while editing it.

Here is the main part of the XML parser (i used javascript):

Xmlcore.jpg

I should improve the "coding style" and the quality of the comments this summer.

To comment what the function is doing here, I create a HTTP request and, when the state of the request change, I check if everything is fine. If so, I start the parsing of the data. When the parsing is done, I change the state of the application in order to show the main view.

Warning.pngWarning: Until the version 3.5 of the app, the entire data inside the file is parsed. Due to endless loading time, I choose a different approach with the 3.5 version: Only selected country and resort will be parsed (and parsed only once, useless to parse it again). The final data is stored in a "3D" Array, with Countries as x, Resorts as y and Webcams images on the z axis. Some other arrays are used to store the countries names, stations names and to keep track of what has been parsed or not.

So, for example, when you click on a resort, the following function is called (I commented it quite much):

function parsestations(p,s){
station = getElementsByTagName(pays[p], 'station');......... // We get the current country resorts
ImageLoader.stationloaded[p][s]=1; ............// We keep track that this resorts is its country has been parsed
var w=0;
ImageLoader.webcam2D[p][s]=new Array(); ...............// For this country and resort, we create a new array
webcams = getElementsByTagName(station[s], 'imaweb'); ........// We get the webcams url from the "station" array
for (w in webcams) {
ImageLoader.webcam2D[p][s]=ImageLoader.webcam2D[p][s].concat(webcams[w].firstChild.nodeValue) // For each webcam, we add the value in the global array
}
}

Warning.pngWarning: Please note I never did any Qml and Javascript before so there are surely tricks and better ways to do it. I hope that in a near future I'll be able to meet older Qt devs who could teach me some tricks.

Screen showed when the app is loading the XML file

That is all for this part, my point of view about the interaction between Javascript and Qt Quick will be described in the "Conclusion" part.

Loading the data

A very interesting aspect of Qml is that it's possible to mix very easily Javascript functions to the Qml part. So I regrouped all the "intel" of the app inside Javascripts files and used Qt Quick to provide the best UI possible.

Looking at coding, it gives the following result, when a resort is selected:

                    onCurrentIndexChanged: {.............// We trigger a change
if(currentIndex!==-1) .....// If an item is really selected
{
highlightItem.width=currentItem.width+5;...//We add a nice effect to it
if(ImageLoader.stationloaded[countryList.currentIndex][currentIndex]===0){...//If we did not parse this resort before
Parser.parsestations(countryList.currentIndex,currentIndex);....//We parse it
}
ImageLoader.createImages(currentIndex,ImageLoader.nompays[countryList.currentIndex]);...//We create all the releated webcams images
}
}

Note.pngNote: I used this kind of coding style for many interactions between Qml and Javascrpt. So far it gives nice and "smooth" result. I never had to complain about that I'm delighted to connect so easily the UI and the "intel" of the app.

Display Webcam image

To display the webcam image, a Flickable area is used. It comes with a Image component, a BusyIndicator, a PinchArea and a MouseArea. States with the help of Transitions are used to represent the image with 180° angle.

A small challenge that I had to face was the size limit of a image. Indeed, when loading a too big image the application started to slow down and it was not smooth anymore, I had to add the following code in the Image component in order to reduce the size of large images:

sourceSize.width: 2000
sourceSize.height: 500

Transition and rotation of the image is coded with the following QML code:

            states:[
State {
name: "normal"
PropertyChanges { target: itemRotation; angle: 0 }}
,State {
name: "rotated"
PropertyChanges { target: itemRotation; angle: 180 }
}
]
transitions: Transition {
RotationAnimation { properties: "angle"; from:if (imagedetail.state == "rotated"){0}else{-180}
duration: 1000 }
}
transform: Rotation {
id: itemRotation
origin.x: imagedetail.width /2;
axis.y: 1; axis.z: 0
}

You can have a overview of the image component inside the video.

Saving a favourite (database interaction)

For this part, I used the following Example. It allowed me to create very quickly a "Save and Load" function to my app. The following code is used to save and reset the favourite resort:

function save() {
//On sauve le pays//
if ((countryList.currentIndex>=0) && (albumList.currentIndex>=0)){
Storage.setSetting("CurrentCountry",countryList.currentIndex);
Storage.setSetting("CurrentStation",albumList.currentIndex);
textsave.text=countryModel.get(countryList.currentIndex).pays+ "\n"+albumModel.get(albumList.currentIndex).album+"\nsaved as favourite"
}
else{
textsave.text="Please select a country\nand a resort first"
}
}
 
function reset() {
//On sauve le pays//
Storage.setSetting("CurrentCountry",-1);
Storage.setSetting("CurrentStation",-1);
textsave.text="\nNo favourite"
}

I implemented the function inside the "More" page of the app, very easily, using a ButtonRow and a TextField, the result is quite nice looking :

Conclusion

I would say that creating this application taught me a lot about Qml and Javascript. My conclusion is that QML provide THE perfect skeleton to any app, mobile or desktop. I am used to learn Java™ in my engineering school and I have to say I am really pleased by the creation of this application. Qt Quick is really much simpler to use than Swing or AWT (even if these last ones are not "mobile" focused) and pretty much straight forward, so this allowed me to save time for focusing on other parts of the application.

Video

Here is a 6 minutes video in which I'm trying to show you the nice aspects of Qt Quick in my application. I also explain the few challenges I had to face (with the tricks I found).


Download

Note.pngNote: Due to the slow QA process concerning Symbian apps on the Nokia Store, I uploaded a unsigned version, with a store UID, here. I strongly recommend you to try the app with this file first. The S^3 version available on the Ovi store is outdated and should be updated as soon as the QA will approve the new version.


And older version (3.6) is available on the Nokia Store. The newest version is waiting for QA approval.

If you own a N9, you can find it here, this is the latest version to date.

Please note that the 2€ version is just here for anyone who wish to support my work and help me in my studies. If you do not wish to help me, the free version is more than enough to just check resorts weather.

Note.pngNote: This is an entry in the Symbian Qt Quick Components Competition 2012Q1

This page was last modified on 11 October 2012, at 04:20.
77 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×