Introduction à Qt Quick pour les développeurs C++

| Traducteur : Louis du Verdier | Qt Developer Network |

L'offre de cette expérience requiert que des designers et des développeurs travaillent plus que jamais ensemble. Finie l'époque où les designers pouvaient encore faire du pixel mapping à tout bout de champ et s'attendre que des développeurs implémentent leur vision. Sont également finis les jours où les développeurs codaient entièrement en faisant attention aux performances, sans jeter un œil au charme visuel. Le design, le développement et les tests doivent devenir un cycle itératif, non un chemin linéaire.

Commentez

- Titre : Introduction à Qt Quick pour les développeurs C++

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

- Parution : 23 avril 2011

- Mise à jour : 20 mai 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. Introduction

- III. Vue d'ensemble de Qt Quick

- IV. Une brève introduction à QML

- IV-A. Éléments visuels et hello world

- IV-B. Éléments visuels superposés

- IV-C. Éléments d'interaction : souris et toucher

- IV-D. Déclarations d'état

- IV-E. Composants QML

- IV-F. Éléments d'animation et transitions fluides

- IV-G. Modèle-vue et QML

- V. Qt Quick dans des applications C++

- V-A. Partager des données entre QML et C++

- V-B. Vues QML et modèles C++

- V-C. Exécution répartie entre C++ et QML

- V-C-1. L'appel de méthodes C++ depuis QML

- V-C-2. Un signal Qt envoyé à un gestionnaire QML

- V-D. Étendre QML depuis C++

- VI. Démarrer

- VII. 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 Introduction to Qt Quick for C++ Developers.

II - Introduction

Qt Quick est fait pour la manière avec laquelle les équipes de développement travaillent aujourd'hui. Le cœur de la logique de l'affaire est codé par les développeurs et optimisés pour les performances, l'interface est conçue par les designers travaillant avec des outils visuels et l'outillage intégré supporte des itérations aller-retour entre les disciplines.

Qt Quick fournit des performances, parce qu'il génère les applications Qt et le framework IU. Le framework Qt est connu pour ses hautes performances à l'exécution et pour son faible coût en ressources, le rendant idéal pour les mobiles, pour l'embarqué et pour les applications portables.

Le framework Qt est étendu par Qt Quick avec QML, un langage déclaratif qui détermine la façon de penser des designers. Chaque plan d'un scénarimage est déclaré en tant que branche d'une arborescence d'éléments, chaque aspect visuel d'un plan est déclaré en tant que propriété d'un élément d'une branche, chaque transition entre plans peut être décorée par une variété d'animations et d'effets.

Qt Quick inclut Qt Creator, un environnement de développement créé pour une collaboration entre les designers et les développeurs. Les designers travaillent dans un environnement visuel, les développeurs dans un EDI débordant de fonctionnalités et Qt Creator supporte l'itération aller et retour du design au code, au test et de retour au design.

III - Vue d'ensemble de Qt Quick

Qt Quick est composé du langage QML, du module C++ QtDeclarative qui intègre le langage QML avec les objets C++ et de l'outil Qt Creator, qui inclut des extensions pour supporter cet environnement. Qt Quick aide les développeurs et les designers à collaborer pour concevoir les interfaces utilisateur fluides qui deviennent de plus en plus courantes sur les dispositifs portables grand public, comme les téléphones portables, les lecteurs média, les set-top boxes et les netbooks. En utilisant le module C++ QtDeclarative, vous pouvez charger et interagir avec les fichiers QML depuis votre application Qt.

QML fournit des mécanismes pour créer déclarativement une arborescence d'objets en utilisant les éléments QML. QML améliore l'intégration entre le JavaScript et le système de types existant de Qt, basé sur QObject, améliore le support pour les liaisons automatiques de propriétés et fournit la transparence réseau au niveau du langage.

Les éléments QML sont un ensemble sophistiqué de blocs de construction graphique et comportementale. Ces différents éléments sont combinés dans les documents QML pour créer des composants s'étendant en complexité des simples boutons et sliders aux applications complètes pouvant accéder à internet comme le navigateur de photos Flickr.

Qt Quick forme l'actuelle force de Qt. QML peut être utilisé pour étendre progressivement une application existante ou pour générer complètement de nouvelles applications. QML est entièrement extensible depuis le C++, par le biais du module QtDeclarative.

L'interface Qt Creator de création de composants Qt Quick

IV - Une brève introduction à QML

QML est un langage riche et un traitement complet est un sujet hors du cadre de cet article. Ce dernier va à la place fournir une introduction à ce que peut faire QML et à la méthode d'intégration du C++ à QML pour tirer le meilleur des deux mondes : les performances considérables du C++ avec les interfaces utilisateur hautement dynamiques utilisant QML. Un détail complet des fonctionnalités de QML est disponible dans la documentation en ligne.

La compréhension de QML se fait à partir du concept des éléments. Un élément est le template d'un bloc de fabrication basique à l'extérieur duquel un programme QML sera conçu. QML supporte par exemple les éléments visuels de type Rectangle et Text, les interactions d'éléments de type MouseArea et Flipable, ainsi que les animations d'éléments de type RotationAnimation et Transition. Il existe également des types d'éléments complexes qui permettent au développeur de travailler avec les données, d'implémenter des vues dans des architectures modèle-vue, et d'autres types d'éléments dits ménagers qui risquent juste d'ajouter une dose de confusion à ce point de l'article.

Tous les éléments QML incluent une propriété ou plus (par exemple, color) qui peut être contrôlée par le développeur et la plupart des éléments incluent des signaux (par exemple, onClicked) qui peuvent être utilisés pour réagir aux évènements ou pour changer d'état.

IV-A - Éléments visuels et hello world

Assez de texte, le moment est venu de présenter l'exemple incontournable Hello World. Voici le code nécessaire à placer le texte Hello World en haut d'un simple rectangle d'arrière-plan :

Hello World

import Qt 4.7

Rectangle {
width: 300
height: 200
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "Hello World"
}
}

Commençons par disséquer ce simple code. L'exemple Hello World est un document QML, ce qui signifie que c'est un bloc complet de code source QML, prêt à être exécuté. Les documents QML correspondent généralement à des fichiers en texte simple stockés sur un disque ou sur des ressources en réseau, mais ils peuvent aussi être construits directement depuis des données textuelles.

Un document QML commence toujours avec une ou plusieurs déclarations d'importation. Ici, vous pouvez voir l'importation de Qt 4.7. Pour éviter que les éléments introduits dans des versions postérieures affectent des documents QML existants, les types d'éléments disponibles à l'intérieur d'un document sont contrôlés par les modules QML importés. En effet, QML est un langage versionné.

Après, vous pouvez voir le template d'élément Rectangle utilisé pour créer un objet actif. Les objets peuvent contenir d'autres objets, créant des relations de parenté. Dans le code ci-dessus, l'objet Rectangle est le parent de l'objet Text. L'élément Rectangle définit également une fenêtre de haut niveau pour la gestion des liens visuels et des segmentations de focus de l'intégralité de l'interface utilisateur.

À l'intérieur des objets, des propriétés sont liées à des valeurs par l'utilisation de déclarations sous la forme property : expression. Deux aspects de cette déclaration nécessitent des explications.

Dans un premier temps, l'expression est une expression JavaScript, ce qui signifie que vous pouvez définir des propriétés basées sur du calcul, une condition ou autres manipulations complexes en JavaScript. Par exemple, vous pouvez définir les proportions d'un rectangle à partir de la valeur d'une variable.

D'autre part, la liaison diffère de l'assignation. Dans une assignation, la valeur d'une propriété est définie quand la déclaration d'assignation est exécutée et est maintenue par la suite (sauf si la déclaration est à nouveau exécutée). Dans une liaison, une propriété est définie quand la déclaration de liaison est appelée en premier, mais va changer quand le résultat d'une expression utilisée pour définir la propriété est modifié (si vous le souhaitez, vous pouvez assigner une valeur à la propriété en utilisant property = expression à l'intérieur d'un bloc JavaScript).

Considérez ce qui se produit lorsque l'orientation change depuis un portrait à un paysage (possiblement en raison d'un capteur interne d'un appareil mobile). Grâce aux propriétés liées, les proportions du rectangle parent changeront et les ancres des éléments textuels vont réagir au changement afin de recentrer le texte.

La déclaration anchors.horizontalCenter: parent.horizontalCenter aligne le centre du texte avec le centre du rectangle parent. Les ancres fournissent un moyen de positionner un élément en spécifiant sa relation avec le parent ou de divers éléments (note : si vous souhaitez vérifier dans la documentation en ligne de l'élément Rectangle, vous ne verrez pas la propriété anchors.horizontalCenter listée. En regardant dans les environs, vous verrez que l'élément Rectangle hérite de toutes les propriétés de l'élément Item, l'élément Item fournissant la propriété anchors.horizontalCenter).

Il y a actuellement dix-sept propriétés-ancres disponibles, vous permettant d'aligner, de centrer et de remplir les éléments relatifs les uns aux autres et vous permettant de définir des marges et des offsets. Par exemple, le code qui suit montre un élément Text ancré à un élément Image, centré horizontalement et verticalement au-dessous, avec une marge.

utilisation d'ancres pour aligner les éléments

Text {
id: label
anchors.horizontalCenter: pic.horizontalCenter
anchors.top: pic.bottom
anchors.topMargin: 5
...
}

IV-B - Éléments visuels superposés

Les éléments visuels de QML peuvent se superposer avec gestion de la transparence en utilisant opacity : real, où real varie de 0 (transparent) à 1 (opaque). Pour des raisons de performances, il faut utiliser cela avec modération, plus particulièrement dans les animations, car chaque couche dans la scène va nécessiter d'être rendue au moment de l'exécution pour chaque image de l'animation. Cela peut être bien pour les prototypages rapides, mais, pour le déploiement final, il est préférable de faire autant que possible de prérendus de la scène, puis de simplement charger les maps de pixels au moment de l'exécution.

Le code qui suit produit une superposition de rectangles, un rouge et un bleu, avec la transparence invoquée de telle manière que la région de superposition soit violette. Notez comment le rectangle enfant (bleu) hérite des 50 % d'opacité de par son parent (rouge).

Superposition d'éléments transparents

Rectangle {
opacity: 0.5
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}

IV-C - Éléments d'interaction : souris et toucher

Pour ajouter une interaction de souris ou de toucher, vous devez ajouter un objet MouseArea. L'objet MouseArea permet à l'utilisateur de cliquer et de faire glisser la souris (ou le point de toucher). D'autres éléments d'interaction disponibles incluent Flickable, Flipable et FocusScope.

Notez que l'objet MouseArea peut être séparé des objets visuellement apparents, fournissant la flexibilité du design. Il est plus ou moins possible, par exemple, de créer la représentation visuelle d'un bouton sur lequel l'utilisateur cliquerait, puis de l'entourer avec une zone de souris plus grande qui permettrait à l'utilisateur de « rater«  l'élément visible de quelques pixels.

Pour introduire une région de souris dans l'exemple Hello World, le rectangle contenant le texte est fait en tant qu'enfant du nouveau rectangle qui définira la région de souris.

Interaction souris-toucher

import Qt 4.7Rectangle {
color: "#ff0000"
width: 310
height: 210
MouseArea {
anchors.fill: parent
onClicked: {
if (parent.color == "#ff0000") {
parent.color = "#ff9900";
} else {
parent.color = "#ff0000";
}
}
}
Rectangle {
width: 300
height: 200
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter

Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "Hello World"
}
}
}

L'élément MouseArea inclut des gestionnaires de signaux qui permettent d'écrire des expressions JavaScript qui vont être appelées à la suite de certains évènements ou changements d'état. Les gestionnaires disponibles incluent onClicked, onEntered, onExited, onPressed et onReleased. Dans l'exemple ci-dessus, le gestionnaire de signaux onClicked fait basculer la couleur du rectangle.

Cet exemple change la couleur du rectangle en réponse à tout clic valide. Un clic est défini en tant que pression suivie d'un relâchement, tous deux effectués à l'intérieur de la MouseArea (la pression, le déplacement à l'extérieur de la MouseArea, puis le retour à l'intérieur et le relâchement est également considéré comme un clic). La syntaxe complète du gestionnaire est MouseArea::onClicked (mouse), où le paramètre mouse fournit des informations à propos du clic, incluant les positions x et y du relâchement du clic et si le clic a été tenu. Notre exemple ne considère pas l'endroit où le clic s'est produit.

Le code interaction souris-toucher montre un cas simple d'état de visualisation, changeant une valeur en réponse à un évènement. La déclaration onClicked deviendra toutefois rapidement horrible si vous tentez de changer de multiples valeurs en réponse à de multiples états. C'est là que les déclarations d'états de QML entrent en jeu.

IV-D - Déclarations d'état

Les déclarations d'états QML définissent un ensemble de changements de valeur de propriétés depuis l'état de base. L'état de base est la déclaration initiale des valeurs de propriétés, il est exprimé par l'utilisation d'une chaine vide en tant que nom d'état. Après un changement d'état, vous pouvez toujours revenir à l'état de base en assignant une chaine vide à la propriété d'état.

Dans le code suivant, des états pour les deux couleurs sont implémentés. Dans la définition du rectangle rouge, la propriété d'ID est définie. Les noms d'objets peuvent être référencés par les ancêtres ou les descendants. Deux états sont également définis : rouge et orange. La propriété d'état est assignée pour donner à l'élément un état initial.

Les éléments d'état incluent une condition when qui peut être utilisée pour déterminer quand un état peut être appliqué. Vous pouvez voir ici que l'état rouge est appliqué quand la MouseArea est en train d'être enfoncée.

Définition d'états

id: buttonRect;

state: "red"
states:
State {
name: "red"
when: mouseArea.pressed == true
PropertyChanges {
target: buttonRect;
color: "red";
width: 80; height: 40
}
},
State {
name: "orange"
when: mouseArea.pressed == false
PropertyChanges {
target: buttonRect;
color: "#ff9900";
width: 120; height: 80
}
}

L'état défini ne détermine pas seulement la couleur pour chaque état, il fixe également la longueur et la hauteur du rectangle. L'état orange fournit un bouton plus large. Pour utiliser les états, la région de souris JavaScript onClicked est actualisée.

Transitions simples d'états

MouseArea {
anchors.fill: parent
onClicked: {
if (parent.state == "red") {
parent.state = "orange"
} else {
parent.state = "red";
}
}
}

Il est possible de définir un ensemble d'états en utilisant du code, comme dans cet exemple, ou en utilisant le composant d'édition graphique Qt Quick Designer de Qt Creator.

Pour créer des animations entre les états, des éléments de transition sont définis. Les éléments de transition peuvent utiliser l'information depuis l'état de base et depuis l'état cible pour interpoler les changements de propriétés en utilisant les éléments d'animation. Les éléments d'animation peuvent à leur tour utiliser un certain nombre de courbes d'assouplissement paramétrique et de techniques de groupage, donnant au développeur et au designer un haut degré de contrôle sur la méthode et le moment du changement de propriétés, durant une transition d'états. Ce point sera traité plus en détail par la suite.

IV-E - Composants QML

La partie concernant le code Hello World décrit le contenu d'un document QML. La manière par laquelle un document QML est appelé entre également en jeu. Un nom de document QML qui commence avec une lettre en majuscule définit un unique composant QML de haut niveau. Un composant QML est un template qui est interprété par le moteur d'exécution de QML de telle manière qu'il crée un objet avec des comportements prédéfinis. Comme il s'agit d'un template, un même composant QML peut être « lancé » de multiples fois pour produire plusieurs objets, chacun d'entre eux étant appelé instance du composant.

Une fois créées, les instances ne dépendent pas du composant les ayant créées, elles peuvent donc opérer sur des données indépendantes. Voici un exemple d'un composant Button simple (défini dans un fichier Button.qml) qui est instancié quatre fois par application.qml. Chaque instance est créée avec une différente valeur pour sa propriété de texte :

Utilisation du bouton quatre fois avec de différentes propriétés de texte

import Qt 4.7

Column {
spacing: 10

Button { text: "Apple" }
Button { text: "Orange" }
Button { text: "Pear" }
Button { text: "Grape" }

Le fichier Button.qml crée un composant de bouton

import Qt 4.7

Rectangle {
property alias text: textItem.text

width: 100; height: 30
border.width: 1
radius: 5
smooth: true

gradient: Gradient {
GradientStop { position: 0.0; color: "darkGray" }
GradientStop { position: 0.5; color: "black" }
GradientStop { position: 1.0; color: "darkGray" }
}

Text {
id: textItem
anchors.centerIn: parent
font.pointSize: 20
color: "white"
}

}

Note : les documents QML peuvent également créer des composants inline par l'utilisation de l'élément Component.

IV-F - Éléments d'animation et transitions fluides

Les effets d'animation sont la clé d'une interface utilisateur fluide. Dans QML, les animations sont créées en appliquant des objets d'animation sur des valeurs de propriétés d'objets pour les changer graduellement au fil du temps. Les objets d'animation sont créés depuis l'ensemble d'objets intégrés d'éléments d'animation, qui peuvent être utilisés pour animer de divers types de valeurs de propriétés. De plus, les objets d'animation peuvent être appliqués de différentes manières, selon le contexte dans lequel ils sont appelés.

Il existe un détail plus complet des animations dans QML dans la documentation en ligne. En tant qu'introduction, considérons les transitions.

Le code suivant montre le code nécessaire à animer le mouvement d'un rectangle. Il crée un objet Rectangle avec deux états : l'état par défaut et l'état déplacé ajouté. Dans l'état déplacé, la position du rectangle change à (50, 50). L'objet Transition spécifie que, quand le rectangle change de l'état par défaut à l'état déplacé, tout changement des propriétés x et y doit être animé, en utilisant Easing.InOutQuad.

Transitions d'états animées

import Qt 4.7

Rectangle {
id: rect
width: 100; height: 100
color: "red"

states: State {
name: "moved"
PropertyChanges { target: rect; x: 50; y: 50 }
}

transitions: Transition {
PropertyAnimation {
properties: "x,y";
easing.type: Easing.InOutQuad
}
}
}

Vous pouvez appliquer de multiples transitions à un élément comme dans le code suivant (gardez en tête que vous pouvez faire à un Rectangle tout ce que vous pouvez faire à un Item). Par défaut, une transition est appliquée à tous les changements d'état. Pour un meilleur contrôle, vous pouvez définir les propriétés from et topour appliquer une transition uniquement lors d'un changement depuis un état donné à un autre état donné ou tout simplement entre deux états donnés.

Transitions multiples

Item {
...
transitions:
Transition { ... }
Transition { ... }

}

IV-G - Modèle-vue et QML

L'utilisation de QML dans un design modèle-vue est un classique. QML peut créer des vues fluides et visuellement attirantes à l'intérieur de modèles si le modèle a été créé en C++ ou directement en QML.

QML fournit actuellement trois éléments dédiés à la création de vues à l'intérieur de modèles. Les éléments ListView et GridView créent respectivement une vue de liste et de grille. L'élément PathView expose des items au modèle fourni sous forme de chemin, par exemple un chemin en boucle qui vous permet de créer une interface carrousel à l'intérieur de la liste.

Créons deux vues différentes à l'intérieur d'un même modèle - un carnet d'adresses classique.

Vous pouvez générer les modèles directement en QML en utilisant l'élément ListModel parmi tant d'autres. Le code suivant montre comment créer un modèle de contacts où chaque contact possède un nom, un numéro de téléphone et une icône. Pour chaque élément dans la liste est défini un élément ListElement ; chaque entrée inclut deux rôles de données, name et icon. Sauvegardez le document dans le fichier ContactModel.qml pour y avoir accès plus tard (notez que c'est la lettre majuscule initiale qui fait de ce fichier un composant accessible).

Définition d'un modèle de liste en QML

import Qt 4.7

ListModel {
ListElement {
name: "Bill Jones"
number: "+1 800 555 1212"
icon: "pics/qtlogo.png"
}
ListElement {
name: "Jane Doe"
number: "+1 800 555 3434"
icon: "pics/qtlogo.png"
}
ListElement {
name: "John Smith"
number: "+1 800 555 5656"
icon: "pics/qtlogo.png"
}
}

Le code suivant utilise un élément ListView pour dessiner des éléments horizontalement ou verticalement. Le code définit la propriété du modèle au composant ContactModel tout juste créé. La propriété delegate fournit un template définissant chaque élément instancié par la vue. Dans ce cas, le template montre le nom et le nombre de rôles utilisant le composant Text intégré. Si vous le voulez, vous pouvez définir les composants delegate de la même manière qu'un autre composant QML.

Une vue de liste à l'intérieur d'un modèle de contacts

import Qt 4.7

ListView {
width: 180; height: 200

model: ContactModel {}
delegate: Text {
text: name + ": " + number
}
}

Maintenant, prenons un peu de bon temps et créons une vue à l'intérieur du modèle de contact qui ressemble à un carrousel 3D et qui permet à l'utilisateur de se déplacer dans la liste. La vue résultante et le code sont montrés dans le fragment qui suit. Notez la création d'un composant inline pour l'utiliser comme la propriété delegate dans l'élément PathView.

Rotation de la vue carrousel à l'intérieur du modèle de contacts

import Qt 4.7

Rectangle {
width: 240; height: 200

Component {
id: delegate
Column {
Image { anchors.horizontalCenter:
name.horizontalCenter;
width: 64; height: 64;
source: icon
}
Text { text: name; font.pointSize: 16 }
}
}

PathView {
anchors.fill: parent
model: ContactModel {}
delegate: delegate
path: Path {
startX: 120; startY: 100
PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 }
PathQuad { x: 120; y: 100; controlX: -20; controlY: 75 }
}
}
}

V - Qt Quick dans des applications C++

Qt Quick est livré avec son propre moteur d'exécution et permet le chargement de nouvelles fonctionnalités via des modules, rendant possible de développer des applications faites entièrement avec QML. Cependant, la réelle force de Qt Quick est sa capacité à s'intégrer dans une application C++.

Pour les besoins les plus basiques, par exemple l'intégration d'une vue QML dans un projet C++, le widget QDeclarativeView peut être utilisé. C'est un dérivé de QGraphicsView en incluant également les composants requis pour héberger une application QML. Alternativement, vous pouvez simplement créer de nouveaux types C++ disponibles au moteur d'exécution de QML par le biais de plug-ins et ces types peuvent faire tout ce que votre application C++ peut faire.

Dans des situations plus complexes, la méthode de procession dépend de la méthode dont le code C++ est généré.

Si vous commencez avec une application basée sur des widgets C++, vous pouvez réutiliser tous vos graphiques actifs et remanier les QWidget sous la forme QML. Puisque vous avez déjà fait le travail d'interaction complètement et le design, le développement et le codage du QML sont relativement simples.

Si vous avez à la place commencé avec une application basée sur un QGraphicsView, la procédure de conversion est bien plus simple et peut être effectuée par étapes. L'application QML entière peut être insérée dans une vue graphique en instanciant un moteur QML. Si désiré, l'interface QML peut coexister avec l'interface utilisateur existante et ainsi permettre à la procédure de conversion d'être faite par étapes.

Le code suivant montre les trois étapes requises pour ajouter un moteur et un contexte QML à une QGraphicsView existante. Il s'agit de créer dans un premier temps un environnement pour instancier les composants QML en utilisant la classe QDeclarativeEngine, puis d'encapsuler une définition de composant QML en utilisant QDeclarativeComponent. Enfin, le QGraphicsObject résultant peut être ajouté à la scène existante et coexister avec le reste de l'interface utilisateur.

Ajout d'un moteur QML à une QGraphicsView

QGraphicsScene *scene = ...;

QDeclarativeEngine *engine = new QDeclarativeEngine;
QDeclarativeComponent component(engine, QUrl::fromLocalFile(...));
QGraphicsObject *object =
qobject_cast<QGraphicsObject*>(component.create());

scene->addItem(object);

Si le composant n'arrive pas à charger le fichier QML, la propriété error sera définie à true. Pour avoir une sortie des messages d'erreur, la déclaration suivante peut être placée juste après l'appel à create().

qWarning() << component.errors();

Pour aligner les interfaces utilisateur, il est possible de transformer le QGraphicsObject et d'ajuster la z-value pour le placer à la bonne profondeur dans la scène. Afin d'atteindre des performances optimales pour la partie QML de l'interface utilisateur, il est recommandé de définir les options suivantes.

Optimisation des performances de l'interface QML

QGraphicsView *view = ...;

view->setOptimizationFlags(QGraphicsView::DontSavePainterState);
view->setViewportUpdateMode(
QGraphicsView::BoundingRectViewportUpdate);
view->setItemIndexMethod(QGraphicsScene::NoIndex);

Bien que la combinaison d'une interface utilisateur basée sur une vue graphique existante avec QML soit possible, il est recommandé de convertir la totalité sous la forme Qt Quick.

V-A - Partager des données entre QML et C++

Qt Quick fournit de nombreux moyens de partager les données entre le C++ et QML avec ou sans implémentation d'une architecture formelle modèle-vue. Il est également possible de déclencher des appels à des fonctions QML depuis le C++ et réciproquement. En toute généralité, l'exposition d'un QObject va rendre disponibles dans l'environnement QML tous ses signaux, slots et propriétés.

Tout code QML s'exécute dans le cadre d'un contexte. Ce contexte garde des traces de quelles données sont disponibles selon les permissions et les branches de l'arborescence d'objets de QML. Les données sont partagées en tant que propriétés contextuelles ou comme objets contextuels. Une propriété contextuelle est simplement un moyen d'exposer un QObject donné à travers un nom donné. Par exemple, pour exposer une propriété de QColor appelée frameColor à QML, utilisez tout simplement ce code :

QDeclarativeContext *context = ...;
context->setContextProperty("frameColor", QColor(Qt::red));

Cette propriété peut être accédée depuis l'intérieur du contexte QML en tant que propriété globale, comme définie ci-dessous. Souvenez-vous que les valeurs des propriétés sont liées et non assignées dans QML. Cela signifie que vous pouvez altérer la propriété frameColor depuis le C++, le changement sera reflété dans QML.

Rectangle {
border.color: frameColor
}

Il est possible d'ajouter de multiples propriétés contextuelles à un objet de QDeclarativeContext, mais, quand la liste de propriétés s'agrandit, la lisibilité du code faiblit. Au lieu de définir individuellement chaque propriété, il est plus joli de rassembler toutes les propriétés contextuelles dans un QObject et de définir un unique objet en tant qu'objet contextuel à la place.

Le code suivant montre comment définir l'objet d'interface MyInterface en utilisant la méthode setContextProperty(). La macro Q_PROPERTY définit les propriétés disponibles à l'intérieur de MyInterface au système de propriétés de Qt et définit les signaux de notification, permettant à des liens ultérieurs de fonctionner.

Notez que toutes les propriétés ajoutées explicitement par QDeclarativeContext::setContextProperty() sont prioritaires par rapport aux propriétés par défaut des objets contextuels.

Définition d'une interface utilisant setContextProperty()

class MyInterface : ... {
...
Q_PROPERTY(QAbstractItemModel *myModel READ model NOTIFY modelChanged)
...
};

MyInterface *myInterface = new MyInterface;
QDeclarativeEngine engine;
QDeclarativeContext *context = new
QDeclarativeContext(engine.rootContext());
context->setContextObject(myDataSet);
QDeclarativeComponent component(&amp;engine;);
component.setData("import Qt 4.7\nListView { model: myModel }", QUrl());
component.create(context);

V-B - Vues QML et modèles C++

Les propriétés d'objet fonctionnent bien lorsque l'on fournit un ensemble limité de valeurs à QML, mais sont difficiles à gérer lorsque de larges ensembles de données sont impliqués. Dans ce type de cas, les modèles formels sont visualisés avec des vues formelles. Cette architecture modèle-vue permet aux développeurs de séparer l'implémentation d'interfaces utilisateur de la partie logique, en supportant l'architecture modèle-vue. Le modèle peut être implémenté en C++ tandis que la vue est codée en QML.

QML crée des vues dans des modèles C++ qui sont exposés en utilisant l'interface QAbstractItemModel.

Pour exposer un QAbstractItemModel à QML, une propriété contextuelle est utilisée :

QAbstractItemModel *model = ...;
context->setContextProperty("dataModel", model);

V-C - Exécution répartie entre C++ et QML

Qt Quick permet à QML d'appeler des méthodes C++ et permet aux signaux C++ d'être gérés par des expressions JavaScript à l'intérieur d'un contexte QML.

V-C-1 - L'appel de méthodes C++ depuis QML

Afin de réagir aux données envoyées de l'utilisateur à la partie logique, QML doit être capable d'appeler des méthodes C++. Ceci peut être réalisé par le biais de slots ou de méthodes définies Q_INVOKABLE. En fournissant un accès QML à un QObject en tant que propriété contextuelle, les slots et les méthodes susceptibles d'être invoqués de cette classe peuvent être appelés depuis QML.

Par exemple, la classe suivante, dérivée de QObject, est ajoutée au contexte QML.

class CallableClass : public QObject
{
Q_OBJECT
...
public slots:
void cppMethod() { qDebug("C++ method called!"); }
};

...

context->setContextProperty("cppObject", new CallableClass);

Le code QML peut alors se référer à la méthode cppMethod utilisant l'objet global cppObject. Dans cet exemple, la méthode en question ne retourne pas plus de valeurs qu'elle n'accepte d'arguments, mais ce n'est pas un problème pour QML. Les valeurs de retour et les arguments de types supportés par QML sont supportés.

MouseArea {
...
onClicked: {
cppObject.cppMethod();
}
}

V-C-2 - Un signal Qt envoyé à un gestionnaire QML

Les signaux C++ peuvent être gérés par le JavaScript exécutant un contexte QML. Par exemple, la classe CallableClass de l'exemple précédent déclare également un signal, cppSignal().

class CallableClass : public QObject
{
Q_OBJECT
...
signals:
void cppSignal();
};

Utilisant un élément QML Connections, un gestionnaire de signaux peut être implémenté dans QML. Les éléments de connexion peuvent être utilisés pour gérer des signaux de n'importe quel objet cible, dont d'autres éléments QML. Le gestionnaire de signaux est appelé onSignalName, où la première lettre du nom du signal est mise en majuscule.

Connections {
target: cppObject
onCppSignal: { console.log("QML function called!"); }
}

V-D - Étendre QML depuis C++

QML a un support intégré pour un ensemble extensible de types d'éléments, mais, quand des besoins spécifiques à l'application apparaissent, il est possible d'étendre QML avec des types d'éléments personnalisés intégrés au C++. Par exemple, disons que vous avez un désir furieux d'avoir un élément QML appelé Person avec les propriétés name et shoeSize.

Tous les éléments QML sont une projection de types C++. Le code suivant déclare une classe Person basique en C++ avec les deux propriétés que nous voulons avoir accessibles dans le type de QML - name et shoeSize. Bien que nous utilisions dans cet exemple le même nom de classe pour la classe C++ et l'élément QML, la classe C++ peut être appelée différemment ou apparaître dans un espace de noms.

Déclaration d'une classe Person

class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)

public:
Person(QObject *parent = 0);

QString name() const;
void setName(const QString &amp;);

int shoeSize() const;
void setShoeSize(int);

private:
QString m_name;
int m_shoeSize;
};

Définition de la classe Person

Person::Person(QObject *parent)
: QObject(parent), m_shoeSize(0)
{
}

QString Person::name() const
{
return m_name;
}

void Person::setName(const QString &amp;n)
{
m_name = n;
}

int Person::shoeSize() const
{
return m_shoeSize;
}

void Person::setShoeSize(int s)
{
m_shoeSize = s;
}

L'implémentation de la classe Person est assez simple. Les accesseurs de la propriété retournent simplement les membres de l'instance de l'objet.

Le fichier main.cpp appelle également la fonction qmlRegisterType() pour inscrire le type Person auprès de QML dans la bibliothèque People en version 1.0 et définit la mise en cohérence des noms de classe C++ et QML.

Le type Person peut dès lors être utilisé depuis QML :

import People 1.0

Person {
name: "Bob Jones"
shoeSize: 12
}

VI - Démarrer

Cet article fournit une brève introduction à Qt Quick. Bien plus d'informations, de tutoriels et d'exemples de code existent pour que vous puissiez les visionner.

Pour plus d'informations à propos Qt Quick :

- essayez le tutoriel QML sur https://qt.developpez.com/doc/4.7/qml-tutorial.html ;

- la documentation complète des éléments QML est disponible en ligne sur https://qt.developpez.com/doc/4.7/qdeclarativeelements.html.

Pour commencer à travailler avec Qt Quick :

- téléchargez et installez les dernières versions de l'EDI Qt Creator (2.1 et plus récentes), qui donnent un aperçu d'un éditeur de texte QML avec une complétion de code, une coloration syntaxique et une aide sensible au contexte ; un éditeur visuel QML qui a été créé de toutes pièces avec QML ; un débogueur QML qui vous permet d'inspecter l'arbre d'éléments QML et ses propriétés au moment de l'exécution afin de vérifier la cadence des trames, d'évaluer les expressions JavaScript et tout cela à l'intérieur de Qt Creator ;

- dès que vous avez installé Qt Creator, passez en revue les exemples inclus, installés dans le répertoire YourInstalledRoot/examples/declarative et exposés à travers l'EDI ;

- vous pourrez trouver des discussions en ligne et des wikis couvrant le sujet de Qt Quick sur http://www.forum.nokia.com/Community et sur http://developer.qt.nokia.com/.

VII - Remerciements

Merci à Thibaut Cuvelier et à Claude Leloup pour leur relecture !