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  · 

Les modèles de données QML

Les éléments QML tels que ListView, GridView et Repeater nécessitent des modèles de données fournissant les données à afficher. Typiquement, ces éléments requièrent un composant délégué (delegate) qui crée une instance pour chaque élément du modèle. Les modèles peuvent être statiques ou voir leurs éléments modifiés, insérés, supprimés ou déplacés de manière dynamique.

Les données sont fournies au délégué au travers de rôles de données nommés, auxquels le délégué peut se lier. Voici un ListModel avec deux rôles type et age, et une ListView avec un délégué qui se lie à ces rôles pour afficher leurs valeurs :

 import QtQuick 1.0
 
 Item {
     width: 200; height: 250
 
     ListModel {
         id: myModel
         ListElement { type: "Dog"; age: 8 }
         ListElement { type: "Cat"; age: 5 }
     }
 
     Component {
         id: myDelegate
         Text { text: type + ", " + age }
     }
 
     ListView {
         anchors.fill: parent
         model: myModel
         delegate: myDelegate
     }
 }

Si un conflit de nommage se produit entre les propriétés du modèle et les propriétés du délégué, les rôles peuvent être accessibles par le nom qualifié model. Par exemple, si l?élément Text possède les propriétés type ou age, le texte ci-dessus affichera ces propriétés au lieu des valeurs type et age de l?élément modèle. Dans ce cas, les propriétés auraient pu être désignées par model.type et model.age, afin de s?assurer que le délégué affiche les valeurs des propriétés de l?élément modèle.

Le rôle spécial index, qui contient l?index de l?élément dans le modèle, est également utilisable par le délégué. Il est à noter que l?index est fixé à -1 si l?élément est supprimé du modèle. Si on souhaite se connecter au rôle index, on doit s?assurer que l?algorithme prend en compte la possibilité d?un index égal à -1, à savoir que l?élément n?est plus valide (en général, l?élément sera détruit rapidement, mais il est possible de différer la destruction du délégué dans certaines vues grâce à la propriété attachée delayRemove).

Les modèles qui ne possèdent pas de rôles nommés (comme par exemple le modèle QStringList présenté ci-dessous), verront leurs données renseignées par l?intermédiaire du rôle modelData. Le rôle modelData est également fourni aux modèles qui ne possèdent qu'un seul rôle. Dans ce cas, le rôle modelData contient les mêmes données que le rôle nommé.

QML fournit différents types de modèles de données parmi l?ensemble des éléments QML intégrés. De plus, les modèles peuvent être créés avec C++ puis rendus disponibles aux composants QML.

Les vues utilisées pour accéder aux modèles de données sont décrites dans Présenter les données avec QML. L?utilisation des éléments de positionnement pour organiser les éléments d?un modèle est décrite dans Utiliser les éléments de positionnement et de répétition.

Les modèles de données QML

ListModel

ListModel est une hiérarchie simple d?éléments spécifiés en QML. Les rôles disponibles sont spécifiés par les propriétés ListElement.

     ListModel {
         id: fruitModel
 
         ListElement {
             name: "Apple"
             cost: 2.45
         }
         ListElement {
             name: "Orange"
             cost: 3.25
         }
         ListElement {
             name: "Banana"
             cost: 1.95
         }
     }

Le code ci-dessus possède deux rôles, name et cost. Ceux-ci peuvent être liés à un délégué ListView, par exemple :

     ListView {
         anchors.fill: parent
         model: fruitModel
         delegate: Row {
             Text { text: "Fruit: " + name }
             Text { text: "Cost: $" + cost }
         }
     }

ListModel fournit des méthodes pour manipuler directement les ListModel via JavaScript. Dans ce cas, les premiers éléments insérés déterminent les rôles disponibles pour toutes les vues utilisant le modèle. Par exemple, si un ListModel vide est créé et peuplé par JavaScript, les rôles fournis par la première insertion sont les seuls rôles qui seront visibles dans cette vue :

     ListModel { id: fruitModel }
     ...
     MouseArea {
         anchors.fill: parent
         onClicked: fruitModel.append({"cost": 5.95, "name":"Pizza"})
     }

Quand la MouseArea sera cliquée, fruitModel possèdera deux rôles, cost et name. Même si des rôles sont ajoutés par la suite, seuls les deux premiers seront gérés par les vues utilisant le modèle. Pour réinitialiser les rôles disponibles dans le modèle, on appellera la méthode ListModel::clear().

XmlListModel

XmlListModel permet la construction d?un modèle à partir d?une source de données XML. Les rôles sont spécifiés par l?élément XmlRole.

Le modèle suivant possède trois rôles, title, link et description :

 XmlListModel {
      id: feedModel
      source: "http://rss.news.yahoo.com/rss/oceania"
      query: "/rss/channel/item"
      XmlRole { name: "title"; query: "title/string()" }
      XmlRole { name: "link"; query: "link/string()" }
      XmlRole { name: "description"; query: "description/string()" }
 }

La démonstration RSS News montre comment XmlListModel peut être utilisé pour afficher un flux RSS.

VisualItemModel

VisualItemModel permet aux éléments QML d'être fournis en tant que modèles.

Ce modèle contient à la fois des données et un délégué ; les éléments enfants d?un VisualItemModel fournissent le contenu au délégué. Les modèles ne fournissent aucun rôle.

  VisualItemModel {
      id: itemModel
      Rectangle { height: 30; width: 80; color: "red" }
      Rectangle { height: 30; width: 80; color: "green" }
      Rectangle { height: 30; width: 80; color: "blue" }
  }
 
  ListView {
      anchors.fill: parent
      model: itemModel
  }

Il est à noter que dans l?exemple ci-dessus, aucun délégué n?est requis. Les éléments du modèle fournissent eux-mêmes les éléments visuels qui seront positionnés dans la vue.

Les modèles de données C++

Les modèles peuvent être définis en C++, et rendus disponibles pour QML. Ceci est utile pour présenter des modèles de données C++ existants, ou des ensembles complexes de données, à QML.

Une classe modèle C++ peut être définie en tant que QStringList, QList<QObject*> ou QAbstractItemModel. Les deux premiers sont utiles pour présenter des ensembles de données simples, tandis que QAbstractItemModel fournit une solution plus flexible adaptée aux modèles plus complexes.

QStringList

Un modèle peut être une simple QStringList qui fournit le contenu de la liste via le rôle modelData.

Voici une ListView avec un délégué qui fait référence à la valeur de l?élément modèle en utilisant le rôle modelData :

 ListView {
     width: 100; height: 100
     anchors.fill: parent
 
     model: myModel
     delegate: Rectangle {
         height: 25
         width: 100
         Text { text: modelData }
     }
 }

Une application Qt peut charger ce document QML et affecter la valeur de myModel à une QStringList :

     QStringList dataList;
     dataList.append("Item 1");
     dataList.append("Item 2");
     dataList.append("Item 3");
     dataList.append("Item 4");
 
     QDeclarativeView view;
     QDeclarativeContext *ctxt = view.rootContext();
     ctxt->setContextProperty("myModel", QVariant::fromValeur(dataList));

L?exemple complet est disponible dans le répertoire Qt examples/declarative/modelviews/stringlistmodel.

À noter : il n?existe aucun moyen pour la vue de savoir que le contenu de la QStringList a été modifié. Si la QStringList est modifiée, il sera nécessaire de réinitialiser le modèle en appelant de nouveau QDeclarativeContext::setContextProperty().

QList<QObject*>

Une liste de valeurs QObject* peut servir de modèle. Une QList<QObject*> fournit les propriétés des objets de la liste en tant que rôles.

L?application suivante crée une classe DataObject avec des valeurs Q_PROPERTY qui seront accessibles en tant que rôles nommés quand une QList<DataObject*> sera exposée à QML :

 class DataObject : public QObject
 {
     Q_OBJECT
 
     Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
     Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
     ...
 };
 
 int main(int argc, char ** argv)
 {
     QApplication app(argc, argv);
 
     QList<QObject*> dataList;
     dataList.append(new DataObject("Item 1", "red"));
     dataList.append(new DataObject("Item 2", "green"));
     dataList.append(new DataObject("Item 3", "blue"));
     dataList.append(new DataObject("Item 4", "yellow"));
 
     QDeclarativeView view;
     QDeclarativeContext *ctxt = view.rootContext();
     ctxt->setContextProperty("myModel", QVariant::fromValeur(dataList));
     ...

Le QObject* est disponible en tant que propriété modelData. Par commodité, les propriétés de l?objet sont également disponibles directement dans le contexte du délégué. Ici, view.qml se réfère aux propriétés de DataObject dans le délégué ListView :

 ListView {
     width: 100; height: 100
     anchors.fill: parent
 
     model: myModel
     delegate: Rectangle {
         height: 25
         width: 100
         color: model.modelData.color
         Text { text: name }
     }
 }

On notera l?utilisation de l?accès entièrement qualifié à la propriété color. Les propriétés de l?objet ne sont pas dupliquées dans l?objet model, étant donné qu?elles sont facilement disponibles grâce à l?objet modelData.

L?exemple complet est disponible dans le répertoire Qt examples/declarative/modelviews/objectlistmodel.

À noter : il n?existe aucun moyen pour la vue de savoir que le contenu de la QList a été modifié. Si la QList est modifiée, il sera nécessaire de réinitialiser le modèle en appelant de nouveau QDeclarativeContext::setContextProperty()

QAbstractItemModel

Un modèle peut être défini en dérivant QAbstractItemModel. C?est la meilleure approche si vous avez un modèle plus complexe qui ne peut pas être pris en charge par les autres approches. Un QAbstractItemModel peut également informer automatiquement une vue QML que les données du modèle ont été modifiées.

Les rôles d?une classe dérivée de QAbstractItemModel peuvent être présentés à QML en appelant QAbstractItemModel::setRoleNames(). Les noms par défaut des rôles définis par Qt sont :

Rôle Qt Nom du rôle QML
Qt::DisplayRole display
Qt::DecorationRole decoration

Voici une application avec une classe dérivée de QAbstractListModel nommée AnimalModel qui possède les rôles type et size. Elle appelle QAbstractItemModel::setRoleNames() pour définir le nom des rôles permettant l?accès aux propriétés via QML :

 class Animal
 {
 public:
     Animal(const QString &type, const QString &size);
     ...
 };
 
 class AnimalModel : public QAbstractListModel
 {
     Q_OBJECT
 public:
     enum AnimalRoles {
         TypeRole = Qt::UserRole + 1,
         SizeRole
     };
 
     AnimalModel(QObject *parent = 0);
     ...
 };
 
 AnimalModel::AnimalModel(QObject *parent)
     : QAbstractListModel(parent)
 {
     QHash<int, QByteArray> roles;
     roles[TypeRole] = "type";
     roles[SizeRole] = "size";
     setRoleNames(roles);
 }
 
 int main(int argc, char ** argv)
 {
     QApplication app(argc, argv);
 
     AnimalModel model;
     model.addAnimal(Animal("Wolf", "Medium"));
     model.addAnimal(Animal("Polar bear", "Large"));
     model.addAnimal(Animal("Quoll", "Small"));
 
     QDeclarativeView view;
     QDeclarativeContext *ctxt = view.rootContext();
     ctxt->setContextProperty("myModel", &model);
     ...

Le modèle est affiché par un délégué ListView qui accède aux rôles type et size :

 ListView {
     width: 200; height: 250
     anchors.fill: parent
 
     model: myModel
     delegate: Text { text: "Animal: " + type + ", " + size }
 }

Les vues QML sont automatiquement mises à jour lorsque le modèle est modifié. Ne pas oublier que le modèle doit suivre les règles standard pour la modification de modèle, et informer la vue quand le modèle est modifié en utilisant QAbstractItemModel::dataChanged(), QAbstractItemModel::beginInsertRows(), etc. Voir la Référence pour dériver un modèle pour plus d?informations.

L?exemple complet est disponible dans le répertoire Qt examples/declarative/modelviews/abstractitemmodel.

QAbstractItemModel offre une hiérarchie de tables, mais les vues actuellement fournies par QML ne peuvent afficher que des données en liste. Afin d?afficher les listes d?enfants d?un modèle hiérarchique, l?élément VisualDataModel fournit plusieurs propriétés et fonctions à utiliser avec des modèles de type QAbstractItemModel :

Présenter des modèles de données C++ à QML

Les exemples précédents utilisent QDeclarativeContext::setContextProperty() pour définir les valeurs des modèles directement dans le composant QML. Une autre possibilité est d?enregistrer la classe modèle C++ en tant que type QML depuis un plug-in QML C++ en utilisant QDeclarativeExtensionPlugin. Ceci permet aux classes modèles d?être créées directement en tant qu?éléments à l?intérieur de QML :

 class MyModelPlugin : public QDeclarativeExtensionPlugin
 {
 public:
     void registerTypes(const char *uri)
     {
         qmlRegisterType<MyModel>(uri, 1, 0,
                 "MyModel");
     }
 }
 
 Q_EXPORT_PLUGIN2(mymodelplugin, MyModelPlugin);
 MyModel {
     id: myModel
     ListElement { someProperty: "some value" }
 }
 
 ListView {
     width: 200; height: 250
     model: myModel
     delegate: Text { text: someProperty }
 }

Voir le Tutoriel : écrire des extensions QML avec C++ pour les détails sur l?écriture de plug-ins QML C++.

Les autres modèles de données

Un entier

Un entier peut être utilisé pour définir un modèle contenant un certain nombre d'éléments. Dans ce cas, le modèle ne possède aucun rôle de donnée.

L'exemple suivant crée une ListView avec cinq éléments :

 Item {
     width: 200; height: 250
 
     Component {
         id: itemDelegate
         Text { text: "I am item number: " + index }
     }
 
     ListView {
         anchors.fill: parent
         model: 5
         delegate: itemDelegate
     }
 
 }

Une instance d'objet

Une instance d?objet peut être utilisée pour définir un modèle avec un unique élément objet. Les propriétés de l?objet sont fournies en tant que rôles.

L?exemple ci-dessous crée une liste avec un seul élément, contenant la couleur de l?objet texte myText. On notera l?utilisation de la propriété entièrement qualifiée model.color afin d?éviter un conflit avec la propriété color de l?élément Text dans le délégué.

 Rectangle {
     width: 200; height: 250
 
     Text {
         id: myText
         text: "Hello"
         color: "#dd44ee"
     }
 
     Component {
         id: myDelegate
         Text { text: model.color }
     }
 
     ListView {
         anchors.fill: parent
         anchors.topMargin: 30
         model: myText
         delegate: myDelegate
     }
 }

Accéder à des vues et des modèles depuis les délégués

Il est possible d?accéder aux vues utilisant un délégué, et à leurs propriétés, en utilisant ListView.view dans un délégué de ListView, GridView.view dans un délégué de GridView, etc. Il est notamment possible d?accéder au modèle et à ses propriétés en utilisant ListView.view.model.

Ceci est utile quand vous souhaitez utiliser le même délégué pour plusieurs vues, par exemple, mais que vous désirez avoir des décorations ou d?autres caractéristiques différentes pour chaque vue, et que vous souhaitez que ces différents réglages soient des propriétés de chacune des vues. De même, il peut être intéressant d?accéder à (ou d?exposer) certaines propriétés du modèle.

Dans l?exemple suivant, le délégué affiche la propriété language du modèle, et la couleur d?un des champs dépend de la propriété fruit_color de la vue.

 Rectangle {
      width: 200; height: 200
 
     ListModel {
         id: fruitModel
         property string language: "en"
         ListElement {
             name: "Apple"
             cost: 2.45
         }
         ListElement {
             name: "Orange"
             cost: 3.25
         }
         ListElement {
             name: "Banana"
             cost: 1.95
         }
     }
 
     Component {
         id: fruitDelegate
         Row {
                 Text { text: " Fruit: " + name; color: ListView.view.fruit_color }
                 Text { text: " Cost: $" + cost }
                 Text { text: " Language: " + ListView.view.model.language }
         }
     }
 
     ListView {
         property color fruit_color: "green"
         model: fruitModel
         delegate: fruitDelegate
         anchors.fill: parent
     }
 }

Un autre cas important est celui où certaines actions (par exemple un clic souris) dans le délégué doivent mettre à jour des données dans le modèle. Dans ce cas, on peut définir une fonction dans le modèle, par exemple :

         setData(int row, const QString & field_name, QVariant new_value)

... et l?appeler depuis le délégué en utilisant :

         ListView.view.model.setData(index, field, value)

... en supposant que field contient le nom du champ qui doit être mis à jour et que value contient la nouvelle valeur.

Remerciements

Merci à Lo?c Leguay 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