Applications mobiles modernes avec Qt et QML

| Traducteur : Louis du Verdier | Qt Developer Network |

Qt est un framework flexible et puissant pour la création d'applications multi-plateformes. QML est désormais un élément de Qt, fournissant un langage qui donne une liberté complète dans le développement d'interfaces utilisateur.

Un bon moyen de voir le pouvoir de Qt est de commencer à coder avec lui. Écrivons une simple application utilisant Qt et QML. Ce sera une application que j'ai appelée 4Toddler ; l'application va commencer en mode plein écran et possède juste deux boutons placés dans le coin en haut à droite de la fenêtre : À propos et Fermer. La fonction principale est d'afficher une image aléatoire avec des effets de feu d'artifice et avec un effet sonore aléatoire.

L'interface utilisateur sera écrite avec QML, la colonne vertébrale avec Qt C++.

Commentez

- Titre : Applications mobiles modernes avec Qt et QML

- Auteurs : Traducteur : Louis du Verdier, Qt Developer Network

- Parution : 24 avril 2011

- Mise à jour : 6 juin 2011

- Licence : Copyright ® 2011 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. L'article original

- II. Création d'un nouveau projet

- III. Le cœur de la logique du code en C++

- IV. Ajout de l'interface QML

- V. Multilinguisme

- VI. Structure de code avec composants

- VII. Communication entre QML et C++

- VIII. Visualisation de l'état et ajout d'animations

- IX. Divers

- X. Remerciements

I - L'article original

Le Qt Developer Network est un réseau de développeurs utilisant Qt afin de partager leur savoir sur ce framework. Vous pouvez le consulter en anglais.

Nokia, Qt, Qt Quarterly et leurs logos sont des marques déposées de Nokia Corporation en Finlande et/ou dans les autres pays. Les autres marques déposées sont détenues par leurs propriétaires respectifs.

Cet article est la traduction de Modern Mobile Applications with Qt and QML.

II - Création d'un nouveau projet

Si vous n'avez pas déjà installé Qt, consultez la page de téléchargement de Qt, dans la section « Qt SDK: Complete Development Environment«  pour télécharger et installer les outils et les exemples pour votre plateforme.

Lancez Qt Creator, faites Fichier Nouveau fichier ou projet. Dans la nouvelle boîte de dialogue, sélectionnez Projet Qt C++ > Application graphique Qt, puis cliquez sur Choisir....

Dans la nouvelle fenêtre, entrez le nom du projet, sélectionnez le chemin du dossier du projet et cliquez sur Suivant.

Dans la fenêtre suivante, décochez l'option Générer l'interface graphique. Nous n'avons pas besoin d'en générer une. Cliquez sur Suivant.

Dans la dernière fenêtre, cliquez juste sur Terminer.

Le squelette de l'application est prêt, nous devons maintenant passer au code pour faire la partie logique et le design de l'interface utilisateur.

III - Le cœur de la logique du code en C++

Nous devons tout d'abord ajouter le module qt-declarative à notre projet. C'est le module qui fournit un widget dans lequel l'interface QML est affichée.

Ouvrez le fichier de projet (4Toddler.pro) et remplacez la ligne :

QT += core gui

par son équivalent avec declarative :

QT += core gui declarative

Nous devons maintenant changer la classe de base pour notre fenêtre principale. Remplacez QMainWindow par QDeclarativeView et incluez QDeclarativeView.

#include <QDeclarativeView>
class MainWindow : public QDeclarativeView
{
...
}

Nous devons également couper QMainWindow(parent) du constructeur de MainWindow ; n'avons donc plus besoin de cette initialisation.

MainWindow::MainWindow(QWidget *parent)
{
Init();
}

Si vous lancez l'application dès maintenant, vous devriez voir une fenêtre vide. C'est parce que nous n'avons pas encore initialisé ou créé notre interface QML.

IV - Ajout de l'interface QML

Ajoutons un nouveau fichier à notre projet : faites un clic droit sur le projet dans l'explorateur de projet.

Cliquez sur Ajouter nouveau puis sélectionnez la section Qt et Fichier QML dans la liste des templates. Cliquez alors sur Choisir.

Entrez le nom du fichier dans le champ Nom, cliquez sur Suivant puis sur Terminer dans la fenêtre suivante.

Cet assistant va créer et ouvrir le nouveau fichier QML dans l'éditeur. Dedans se situe uniquement un élément Rectangle. Ce sera l'élément racine de notre interface utilisateur. Ajoutons quelques propriétés à cet élément.

Note : certaines versions de Qt Creator vont ajouter un élément textuel Hello World centré dans le Rectangle. Retirez cet élément textuel avant de continuer.

Rectangle
{
id: canvas;
color: "black"
anchors.fill: parent
focus: true
}

Il n'y a rien de spécial pour le moment, juste un arrière-plan noir.

V - Multilinguisme

Durant le reste de ce tutoriel, nous allons passer et repasser du développement QML au développement C++.

Nous devons ajouter du code d'initialisation pour notre interface QML en définissant une nouvelle méthode dans le fichier mainwindow.h

void Init();

et de l'implémenter dans le fichier mainwindow.cpp.

void MainWindow::Init()
{
QString contentPath;
#ifdef QT_DEBUG
contentPath = "D:/MyProjects/QT/4Toddler";
#else
contentPath = QApplication::applicationDirPath();
#endif
setFocusPolicy(Qt::StrongFocus);
setResizeMode(QDeclarativeView::SizeRootObjectToView);
setSource(QUrl::fromLocalFile(contentPath + "/main.qml"));
}

Maintenant, nous devons remplacer une ligne dans le fichier main.cpp :

int main(int argc, char *argv[])
{
...
w.show();
...
}

Par :

int main(int argc, char *argv[])
{
...
w.showFullScreen();
...
}

Cela va forcer notre fenêtre à s'ouvrir en mode plein écran.

VI - Structure de code avec composants

Avant de lancer notre application, ajoutons-y deux boutons. L'un afin d'afficher la boîte de dialogue À propos et le second pour Fermer notre fenêtre. Nous n'avons pas besoin d'implémenter à deux reprises des boutons, nous allons créer un composant et l'utiliser à chaque fois que nous le voudrons en changeant uniquement quelques propriétés.

Ajoutons le fichier QML WindowButton.qml à notre projet. Notez que le nom de fichier commence par une lettre majuscule, ce qui signifie qu'il définit un composant QML.

Image
{
id: button
MouseArea
{
anchors.fill: parent
id: mouseArea
onClicked: callback()
}
}

Nous pouvons désormais ajouter deux boutons à l'intérieur de notre fenêtre :

// Place items in row
Row
{
anchors.right: parent.right;
anchors.rightMargin: 4;
anchors.top: parent.top;
anchors.topMargin: 4;
spacing: 4
WindowButton
{
id: about;
source: "about.png";
function callback()
{
}
}
WindowButton
{
id: exit;
source: "exit.png";
function callback()
{
}
}
}

VII - Communication entre QML et C++

Maintenant, nous devrions également implémenter les méthodes callback(). Pour fermer la fenêtre, nous allons appeler la fonction Quitter de la fenêtre principale. Dès lors, nous allons voir comment Qt et QML communiquent. Les méthodes vont être implémentées à l'intérieur du fichier Qt et être appelées depuis le fichier QML.

Ajoutons une nouvelle fonction au fichier d'en-tête mainwindow.h.

Q_INVOKABLE void Quit();

Et l'implémentation dans le fichier mainwindow.cpp :

void MainWindow::Quit()
{
QApplication::quit();
}

Maintenant, nous devons « prévenir » QML de l'existence de cette méthode. À l'intérieur de Init, nous devons uniquement ajouter une ligne :

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

Maintenant, nous pouvons accéder aux fonctions de l'objet window déclarées Q_INVOKABLE depuis notre UI QML. Window est juste là en tant qu'exemple, vous pouvez utiliser n'importe quel nom d'objet.

Ajoutons une implémentation de fonction callback() à notre bouton de fermeture.

function callback()
{
window.Quit();
}

VIII - Visualisation de l'état et ajout d'animations

OK, l'application peut être lancée. Lancez-la et cliquez sur le bouton de fermeture. Vous voyez ? L'état du bouton n'est pas changé après un clic ; il semble que le bouton soit juste inactif. Ajoutons des changements d'états pour l'état normal et l'état enfoncé.

Image
{
...
states:[
State
{
name: "hovered";
when: mouseArea.pressed;
PropertyChanges { target: button; opacity: 1;}
},
State
{
name: "normal"
when: mouseArea.pressed == false;
PropertyChanges { target: button; opacity: 0.7; }
}
]
}

L'élément va changer d'état si la condition décrie dans la propriété when est vraie. Vous pourriez aussi changer l'état manuellement en assignant un état à la propriété.

Lançons à nouveau l'application. OK, cette fois, cela semble bien plus actif. Nous pourrions donner un meilleur aspect à notre bouton en ajoutant une animation.

Image
{
...
Behavior on opacity
{
// Animaion step is a 100 milliseconds
// On every iteration opacity will increase or descrease with step equals to 0,1
NumberAnimation { duration: 100 }
}
}

Le Behavior est un moyen simple est flexible de créer des animations. Cet élément permet de spécifier une animation par défaut pour un changement de propriété.

Lancez l'application est tentez de cliquer sur les boutons À propos ou Fermer. C'est déjà bien mieux !

Nous allons implémenter la boîte de dialogue À propos uniquement en utilisant QML. La boîte de dialogue va apparaître à l'écran au moment où l'on cliquera sur le bouton À propos et disparaître lorsque l'utilisateur va cliquer à l'intérieur du bouton ou sur l'arrière-plan.

Ajoutons le fichier About.qml à notre projet.

Rectangle
{
id: about
function show()
{
about.opacity = 1;
}
function hide()
{
about.opacity = 0;
}

color: "transparent"
opacity: 0

width: parent.width
height: parent.height
visible: opacity > 0
Rectangle
{
anchors.fill: parent
opacity: 0.5
color: "gray"
} Rectangle
{
id: dialog

width: 360
height: 230
x: parent.width / 2 - dialog.width / 2;
y: parent.height / 2 - dialog.height / 2;
z: 10
border.color: "gray"
Text
{
text: "4 Toddler"
font.bold: true
font.pixelSize: 22

anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
}
}
Behavior on opacity
{
NumberAnimation { duration: 100 }
}
MouseArea
{
anchors.fill: parent;

onClicked: hide();
}
}

Regardez la ligne :

visible: opacity > 0

Comme vous pouvez le voir, les propriétés peuvent être assignées et être calculées.

Ajoutons la boîte de dialogue À propos et les implémentations de fonction callback() au bouton À propos. Dans le fichier Main.qml, nous devons déclarer le nouvel élément About.

Rectangle
{
id: canvas
...
About
{
id: aboutDlg
}
}

Et l'implémentation de la fonction callback() :

aboutDlg.show();

À :

WindowButton
{
id: about;
...
function callback()
{
aboutDlg.show();
}
}

Enfin, nous devons implémenter la fonctionnalité principale de notre application.

L'élément à afficher avec une image aléatoire sera un élément Image. Ajoutons un nouveau fichier pour cet élément, Block.qml.

Image
{
id: block;
property bool remove: false
property bool show: false

opacity: 0;
fillMode: Image.Stretch;
states: [
State
{
name: "remove"; when: remove == true;
PropertyChanges { target: block; opacity: 0 }
StateChangeScript { script: block.destroy(1000); }
},
State
{
name: "show"; when: show == true;
PropertyChanges { target: block; opacity: 1 }
}
]
Behavior on opacity { NumberAnimation { duration: 300 } }
}

Maintenant, nous devons implémenter la gestion du clavier. La gestion sera implémentée avec JavaScript. Ajoutons dès lors un fichier main.js à notre projet.

// Template for new elements

var component = Qt.createComponent("block.qml");
var maxBlocksCount = 10;
var blocksArray  = new Array();
function handleKey()
{
var x = Math.floor(Math.random() * canvas.width);
var y = Math.floor(Math.random() * canvas.height);
createNewBlock(x, y);
}
function createNewBlock(x, y)
{
if(component.status != Component.Ready)
{
return false;
}
if(blocksArray.length > maxBlocksCount)
{
removeAllBlocks();
}
var newBlock = component.createObject(canvas);
if(newBlock == null)
{
return false;
} var iconFile = window.randomIcon;
newBlock.source = ("Icons/" + iconFile);
newBlock.x = x;
newBlock.y = y;
newBlock.show = true;
blocksArray.push(newBlock);
window.PlaySound();
return true;
}
function removeAllBlocks()
{
for(var i = 0; i < blocksArray.length; ++i)
{
blocksArray[i].remove = true;
}
while(blocksArray.length != 0)
{
blocksArray.pop();
}
}

Comme vous le voyez dans le code ci-dessus, nous devons implémenter une nouvelle propriété randomIcon et une méthode PlaySound.

Il est temps d'ajouter la déclaration de propriété et la méthode d'accès à celle-ci à notre fichier mainwindow.h.

Q_PROPERTY(QString randomIcon READ RandomIcon)

QString RandomIcon();

Implémentation dans le fichier mainwindow.cpp :

QString MainWindow::RandomIcon()
{
QStringList iconFilesList;

QString searchPath = m_ContentPath + "/Icons/";
QDir directory = QDir(searchPath);
QStringList filters;
filters << "*.png";
directory.setNameFilters(filters);
iconFilesList = directory.entryList(QDir::AllEntries);
int fileIdx = qrand() % iconFilesList.count();

return iconFilesList.at(fileIdx);
}

Maintenant, nous devons déclarer une méthode pour jouer un son aléatoire dans le fichier mainwindow.h :

Q_INVOKABLE void PlaySound();

Et l'implémenter dans le fichier mainwindow.cpp.

void MainWindow::PlaySound()
{
QStringList soundFilesList;

QDir directory = QDir(m_ContentPath + "/Sounds/");

QStringList filters;

filters << "*.wav";

directory.setNameFilters(filters);
soundFilesList = directory.entryList(QDir::AllEntries);
int fileIdx = qrand() % soundFilesList.count();
QString soundFile = m_ContentPath + "/Sounds/" + soundFilesList.at(fileIdx);
QSound::play(soundFile);
}

La plupart des choses est faite. Tout ce dont nous avons besoin est d'ajouter la gestion du clavier à notre élément QML parent. Mais tout d'abord, nous devons inclure le fichier main.js au fichier main.qml.

import Qt 4.7

import "main.js" as Main

Les gestionnaires d'évènements claviers pour les éléments racine :

Rectangle
{
id: canvas
...
Keys.onPressed: { if(event.isAutoRepeat == false) { Main.handleKey(); } }

C'est tout ! Maintenant, nous pouvons lancer l'application et tenter de presser l'un des boutons du clavier. Mais on a oublié les effets de feu d'artifice.

Ajoutons une fois de plus un fichier, cette fois-ci appelé Fireworks.qml. Pourquoi avons-nous besoin d'un nouveau fichier ? Parce que ce sera un fichier réutilisable, permettant de gagner du temps à l'avenir.

import Qt.labs.particles 1.0
Particles
{
id: particles
width: 1;
height: 1;
anchors.centerIn: parent
emissionRate: 0
lifeSpan: 700;
lifeSpanDeviation: 600
angle: 0;
angleDeviation: 360;
velocity: 100;
velocityDeviation: 30;
source: randomImage();
function randomImage()
{
var images = ["red.png", "blue.png", "green.png", "white.png", "yellow.png"];
var idx = Math.floor((Math.random() * 100)) % images.length;
return ("Stars/" + images[idx]);
}
}

Maintenant, nous devons ajouter notre élément Firework à l'élément Block. Ouvrez le fichier Block.qml et ajoutez la déclaration du nouvel élément :

Image
{
id: block;
...
Firework
{
id: firework
}
...
}

Pour lancer les feux d'artifice sur l'élément passant à l'état visible, nous devons juste ajouter une ligne de code :

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

Pour montrer l'état « show« .

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

Lancez l'application et appuyez sur n'importe quelle touche. L'image apparaît à l'écran avec des feux d'artifice et un effet sonore.

C'est une simple application, mais c'est un bon moyen de comprendre les principes de QML.

Pour en apprendre plus à propos de QML, vous pouvez étendre cette application en y ajoutant des propriétés. Qui sait, peut-être qu'un jour, de cette petite application sortira une application commerciale ? Petite idée personnelle : les logiciels éducatifs avec des lettres, nombres, formes, couleurs, etc.

IX - Divers

Cet article apparaît originellement sur Intel AppUp(SM) developer program blog. Une version russe est disponible ici.

Merci à Dmitry de nous avoir autorisé à copier son tutoriel.

X - Remerciements

Merci à Thibaut Cuvelier pour sa relecture !