Introduction à Qt Quick pour les développeurs C++
Date de publication : 23/04/11. Date de mise à jour : 20/05/2011.
Par
Traducteur : Louis du Verdier (Site Web)
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.
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
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.
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.
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.

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.
| 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(&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 &);
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 &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 :
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


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.
Cette page est déposée.