English French [qt-devnet.developpez.com]

Modern Mobile Applications with Qt and QML

Qt is a flexible and powerful framework for creating cross-platform applications. QML is now part of Qt, providing a markup language which gives complete freedom in development of user interfaces.

A good way to see the power of Qt is to start coding with it. Let’s write a simple application using Qt and QML. This will be an application I called 4Toddler; the application will start in full-screen mode and it has just two buttons placed in the top right corner of the window: About and Close. The main function is to display a random image with a fireworks effect and with a random sound effect.

The User interface will be written with QML; the backbone is written in Qt C++.

Create new project

If you do not have Qt already installed, visit Qt download page [qt.nokia.com] to download Qt SDK for your platform and install it.

Launch Qt Creator, select File -> New File or Project. In the new dialog select Qt C++ Project -> Qt Gui Application, and then click Choose…

screenshot

In the new window: type project name, select path to the project folder and then click Next.

screenshot

In the next window: uncheck Generate form option. We do not need to generate a form. Click Next

screenshot

In the last window: just click Finish.

The application skeleton is ready, now we can proceed to code the logic and design the UI.

Code core logic in C++

First we need to add the qt-declarative [doc.qt.nokia.com] module to our project. This is the module that provides a widget in which a QML interface is displayed.

Open project file (4Toddler.pro) and append the line

  1. QT += core gui

with declarative

  1. QT += core gui declarative

Now we need to change the base class for our main window. Replace QMainWindow with QDeclarativeView and include QDeclarativeView

  1. #include <QDeclarativeView>
  2. class MainWindow : public QDeclarativeView
  3. {
  4.   ...
  5. }

Also we need to cut off QMainWindow(parent) from the MainWindow constructor; we do not need this initialization anymore.

  1. MainWindow::MainWindow(QWidget *parent)
  2. {
  3.   Init();
  4. }

If you launch the application right now you will see an empty window. This is because we have not initialized or created our QML interface yet.

Add QML interface

Let’s add a new file to our project: right click on the project in the projects explorer window.

Add New then select Qt section and Qt QML File in the templates list and click Choose…

Type the file name in the Name field, click Next then Finish in the next window.

The wizard will create and open the new QML file in the editor. There is just one Rectangle element inside it. This will be the root element for our user interface. Let’s add some new properties to this element.

Note: some versions of Qt Creator will add a Hello World text element centered in the Rectangle. Remove the text element before proceeding.

  1. Rectangle
  2. {
  3.   // ID of this element. Using this ID we can access the element and its properties from other elements
  4.   id: canvas
  5.   // Background color, black in this case
  6.   color: "black"
  7.   // Change sizes of this element to fill the parent
  8.   anchors.fill: parent
  9.   // Element can receive focus
  10.   focus: true
  11. }

There is nothing special for now, just black background.

Multilingual

During the remainder of this tutorial we will shift back and forth between QML and C++ development.

We need to add some initialization code for our QML interface by defining a new method inside the mainwindow.h file …

  1. void Init();

.. And implementing it in the mainwindow.cpp file

  1. void MainWindow::Init()
  2. {
  3.   // Path to the content folder
  4.   QString contentPath;
  5.  
  6. #ifdef QT_DEBUG
  7.   // In the debug version of our project this is a path to the project folder
  8.   contentPath = "D:/MyProjects/QT/4Toddler";
  9. #else
  10.   // In the release version - path to the application folder
  11.   contentPath = QApplication::applicationDirPath();
  12. #endif
  13.   setFocusPolicy(Qt::StrongFocus);
  14.   // Change QML document sizes to fit the main window
  15.   setResizeMode(QDeclarativeView::SizeRootObjectToView);
  16.   // Load QML file
  17.   setSource(QUrl::fromLocalFile(contentPath + "/main.qml"));
  18. }

Now we need to replace one line in the main.cpp file

  1. int main(int argc, char *argv[])
  2. {
  3.   ...
  4.   w.show();
  5.   ...
  6. }

With

  1. int main(int argc, char *argv[])
  2.  
  3. {
  4.   ...
  5.   w.showFullScreen();
  6.   ...
  7. }

This will force our window to open in full-screen mode.

Structure code with components

Before we launch our application, let’s add two buttons. One is to display About dialog and the second to Close our window. We do not need to implement buttons twice, we going to create a component [doc.qt.nokia.com] and use it every time we need to add a new button by overriding just a few properties.

Let’s add the new Qml file – WindowButton.qml to our project. Note the filename begins with a capital letter — this signifies it defines a QML component.

  1. Image
  2. {
  3.   // ID of this element
  4.   id: button
  5.   // The MouseArea item enables simple mouse handling
  6.   MouseArea
  7.   {
  8.     anchors.fill: parent
  9.     id: mouseArea
  10.     // On click call callback() handler
  11.     onClicked: callback()
  12.   }
  13. }

Now we can add two buttons to our window

  1. // Place items in row
  2.  
  3. Row
  4. {
  5.   // Right side of the item is anchored to the right side of the parent element
  6.   anchors.right: parent.right
  7.   // Right margin
  8.   anchors.rightMargin: 4
  9.   // Top side of the item is anchored to the top side of the parent element
  10.   anchors.top: parent.top
  11.   // Top margin
  12.   anchors.topMargin: 4
  13.   // Margins for children elements
  14.   spacing: 4
  15.   WindowButton
  16.   {
  17.     // Button to display the About dialog
  18.     id: about
  19.     // Path to background picture. This is a relative path to the path of this QML file
  20.     source: "about.png"
  21.     // Callback method, which will be called on mouse click
  22.     // onClicked: callback()
  23.     function callback()
  24.     {
  25.     }
  26.   }
  27.   WindowButton
  28.   {
  29.     // Button to close window
  30.     id: exit
  31.     source: "exit.png"
  32.     function callback()
  33.     {
  34.     }
  35.   }
  36. }

Communicate between QML and C++

Now we should implement both callback() methods. To close the window we will call the Quit function of the main window. Now we will see how Qt and QML communicate. Methods will be implemented inside Qt and then called from the QML file.

Let’s add a new function to the mainwindow.h header file

  1. Q_INVOKABLE void Quit();

And implementation in mainwindow.cpp file

  1. void MainWindow::Quit()
  2.  
  3. {
  4.  
  5.   QApplication::quit();
  6.  
  7. }

Now we need to “tell” QML about this method. Inside Init we should to add just one line:

  1. rootContext()->setContextProperty("window", this);

Now we can access window object’s functions declared as Q_INVOKABLE from QML. Window here is just an example, you could use any object name you want.

Let’s add callback() function implementation to close button

  1. function callback()
  2. {
  3.   window.Quit();
  4. }

Visualize state and add animation

Ok, the application can be launched. Launch it and click the Close button. You see? State of the button is not changed after click; it looks like the button is just inactive. Let’s add state changes for normal and clicked states.

  1. Image
  2. {
  3.   ...
  4.   states:[
  5.     State
  6.     {
  7.       // Name of the state
  8.       name: "hovered"
  9.       // When condition. Item will go to this state if condition is true
  10.       // In this case on mouse click
  11.       when: mouseArea.pressed
  12.       // Which properties will be changed in that state
  13.       // In this case - opacity
  14.       PropertyChanges { target: button; opacity: 1}
  15.     },
  16.  
  17.     State
  18.     {
  19.       name: "normal"
  20.       // This state will be activated if mouse button is not pressed
  21.       when: mouseArea.pressed == false
  22.       PropertyChanges { target: button; opacity: 0.7; }
  23.     }
  24.    ]
  25. }

Item will change state if the condition described in when property is true. You could change state manually by assigning state property.
Let’s launch the application again. Ok, this time it looks more active. We can make our button nicer by adding some animation.

  1. Image
  2. {
  3.   ...
  4.   Behavior on opacity
  5.   {
  6.     // Animaion step is a 100 milliseconds
  7.     // On every iteration opacity will increase or descrease with step equals to 0,1
  8.     NumberAnimation { duration: 100 }
  9.   }
  10. }

The Behavior is a simple and flexible way to create animations. This element allows you to specify a default animation for a property change.

Launch the application and try to click on About or Close buttons. This looks much better!

We will implement the About dialog only using QML. Dialog will appear on the screen when About button is clicked and disappear when the user clicks inside the button or on the background.

Let’s add About.qml file to our project.

  1. // Parent element for dialog window
  2. Rectangle
  3. {
  4.   id: about
  5.   // Function to display dialog window
  6.   // This function just changes opacity of the main element to 1
  7.   function show()
  8.   {
  9.     about.opacity = 1
  10.   }
  11.   // Function to hide dialog window
  12.   // This function just changes opacity of the main element to 0
  13.   function hide()
  14.   {
  15.     about.opacity = 0
  16.   }
  17.   // Transparent background
  18.   color: "transparent"
  19.   // Opacity is 0 by default, dialog not visible
  20.   opacity: 0
  21.   // Anchors to parent width and height.
  22.   width: parent.width
  23.   height: parent.height
  24.   // Element is visible if opacity > 0
  25.   // opacity > 0
  26.   visible: opacity > 0
  27.   // Child element to create semitransparent background
  28.   Rectangle
  29.   {
  30.     anchors.fill: parent
  31.     opacity: 0.5
  32.     color: "gray"
  33.   }
  34.   // Body of the dialog window
  35.   Rectangle
  36.   {
  37.     id: dialog
  38.    
  39.     // Fixed width and height
  40.     width: 360
  41.     height: 230
  42.     // To make dialog centered inside parent we need to calculate its x and y coordinates
  43.     x: parent.width / 2 - dialog.width / 2
  44.     y: parent.height / 2 - dialog.height / 2
  45.     // Place dialog on top of other elements
  46.     z: 10
  47.     border.color: "gray"
  48.     Text
  49.     {
  50.       text: "4 Toddler"
  51.       font.bold: true
  52.       font.pixelSize: 22
  53.      
  54.       anchors.horizontalCenter: parent.horizontalCenter
  55.       anchors.verticalCenter: parent.verticalCenter
  56.     }
  57.   }
  58.   Behavior on opacity
  59.   {
  60.     NumberAnimation { duration: 100 }
  61.   }
  62.   MouseArea
  63.   {
  64.     anchors.fill: parent
  65.     // Hide dialog on mouse click
  66.     onClicked: hide()
  67.   }
  68. }

Take a look at the line

  1. visible: opacity > 0

As you can see, property can be assigned and can be calculated.

Let’s add About dialog and callback() function implementation for the button About. In Main.qml file we need to declare the new About element.

  1. Rectangle
  2. {
  3.   id: canvas
  4.   ..
  5.   About
  6.   {
  7.     id: aboutDlg
  8.   }
  9. }

And callback() function implementation

  1. aboutDlg.show();

to

  1. WindowButton
  2. {
  3.   id: about
  4.   ...
  5.   function callback()
  6.   {
  7.     aboutDlg.show()
  8.   }
  9. }

Finally we need to implement the main functionality for our application.

The element to display a random icon on the screen will be an Image element. Let’s add a new file for the new element – Block.qml

  1. Image
  2. {
  3.     id: block;
  4.    // New custom properties to change visibility of this element
  5.     property bool remove: false
  6.     property bool show: false
  7.     opacity: 0
  8.     fillMode: Image.Stretch
  9.   states: [
  10.     State
  11.     {
  12.       // State is used to remove element from the screen and destroy it
  13.       name: "remove"; when: remove == true
  14.       PropertyChanges { target: block; opacity: 0 }
  15.       StateChangeScript { script: block.destroy(1000) }
  16.     },
  17.     State
  18.     {
  19.       // State is used to display element
  20.       name: "show"; when: show == true
  21.       PropertyChanges { target: block; opacity: 1 }
  22.     }
  23.   ]
  24.   Behavior on opacity { NumberAnimation { duration: 300 } }
  25. }

Now we need to implement the keyboard handler. Handler will be implemented with JavaScript. Let’s add new main.js to our project.

  1. // Template for new elements
  2. var component = Qt.createComponent("block.qml")
  3. // Max number of items on the screen
  4. var maxBlocksCount = 10
  5. // Array for the items
  6. var blocksArray    = new Array()
  7. // Keyboard handler
  8. function handleKey()
  9. {
  10.   // x coordinate is a random number from 0 to window with in pixels
  11.   var x = Math.floor(Math.random() * canvas.width)
  12.   // y coordinate is a random number from 0 to window height in pixels
  13.   var y = Math.floor(Math.random() * canvas.height)
  14.   // This function will create a new element on each call with random x and y coordinates
  15.   createNewBlock(x, y)
  16. }
  17. // Function to create a new element
  18. function createNewBlock(x, y)
  19. {
  20.   if(component.status != Component.Ready)
  21.   {
  22.     return false
  23.   }
  24.   // Remove items if number of items is more than maxBlocksCount
  25.   if(blocksArray.length > maxBlocksCount)
  26.   {
  27.     removeAllBlocks()
  28.   }
  29.   var newBlock = component.createObject(canvas)
  30.   if(newBlock == null)
  31.   {
  32.     return false
  33.   }
  34.   // Path to image is available via randomIcon property of the main window
  35.   var iconFile = window.randomIcon
  36.   newBlock.source = ("Icons/" + iconFile)
  37.   newBlock.x = x
  38.   newBlock.y = y
  39.   // Change state to show
  40.   newBlock.show = true
  41.   blocksArray.push(newBlock)
  42.   // Play random sound effect
  43.   window.PlaySound()
  44.   return true
  45. }
  46. // Function do remove all existings items
  47. function removeAllBlocks()
  48. {
  49.   for(var i = 0; i < blocksArray.length; ++i)
  50.   {
  51.     blocksArray[i].remove = true
  52.   }
  53.   while(blocksArray.length != 0)
  54.   {
  55.     blocksArray.pop()
  56.   }
  57. }

As you see in code above we should implement new property randomIcon and method PlaySound.

Let’s add property declaration and method to access it to our mainwindow.h file.

  1. Q_PROPERTY(QString randomIcon READ RandomIcon)

  1. QString RandomIcon();

Implementation in mainwindow.cpp file

  1. QString MainWindow::RandomIcon()
  2. {
  3.   QStringList iconFilesList;
  4.   QString searchPath = m_ContentPath + "/Icons/";
  5.   QDir directory = QDir(searchPath);
  6.   QStringList filters;
  7.   filters << "*.png";
  8.   directory.setNameFilters(filters);
  9.   // Get the list of the png files inside Icons directory
  10.   iconFilesList = directory.entryList(QDir::AllEntries);
  11.   // Generate random index of the element
  12.   int fileIdx = qrand() % iconFilesList.count();
  13.  
  14.   // Return file name
  15.   return iconFilesList.at(fileIdx);
  16. }

Now we need to declare the method to play a random sound effect in mainwindow.h file

  1. Q_INVOKABLE void PlaySound();

And implementation in mainwindow.cpp file

  1. void MainWindow::PlaySound()
  2. {
  3.   QStringList soundFilesList;
  4.   QDir directory = QDir(m_ContentPath + "/Sounds/");
  5.   QStringList filters;
  6.   filters << "*.wav";
  7.   directory.setNameFilters(filters);
  8.   // Wav files list inside Sounds directory
  9.   soundFilesList = directory.entryList(QDir::AllEntries);
  10.   // Generate random index of the element
  11.   int fileIdx = qrand() % soundFilesList.count();
  12.   // File name
  13.   QString soundFile = m_ContentPath + "/Sounds/" + soundFilesList.at(fileIdx);
  14.   // Play file asynchronously
  15.   QSound::play(soundFile);
  16. }

Almost done. All we need is to add the keyboard handler to our parent QML element. But first we should include main.js file to main.qml file

  1. import Qt 4.7
  2. import "main.js" as Main

Keyboard event handler for root element

  1. Rectangle
  2. {
  3.   id: canvas
  4.   ...
  5.   Keys.onPressed: {
  6.          if(event.isAutoRepeat == false) {
  7.                 Main.handleKey()
  8.          }
  9.   }
  10. }

That’s all! Now you can launch the application and try to press any key on the keyboard. But… I almost forgot about the fireworks effect.

Let’s add a new file called Fireworks.qml. Why do we need a new file? Because this will be a reusable element, this will save time in the future.

  1. import Qt.labs.particles 1.0
  2. Particles
  3. {
  4.   id: particles
  5.   width: 1
  6.   height: 1
  7.   anchors.centerIn: parent
  8.   emissionRate: 0
  9.   lifeSpan: 700
  10.   lifeSpanDeviation: 600
  11.   angle: 0
  12.   angleDeviation: 360
  13.   velocity: 100
  14.   velocityDeviation: 30
  15.   source: randomImage()
  16.   // Get random image path
  17.   function randomImage()
  18.   {
  19.     // Array of the image files
  20.     var images = ["red.png", "blue.png", "green.png", "white.png", "yellow.png"]
  21.     // Get random index of the array element
  22.     var idx = Math.floor((Math.random() * 100)) % images.length
  23.     // Return the relative image file path
  24.     return ("Stars/" + images[idx])
  25.   }
  26. }

Now we need to add our Firework element to the Block element. Open Block.qml file and add declaration of the new element

  1. Image
  2. {
  3.   id: block
  4.   ...
  5.   Firework
  6.   {
  7.     id: firework
  8.   }
  9.   ...
  10. }

To launch fireworks on item going to visible state we need to add just one line of code

  1. StateChangeScript { script: firework.burst(50); }

to “show” state

  1. State
  2. {
  3.   name: "show"; when: show == true
  4.   StateChangeScript { script: firework.burst(50)}
  5.   PropertyChanges { target: block; opacity: 1 }
  6. }

Launch application and press any key. Image appears on the screen with fireworks and sound effect.

This is a simple application, but this is a good playground to understand the principles of QML.

To learn more about QML you can extend this application by adding features. Who knows, maybe someday this sample will grow to a commercial app? Idea from me: educational software with letters, numbers, shapes, colors, etc.

***

This article originally appeared on the Intel AppUp(SM) developer program blog [appdeveloper.intel.com]. A Russian version is available here [habrahabr.ru]

Thanks to Dmitry [intelloware.com] for allowing us to copy his tutorial.

Categories: