Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

Vous n'avez pas encore de compte Developpez.com ? L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Developpez.com

Qt

Choisissez la catégorie, puis la rubrique :

Viadeo Twitter Facebook Share on Google+   
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Toutes les fonctions  ·  Vues d'ensemble  · 

Introduction à Qt Quick

Aperçu

QML est un langage scripté de haut niveau. Ses commandes, ou plus précisément, ses éléments, associés à la puissance et à l'efficacité des bibliothèques Qt, rendent la programmation plus intuitive. Dessiner un rectangle, afficher une image à une position donnée, etc. : derrière ces éléments se trouvent des bibliothèques C++ complexes qui effectuent ces actions efficacement. Comme pour toute application graphique, restez conscients que cette facilité à construire des applications graphiques riches signifie qu'une attention particulière peut être nécessaire pour prévenir des problèmes de performances.

Le langage permet également de rendre ces commandes plus flexibles par l'utilisation de JavaScript plutôt que de C++ lors de l'ajout de nouvelles couches de logique à votre application. Le JavaScript est plus facile à apprendre que le C++ et peut être intégré dans les fichiers QML ou importé à partir d'un fichier distinct.

En QML les divers types d'« objets » sont appelés des éléments.

Un élément contient habituellement des propriétés qui aident à définir l'élément. Par exemple, si nous créons un élément appelé Cercle alors le rayon du cercle serait une propriété.

Un premier regard

La syntaxe basique d'un élément est

 SomeElement {
     id: myObject
     ... d'autres choses ici ...
 }

Nous définissons ici un nouvel objet. Nous spécifions d'abord son ‹type› comme étant SomeElement. Ensuite, entre accolades { ... } nous spécifions les différentes parties de notre élément.

L'id est un identifiant unique de l'élément, il doit commencer par une lettre minuscule et ne contenir que des lettres, nombres et tirets bas. C'est le nom de l'objet. Si cet élément SomeElement avait été un Rectangle et qu'il en existait plusieurs, alors l'identifiant unique optionnel nous aurait permis de manipuler chaque élément individuellement.

Chaque élément visuel est directement ou indirectement basé sur un, ou hérité d'un, élément appelé Item. Item possède certaines propriétés et actions utiles. Les propriétés possèdent des valeurs par défaut permettant de ne spécifier que celles dont vous avez besoin.

Prenons un élément simple tel que Rectangle. Il possède un id, que nous allons appeler myRectangle, il a une largeur width et une hauteur height. Imaginez que vous souhaitiez un rectangle de 500 pixels de large sur 400 de haut.

Nous pouvons l'implémenter avec Rectangle et ses propriétés de cette façon :

 import QtQuick 1.0
 
 // Ceci est un commentaire. Et ci-dessous myRectangle est défini.
 Rectangle {
     id: myRectangle
     width: 500
     height: 400
 }

C'est un script QML valide. Pour l'exécuter, copiez-le et sauvegardez-le dans un fichier myexample.qml par exemple et dans la ligne de commande, entrez :

qmlviewer myexample.qml

Sur Mac OS X, ouvrez à la place l'application « QMLViewer » et chargez le fichier myexample.qml ou exécutez-le en ligne de commande avec :

 QMLViewer.app/Contents/MacOS/QMLViewer myexample.qml

Cela créera un rectangle très peu intéressant, affiché dans sa propre fenêtre.

Hello World!

Maintenant, nous voulons ajouter un peu de couleur et de texte pour faire notre programme QML Hello World.

Rectangle possède une propriété color pour produire une couleur de fond.

Le texte est géré par un élément différent appelé Text. Nous devons créer un objet Text à l'intérieur du Rectangle et définir sa propriété text à « Hello World! ». Donc, pour avoir le texte « Hello world » et la couleur de fond grise, il faut le code suivant :

 import QtQuick 1.0
 
 Rectangle {
     id: myRectangle
     width: 500
     height: 400
 
     Text { text: "Hello World!" }
 
     color: "lightgray"
 }

Encore Hello World

À partir de maintenant, nous n'allons plus systématiquement montrer la ligne d'importation pour Qt mais elle doit toujours être là lorsque vous créez des scripts QML.

Pour rendre notre exemple d'Hello World un peu plus beau, définissez la position du texte à la position de pixel x = 100, y = 100 de la fenêtre affichée. La position appartient à l'élément Text donc nous définissons la position à l'intérieur de sa définition. Notez que nous séparons les différentes commandes d'une même ligne avec l'aide d'un point virgule ou alors, nous pouvons mettre une commande par ligne :

     Text {
         text: "<h2>Hello World</h2>"; color: "darkgreen"
         x: 100; y:100
     }

Nous n'avons pas seulement repositionné le texte mais aussi ajouté des balises HTML pour changer la taille du texte. La couleur du texte a aussi été modifiée pour devenir vert foncé en utilisant un mot-clé standard du SVG pour les noms de couleur.

Nous aurions aussi pu utiliser une chaine de caractères hexadécimale pour les valeurs RGB de la couleur (rouge-vert-bleu, comme #rrggbb), ce qui est similaire à la méthode utilisée en HTML. Par exemple, une couleur presque bleue avec un peu de vert :

     Text {
         text: "<h1>Hello world again</h1>"
         color: "#002288"
         x: 100; y: 100
     }

Tous ces changements ont été faits à l'intérieur de l'objet Text qui est le champ d'action de ces changements de propriétés.

Les autres objets peuvent utiliser les informations mais elles appartiennent à l'élément dans lequel les propriétés ont été définies.

Images

Pour ajouter une image à notre petite application, nous utilisons l'élément Image. Une Image utilise un chemin vers un fichier image et possède des propriétés pour contrôler les proportions, la taille de l'image, le pavage de la zone, etc. La source de l'image (le chemin vers le fichier) est une URL. Ainsi le fichier peut être local : mydir/myimage1.png, ou il peut être distant : « http://www.example.com/images/myimage1.png ».

     Image {
         source: "images/qt-logo.svg"
     }

Cela affiche une image, comme nous l'attendions, dans le coin supérieur gauche de la fenêtre. La position par défaut est x = 0, y = 0. Cet exemple utilise un fichier SVG mais nous aurions pu utiliser l'un des nombreux autres formats acceptés, incluant le PNG, JPG et GIF.

Repositionnons l'image et agrandissons-la. Plaçons-la à la même position en x que le texte « Hello world again » mais plaçons-la 50 pixels en dessous du texte, puis donnons-lui une taille de 150 par 150 pixels :

     Image {
         source: "images/qt-logo.svg"
         x: 100; y: 150
         width: 150; height: 150
     }

En regroupant tout ce que nous avons vu, nous pouvons écrire un petit fichier QML qui commence à ressembler à quelque chose.

 import QtQuick 1.0
 
 Rectangle {
     id: myRectangle
     width: 500
     height: 400
 
     Text {
         text: "<h1>Hello world again</h1>"
         color: "#002288"
         x: 100; y: 100
     }
 
     Image {
         source: "images/qt-logo.svg"
         x: 100; y: 150
         width: 150; height: 150
     }
 
     color: "lightgray"
 }

Le résultat reste simple.

image

Ancres : Aligner des éléments

L'utilisation du positionnement absolu, tel que x = 100 et y = 150, fonctionne bien tant que l'utilisateur ou le développeur ne modifie pas la taille de la fenêtre. Si cela arrive, les positions doivent être recalculées. Une bonne chose serait d'avoir un positionnement relatif des objets dans la fenêtre ou le rectangle. Par exemple, si nous voulons placer une image en bas du rectangle, nous aimerions spécifier l'emplacement de l'image comme ‹le bas de la fenêtre› et non en utilisant des coordonnées. Nous pouvons le faire avec les propriétés d'ancres, que les objets possèdent grâce à l'héritage d'Item.

Les propriétés d'ancres sont en fait un groupe de propriétés. C'est un ensemble de propriétés liées. Il contient des propriétés pouvant être utilisées grâce à la notation « point ».

La notation « point » utilise l'identifiant id de l'objet et les noms de propriétés pour utiliser un objet ou une propriété particulière. Mettons que nous ayons un rectangle r1, contenant un rectangle r2, contenant un élément item1, qui possède une propriété ‹x› que nous souhaitons changer. Nous pouvons identifier celle-ci avec la notation « point »: r1.r2.item1.x

Si nous souhaitons positionner l'image en bas du rectangle dans lequel elle se situe, nous devons spécifier que le bas de l'image est en bas du rectangle :

 import QtQuick 1.0
 
 Rectangle {
     id: myWin
     width: 500
     height: 400
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         width: 150; height: 150
         anchors.bottom: myWin.bottom
     }
 }

Cela place le logo en bas à gauche de la fenêtre.

image

Nous aimerions que l'image soit centrée et ne touchant pas le bas de la fenêtre, pour des raisons esthétiques. Pour le centrage nous utilisons la propriété horizontalCenter et pour éviter que l'image ne touche le bas du rectangle, la propriété bottomMargin est utilisée. Donc les nouvelles actions du script sont :

  • définir le bas de l'image (anchors.bottom) comme le bas de la fenêtre ;
  • déplacer l'image pour la centrer horizontalement dans la fenêtre ;
  • définir une marge de 10 pixels pour que l'image ne touche pas la bordure basse de la fenêtre.

En script QML cela devient :

 import QtQuick 1.0
 
 Rectangle {
     id: myWin
     width: 500
     height: 400
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         width: 150; height: 150
         anchors.bottom: myWin.bottom
         anchors.horizontalCenter: myWin.horizontalCenter
         anchors.bottomMargin: 10
     }
 }

Exécutez ceci et redimensionnez la fenêtre. Vous allez voir que la position de l'image s'ajuste durant le redimensionnement.

image

Vous pouvez aussi ajouter un autre objet, disons un texte descriptif et le placer au-dessus, en dessous, ou à côté de l'image. Le code suivant place le texte juste au dessus de l'image :

     Text {
         text: "<h2>The Qt Logo</h2>"
         anchors.bottom: image1.top
         anchors.horizontalCenter: myWin.horizontalCenter
         anchors.bottomMargin: 15
     }

image

Note : anchors est un groupe de propriétés, à utiliser à l'intérieur de l'objet. Lors du référencement de ces propriétés dans un autre objet, nous utilisons la propriété directement, à la place de :

 myRectangle.anchors.top  // Faux

nous utilisons

 myRectangle.top         // Juste

Transformations

Nous pouvons transformer un objet graphique pour obtenir des effets supplémentaires. Tourner un texte de 180 degrés pour l'afficher renversé, tourner une image de 90 degrés pour la coucher sur le côté : ces transformations demandent des informations supplémentaires.

Pour les rotations, les informations supplémentaires sont : le point d'origine, l'axe de rotation et l'angle de rotation en degrés dans le sens horaire. L'axe n'est pas obligatoirement l'axe z, la ligne entre vos yeux et l'image, il peut être autour de l'axe vertical y ou de l'axe horizontal x. Nous avons trois dimensions à notre disposition. Afin de simplifier l'exemple, nous tournons de 90 degrés autour de l'axe z dans le sens antihoraire.

Nous avons suggéré une rotation du texte. Il peut être utile de redimensionner le texte, et nous pouvons faire les deux. La propriété transform est une liste liste d'éléments Transform, donc en utilisant la syntaxe des listes :

 myList: [ listElement1, listElement2, ... } ]

nous pouvons produire une liste de transformations.

Le texte va être tourné de 45 degrés dans le sens antihoraire, agrandi verticalement d'un facteur 1,5 et horizontalement d'un facteur 1,2.

En utilisant comme base l'exemple précédent, nous avons :

 import QtQuick 1.0
 
 Rectangle {
     id: myWin
     width: 500
     height: 400
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         width: 150; height: 150
         anchors.bottom: myWin.bottom
         anchors.horizontalCenter: myWin.horizontalCenter
         anchors.bottomMargin: 10
 
         transform: Rotation {
             origin.x: 75; origin.y: 75
             axis{ x: 0; y: 0; z:1 }  angle: -90
         }
 
     }
 
     Text {
         text: "<h2>The Qt Logo -- taking it easy</h2>"
         anchors.bottom: image1.top
         anchors.horizontalCenter: myWin.horizontalCenter
         anchors.bottomMargin: 15
 
         transform: [
             Scale { xScale: 1.5; yScale: 1.2 } ,
 
             Rotation {
                 origin.x: 75; origin.y: 75
                 axis{ x: 0; y: 0; z:1 }  angle: -45
             }
         ]
     }
 }

Le bloc de code dans image1 commençant par transform spécifie que la propriété transform est une Rotation de -90 degrés, c'est-à-dire antihoraire, autour de l'axe z, passant par le centre de l'image, c'est dire la position (75, 75) car l'image est de 150 × 150 pixels.

L'autre transformation disponible est Translate. Elle entraine un changement de position de l'élément.

Note : dans une liste de transformations, l'ordre des transformations est important. Dans l'exemple ci-dessus, essayez d'inverser le redimensionnement avec la rotation, sans oublier d'enlever ou ajouter une virgule. Les deux résultats sont acceptables pour notre petit test, mais ils sont différents.

Animations

Une animation en QML est effectuée en animant les propriétés des objets. Les propriétés sont des nombres, couleurs, Rectangles, points et directions. Dans QML ce sont des types basiques de QML nommés real, int, color, rect, point, size et vector3d. Il y a différentes possibilités pour créer une animation. Nous allons en voir quelques-unes.

Animation numérique

Précédemment, nous avons utilisé une rotation pour changer l'orientation de l'image. Nous pourrions facilement animer cette rotation, en remplaçant la rotation directe de l'élément de 90 degrés par une rotation animée de l'image sur 360 degrés. L'axe de la rotation ne changera pas, la position du centre de l'image non plus. Dans ce cas, une NumberAnimation de l'angle de rotation suffira. Si nous souhaitons une simple rotation autour du centre de l'image alors nous pouvons utiliser la propriété rotation héritée de Item. La propriété rotation est un nombre réel qui indique l'angle de rotation de l'objet dans le sens horaire. Voici le code de notre image animée d'une rotation :

 import QtQuick 1.0
 
 Rectangle {
     id: mainRec
     width:  600
     height: 400
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         x: 200; y: 100
         width: 100; height: 100
 
         // Anime la rotation
         transformOrigin: Item.Center
         NumberAnimation on rotation {
             from: 0; to: 360
             duration: 2000
             loops: Animation.Infinite
         }
     }
 }

Le transformOrigin: Item.Center est redondant car c'est la valeur par défaut de la rotation. Mais si vous changez Center en BottomRight vous verrez une variation intéressante.

De plus, si une transformation Rotation avait été utilisée alors nous aurions pu avoir plus de contrôle sur les différents paramètres. Nous pouvions varier l'axe, pour non seulement pouvoir déplacer l'axe z, mais pouvoir utiliser l'axe y, l'axe x, ou une de leurs combinaisons. Par exemple, si notre objectif est d'animer une rotation à partir du centre de l'image autour de l'axe y, nous aurions le code suivant :

 import QtQuick 1.0
 
 Rectangle {
     id: mainRec
     width:  600
     height: 400
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         x: 200; y: 100
         width: 100; height: 100
 
         // Anime la rotation
         transform: Rotation {
             origin.x: 50; origin.y: 50; axis {x:0; y:1; z:0} angle:0
             NumberAnimation on angle  {
                 from: 0; to: 360;
                 duration: 3000;
                 loops: Animation.Infinite
             }
         }
     }
 }

Nous utilisons un rectangle de 600 sur 400 pixels. Dans ce rectangle une image de 100 par 100 pixels est insérée. Elle est tournée en son centre autour de l'axe y, ce qui a l'apparence d'une rotation autour d'un fil vertical invisible auquel l'image serait suspendue. Le temps de révolution est de 3 secondes (3000 millisecondes). La NumberAnimation est appliquée sur l'angle en partant de 0 (aucun changement) à 360 degrés, revenant au point où elle a commencé. Strictement parlant, il n'est pas nécessaire d'aller de 0 à 360 car cela duplique le même emplacement mais cela rend l'exemple plus facile à lire et ne change rien visuellement. Le nombre de boucles pour l'animation est défini comme Animation.Infinite ce qui signifie que l'animation va s'effectuer indéfiniment.

Pour voir une variation intéressante, changez l'axe à axis { x:1; y:1; z:1 }. C'est une ligne allant du centre de l'image vers le bas, à droite et en sortant de l'écran. Bien que le changement soit simple, la rotation semble complexe.

Animation séquentielle

Pour une animation plus complexe nous avons besoin de deux images. La première image sera positionnée au centre d'une fenêtre (Rectangle) et la seconde sera dans le coin supérieur gauche de la fenêtre. L'animation déplacera la deuxième image du coin supérieur gauche au coin inférieur droit. En faisant cela, nous allons animer la position et la taille de l'image.

Premièrement, créons deux images :

 import QtQuick 1.0
 
 Rectangle {
     id: mainRec
     width:  600
     height: 400
     z: 0
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         x: 20; y: 20 ; z: 1
         width: 100; height: 100
     }
 
     Image {
         id: image2
         source: "images/qt-logo.svg"
         width: 100; height: 100
         x: (mainRec.width - 100)/2; y: (mainRec.height - 100)/2
         z: 2
     }
 }

Nous allons ajouter une SequentialAnimation à l'image image1 de x = 20 à la cible x = 450. Les valeurs ‹from› seront utilisées car nous répétons l'animation, donc l'objet doit connaitre sa position d'origine, pour les deux axes, x et y. La SequentialAnimation de x sera répétée, grâce à la valeur Animation.Infinite pour la propriété ‹loop› qui signifie un cycle infini. De plus il y aura une NumberAnimation pour faire varier la propriété numérique des valeurs de x sur une certaine durée. Après la NumberAnimation il y aura une PauseAnimation qui mettra l'animation en pause pendant 500 millisecondes (une demi-seconde) simplement pour l'effet visuel.

     Image {
         id: image1
         source: "images/qt-logo.svg"
         width: 100; height: 100
 
         SequentialAnimation on x {
             loops: Animation.Infinite
             NumberAnimation {
                 from: 20; to: 450; easing.type: "InOutQuad";
                 duration: 2000
             }
             PauseAnimation { duration: 500 }
         }
     }

Un bloc de code similaire est écrit pour l'animation de la valeur ‹y› de la position.

Nous allons aussi animer le redimensionnement de l'objet durant son déplacement du coin supérieur gauche au coin inférieur droit de la fenêtre. Il deviendra plus petit avant d'atteindre le milieu, puis grossira. Pour compléter l'animation, nous allons définir les valeurs de ‹z› des images. La valeur ‹z› indique l'ordre d'empilement. L'axe z indique la direction entre l'écran et vos yeux. La valeur par défaut est 0. Donc si nous définissons le Rectangle avec un z de 0, juste pour être sur, l'image1 à 1 et l'image2 à 2 alors l'image1 sera au fond et l'image2 en premier plan. Lorsque l'image1 passe l'image2, elle va être en dessous. Le code complet ressemble à :

 import QtQuick 1.0
 
 Rectangle {
     id: mainRec
     width:  600
     height: 400
     z: 0
 
     Image {
         id: image2
         source: "images/qt-logo.svg"
         width: 100; height: 100
         x: (mainRec.width - 100)/2; y: (mainRec.height - 100)/2
         z: 2
     }
 
     Image {
         id: image1
         source: "images/qt-logo.svg"
         x: 20; y: 20 ; z: 1
         width: 100; height: 100
 
         SequentialAnimation on x {
             loops: Animation.Infinite
             NumberAnimation {
                 from: 20; to: 450
                 easing.type: "InOutQuad"; duration: 2000
             }
             PauseAnimation { duration: 500 }
         }
 
         SequentialAnimation on y {
             loops: Animation.Infinite
             NumberAnimation {
                 from: 20; to: 250
                 easing.type: "InOutQuad"; duration: 2000
             }
             PauseAnimation { duration: 500 }
         }
 
         SequentialAnimation on scale {
             loops: Animation.Infinite
             NumberAnimation { from: 1; to: 0.5; duration: 1000 }
             NumberAnimation { from: 0.5; to: 1; duration: 1000 }
             PauseAnimation { duration: 500 }
         }
     }
 }

Le easing.type possède de nombreuses options, exprimées sous forme de chaines de caractères. Il indique les équations utilisées pour décrire l'accélération de la valeur de la propriété, pas forcément une position, en fonction du temps.

Par exemple, InOutQuad signifie qu'au début et à la fin de l'animation la vélocité ‹velocity› est faible mais que l'accélération et la décélération sont fortes. Tout comme une voiture accélérant au début et décélérant à la fin du voyage, avec la vitesse maximale atteinte au milieu du parcours. Examinez la documentation easing et les différents graphiques qui montrent cet effet. L'axe horizontal, ‹progress›, peut être considéré comme le temps. L'axe vertical est la valeur de la propriété.

Au cours de cette discussion sur les animations, nous devons aussi décrire trois objets : State, MouseArea et Signals. Bien qu'indépendantes des éléments d'animations, les animations permettent d'illustrer parfaitement ces nouveaux éléments.

Résumé des animations

Nom Description
PropertyAnimation Une propriété d'un objet cible passe d'une valeur à une autre en un temps donné.
NumberAnimation Anime une propriété numérique depuis une valeur vers une autre en un temps donné.
PauseAnimation Met la tâche en attente pendant un temps donné, en millisecondes.
SequentialAnimation Nous permet de lister des évènements d'animation qui vont être exécutés séquentiellement. A en premier, puis B, puis C, etc.
ParallelAnimation Nous permet d'exécuter différentes animations en même temps au lieu de les exécuter séquentiellement.

Utilisation des états

Un état est un ensemble de valeurs définies dans la configuration d'un objet et qui dépend souvent de l'état précédent. Par exemple, un verre peut être dans un état appelé ‹MoitiéPlein› s'il a été rempli avec un liquide et qu'il a atteint la moitié de sa capacité. Nous pourrions aussi avoir un état ‹MoitiéVide› qui serait un état qui apparaîtrait lorsque la quantité de liquide diminue jusqu'à la moitié de la capacité du verre. Les deux états représentent la même quantité de liquide mais nous les considérons différents. De la même façon, les états dans un programme représentent non seulement des valeurs mais peuvent aussi inclure la façon dont les valeurs ont été atteintes.

Lorsqu'un état change une transition se produit. La transition permet d'appliquer des changements ou d'exécuter des actions qui dépendent du mouvement vers ce nouvel état. Par exemple, si nous avons une scène d'un paysage où la variable d'état possède deux états « jour » et « nuit », alors lors du changement d'état vers la « nuit », le ciel deviendra sombre, les étoiles apparaitront, le paysage sera assombri. Et lorsque l'état reviendra à « jour », les changements contraires seront appliqués : le ciel devient bleu, la scène devient verte et le soleil apparaît dans le ciel.

Voici un programme QML simple montrant le changement d'état de l'exemple ci-dessus. Nous avons deux rectangles, celui du haut est le ciel (sky) et celui du bas le sol (ground). Nous voulons animer le changement du jour vers la nuit. Il y aura deux états mais nous en définissons seulement un seul car le jour (daylight) est l'état par défaut. Nous allons juste aller vers la nuit (night) en cliquant et maintenant le bouton gauche de la souris. Le relâchement du bouton inversera le processus.

 import QtQuick 1.0
 
 Rectangle {
     id: mainRectangle
     width:  600
     height: 400
     color: "black"
 
     Rectangle {
         id: sky
         width: 600
         height: 200
         y: 0
         color: "lightblue"
     }
 
     Rectangle {
         id: ground
         width: 600; height: 200
         y: 200
         color: "green"
     }
 
     MouseArea {
         id: mousearea
         anchors.fill: mainRectangle
     }
 
     states: [ State {
             name: "night"
             when: mousearea.pressed == true
             PropertyChanges { target: sky; color: "darkblue" }
             PropertyChanges { target: ground; color: "black" }
         },
         State {
             name: "daylight"
             when: mousearea.pressed == false
             PropertyChanges { target: sky; color: "lightblue" }
             PropertyChanges { target: ground; color: "green" }
         }
     ]
 
     transitions: [ Transition {
             from: "daylight"; to: "night"
             ColorAnimation { duration: 1000 }
         },
         Transition {
             from: "night"; to: "daylight"
             ColorAnimation { duration: 500 }
         }
     ]
 }

Plusieurs nouveaux éléments sont apparus dans cet exemple. Premièrement, nous utilisons un élément MouseArea pour détecter le clic sur la souris dans le rectangle mainRectangle. Deuxièmement, nous utilisons une notation de liste [ thing1 , thing2, ... ] pour construire une liste d'états et une liste de transitions.

MouseArea définit une région qui répondra aux clics de souris. Dans ce cas nous nous intéressons seulement au fait qu'un des boutons de la souris soit appuyé ou non, sans nous soucier de quel bouton ni des autres détails. La zone de la MouseArea est la fenêtre principale dans son ensemble, donc en cliquant n'importe où dans cette région, l'animation démarrera. Comme nous utilisons seulement l'état de souris appuyé (pressed), l'animation se fera du jour (daylight) à la nuit (night) tant que le bouton de la souris reste appuyé.

Lorsque le bouton est relâché, l'état jour (daylight) revient et la transition entre la nuit (night) et le jour est déclenchée, causant l'exécution de l'animation. La transition spécifie la durée en millisecondes de la ColorAnimation, alors que l'état spécifie la couleur du nouvel état.

La commande PropertyChanges est le moyen d'indiquer quelles propriétés changent dans un changement d'état et quelles nouvelles valeurs ces propriétés auront. Par exemple, nous voulons pour l'état night que la région sky devienne bleu foncé et que la région ground devienne noire, et donc les rectangles de ces régions sont les cibles (target), et la propriété dans la cible est la couleur (color).

Signaux

Les signaux sont simplement des évènements qui peuvent être attachés à des actions que nous voulons exécuter. En QML ils sont généralement précédés du mot ‹on›, par exemple dans l'animation utilisant une MouseArea, le signal était onPressed. Si vous regardez dans la documentation C++ vous allez voir une longue description des signaux et slots. Les signaux sont connectés aux slots. Le signal représente un évènement, et le slot une fonction qui fait quelque chose en fonction de cet évènement. Vous pouvez aussi avoir des signaux connectés à d'autres signaux, faisant qu'un signal (évènement) déclenche un autre signal (évènement) et ainsi de suite. Il est toujours intéressant de savoir ce qui se passe derrière la couche QML, mais pas essentiel pour l'utiliser.

La plupart des éléments n'ont pas de signaux associés. Par contre, certains, comme l'élément Audio possèdent plusieurs signaux. Certains signaux de Audio sont utilisés pour représenter des évènements comme le fait que le son est arrêté, que la lecture commence, est mise en pause, ou que l'on atteint la fin du média. Ils permettent au développeur de connecter, par exemple, l'appui d'un bouton de l'interface utilisateur (peut-être une MouseArea) à un code QML qui gérera cet évènement.

Analyse d'un exemple: contrôle d'un cadran

Dans le dossier Qt examples/declarative/ui-components vous pouvez trouver un dossier dialcontrol qui contient l'exemple dialcontrol (contrôle d'un cadran).

image

Pour l'essentiel, cette petite application est constituée d'une glissière (slider) que vous pouvez positionner avec la souris et d'un cadran graphique répondant à la position de la glissière.

Le code de cet exemple est séparé en deux parties : Dial.qml et dialcontrol.qml.

Dial.qml peut être trouvé dans le sous-dossier content. Il définit un composant Dial similaire à un cadran de compteur de vitesse. Par la suite, l'exemple va contenir un composant glissière qui lorsqu'il sera déplacé, changera la position de l'aiguille sur le cadran.

Le code pour le Dial, identifié par le nom du fichier, contient quatre images dont l'ordre de recouvrement est le suivant : le fond (nombres et divisions), l'ombre de l'aiguille, l'aiguille elle-même, et la vitre (glass) recouvrant le cadran (contenant des couches transparentes).

L'image needle_shadow.png possède une Rotation affectée à l'attribut transformation transform de l'Image. La rotation est définie pour correspondre à la valeur de l'angle de l'image de l'aiguille needleRotation.angle. L'aiguille et son ombre possèdent la même valeur par défaut x et y mais l'origine de la rotation pour l'aiguille est légèrement différente afin que l'ombre soit visible lorsque l'aiguille bouge.

     Image {
         x: 96
         y: 35
         source: "needle_shadow.png"
         transform: Rotation {
             origin.x: 9; origin.y: 67
             angle: needleRotation.angle
         }
     }

Et l'aiguille:

     Image {
         id: needle
         x: 98; y: 33
         smooth: true
         source: "needle.png"
         transform: Rotation {
             id: needleRotation
             origin.x: 5; origin.y: 65
             //! [angle de l'aiguille]
             angle: Math.min(Math.max(-130, root.value*2.6 - 130), 133)
             Behavior on angle {
                 SpringAnimation {
                     spring: 1.4
                     damping: .15
                 }
             }
             //! [angle de l'aiguille]
         }
     }

L'image finale est la vitre qui possède simplement une position définie.

     Image { x: 21; y: 18; source: "overlay.png" }

dialcontrol.qml dans le dossier examples/declarative/ui-components/dialcontrol est le fichier principal de l'exemple. Il définit l'environnement visuel dans lequel le compteur sera placé. Comme le composant Dial et les images résident dans le sous-dossier content nous devons importer celui-ci dans dialcontrol.qml. Donc le début du fichier ressemble à :

 import QtQuick 1.0
 import "content"

L'espace visuel est limité à un Rectangle de 300 par 300 pixels ayant une couleur grise. Dans ce rectangle nous avons notre composant ‹Dial› et un Rectangle. À l'intérieur du rectangle appelé container se trouve un autre rectangle nommé sans grande originalité slider.

 Rectangle {
     color: "#545454"
     width: 300; height: 300
 
     // Cadran avec une glissière pour l'ajuster
     Dial {
         id: dial
         anchors.centerIn: parent
         value: slider.x * 100 / (container.width - 34)
     }
 
     Rectangle {
         id: container
         anchors { bottom: parent.bottom; left: parent.left
             right: parent.right; leftMargin: 20; rightMargin: 20
             bottomMargin: 10
         }
         height: 16
 
         radius: 8
         opacity: 0.7
         smooth: true
         gradient: Gradient {
             GradientStop { position: 0.0; color: "gray" }
             GradientStop { position: 1.0; color: "white" }
         }
 
         Rectangle {
             id: slider
             x: 1; y: 1; width: 30; height: 14
             radius: 6
             smooth: true
             gradient: Gradient {
                 GradientStop { position: 0.0; color: "#424242" }
                 GradientStop { position: 1.0; color: "black" }
             }
 
             MouseArea {
                 anchors.fill: parent
                 anchors.margins: -16 // Étend la zone de la souris largement en dehors du slider
                 drag.target: parent; drag.axis: Drag.XAxis
                 drag.minimumX: 2; drag.maximumX: container.width - 32
             }
         }
     }
     QuitButton {
         anchors.right: parent.right
         anchors.top: parent.top
         anchors.margins: 10
     }
 }

Le composant, nommé dial, est ‹ancré› au centre du rectangle principal. L'attribut value de dial est défini à une valeur basée sur la position horizontale du slider et la largeur de container. Donc le changement de la position du slider changera la valeur value du compteur qui est utilisée dans le cadran pour calculer la rotation de l'image de l'aiguille. Notez cette partie du code dans Dial où le changement de la valeur value modifie la position de l'aiguille :

             angle: Math.min(Math.max(-130, root.value*2.6 - 130), 133)
             Behavior on angle {
                 SpringAnimation {
                     spring: 1.4
                     damping: .15
                 }
             }

C'est cette partie de needleRotation qui fait tourner l'aiguille et son ombre. SpringAnimation est un élément qui modifie la valeur de rotation angle et imite le comportement oscillatoire de la source, avec la constante spring appropriée pour contrôler l'accélération et le damping pour contrôler la vitesse à laquelle l'effet disparaît.

Le container est gris clair avec un dégradé défini avec GradientStop. Le dégradé est appliqué verticalement. Si vous avez besoin d'un dégradé horizontal, vous pouvez appliquer un dégradé vertical et tourner l'élément de 90 degrés.

Le slider est gris foncé et possède aussi un dégradé vertical de couleur. La chose la plus importante à propos du slider est qu'il possède une MouseArea, qui spécifie un drag.target sur lui-même suivant l'axe X. Des valeurs minimales et maximales sur l'axe X sont définies, donc nous pouvons cliquer sur le ‹slider› et le glisser de gauche à droite dans les limites du ‹container›. Le déplacement du slider modifiera l'attribut value dans le cadran Dial comme vu ci-dessus.

Notez aussi l'utilisation d'une valeur radius pour un rectangle. Cela produit des coins arrondis. C'est ce qui permet au container et au slider d'afficher des formes rondes plaisantes à l'?il.

Remerciements

Merci à Alexandre Laurent pour la traduction ainsi qu'à Ilya Diallo, Jonathan Courtois et Claude Leloup pour leur relecture !

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt 4.7
Copyright © 2020 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'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur, une redirection cassée ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
Responsable bénévole de la rubrique Qt : Thibaut Cuvelier -

Partenaire : Hébergement Web