Apostille au MVC : les Delegate

Formation

En Ligne

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Méthodologie

    En ligne

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Le programme

Introduction du cours

Dans la partie 3 de leur tutoriel sur le C++, M@teo21 et Nanoc font état d'un chapitre intitulé L'architecture MVC avec les widgets complexes.

Ce chapitre constitue, pour le lecteur, une mise en bouche à l'architecture MVC mise en place par Qt ainsi que ses différents composants. Celui-ci traite principalement de l'aspect Modèle en présentant quelques modèles déjà existant comme QStringListModel ou encore QStandardItemModel, ainsi que de l'aspect View en présentant les trois grands types de vue disponibles avec Qt, à savoir QListView, QTableView ainsi que QTreeView.

Dans ce contexte, une apostille constitue un rajout. Dans ce tutoriel, je me propose d'introduire un composant non évoqué dans le tutoriel officiel : les Delegate

Au terme de ce mini-tutoriel, nous apprendrons ce que sont les Delegate, à quelle problématique ils répondent, et comment s'en servir, au travers d'un exemple simple.

Quelques piqûres de rappelL'architecture MVC

L'architecture Modèle Vue Contrôleur (Model View Controler) est un modèle de conception (design pattern) bien connu et beaucoup utilisé lors de la création d'interfaces graphiques. Originaire du langage Smalltalk, ce modèle permet de diviser une interface utilisateur en trois entités :

  • Le modèle contient les données. Il s'occupe du traitement et de l’interaction des données avec, par exemple, une base de données ;

  • La vue est la représentation graphique. Elle correspond à la partie affichage des données, de ce que l'utilisateur peut voir ;

  • Enfin, le contrôleur prend en charge l'interaction avec l'utilisateur, recevant tous les événements déclenchés par l'utilisateur (clic, sélection, ...) et mettant par la suite à jour les données.

Cette achitecture permet donc de séparer les différentes entités d'une application, aboutissant à une architecture flexible, claire, et maintenable.

Mise en oeuvre avec Qt

Dans sa version 4, Qt a introduit un ensemble de nouvelles vues mettant en oeuvre une architecture de type modèle/vue

Les modèles

Tous les modèles fournis par Qt sont basés sur la classe QAbstractItemModel. Cette classe est la plus abstraite, et la plus haute dans la hiérarchie des classes des différents modèles. Cette classe fournit une interface que tous les modèles doivent respecter, afin de pouvoir être utilisé correctement avec une vue.

De base, Qt fournit un ensemble de modèles pouvant être directement utilisés comme :

  • QStringListModel : modèle utilisé pour stocker une liste de QString ;

  • QFileSystemModel : modèle fournissant un ensemble d'informations sur un fichier ou un répertoire du système de fichier local (anciennement QDirModel) ;

  • QStandardItemModel : ce modèle permet de gérer tout de type de structure, complexe ou non ;

  • Et enfin, QSqlQueryModel, QSqlTableModel, QSqlRelationalTableModel, trois modèles utilisés pour accéder à une base de données (SQLite, MySQL, ...).

Cependant, il arrive parfois (voire souvent), que ces modèles ne conviennent pas à notre utilisation, et que l'on souhaite créer nos propres modèles. Pour cela, rien de réellement complexe. Il suffit en effet de créer une classe dérivant d'une des classes suivantes :

  • QAbstractItemModel : comme évoqué plus haut, il s'agit de la classe la plus abstraite ;

  • QAbstractListModel : classe abstraite fournissant une interface pour un modèle de type liste (associé à une QListView) ;

  • QAbstractTableModel : classe abstraite fournissant une interface pour un modèle de type tableau (associé à une QTableView) ;

A noter qu'il n'existe pas de classe séparée pour un modèle de type hiérarchique (QTreeView). Il s'agit simplement de QAbstractItemModel.

Vous remarquerez que toutes ces classes utilise la même racine, à savoir QAbstractXModel.

Une fois que l'on a choisi la classe de base convenant le mieux à notre utilisation, il ne reste plus qu'à redéfinir certaines méthodes virtuelles comme :

  • int QAbstractItemModel::rowCount(const QModelIndex & parent = QModelIndex()) const : fonction retournant le nombre de lignes du modèle ;

  • QVariant QAbstractItemModel::data (const QModelIndex & index, int role = Qt::DisplayRole ) const : fonction renvoyant la donnée associée au rôle role pour l'index index

Pour des modèles modifiables, il est aussi nécessaire de redéfinir la fonction bool QAbstractItemModel::setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole )

La création de modèles personnalisés n'est cependant pas l'objet de ce tutoriel. Bien que nous l'aborderons brièvement au travers d'un exemple, je vous renvoie à la documentation traitant le sujet en profondeur.

Les vues

Une fois que l'on dispose d'un modèle (déjà existant ou personnalisé), on dispose de trois types de vue :

  • QListView : liste d'éléments ;

  • QTableView : tableau d'éléments ;

  • QTreeView : représentation d'éléments sous forme hiérarchique (arbre d'éléments).

Une fois la vue choisie, le modèle y est affecté en utilisant la fonction void QAbstractItemView::setModel ( QAbstractItemModel * model ).

Pour d'avantage de précisions, libre à vous de vous reporter à la documentation ou bien au tutoriel officiel

Parlons du contrôleur

Comme vous l'aurez sans doute remarqué, nous n'avons jusqu'à présent pas évoqué le dernier aspect à savoir le Contrôleur. Voici la vérité : Qt n'utilise pas tout à fait une architecture MVC. Ce n'est plus un C mais désormais un D. Pour ceux qui n'auraient pas fait le rapprochement, il s'agit d'un D pour ... Delegate. Eh oui, maintenant, vous savez tout : Qt utilise en réalité une architecture model/view entourée d'un delegate.

Présentation du concept

Mais à quoi Diantre ce fameux Delegate sert-il ?

Au contraire d'une architecture MVC classique, l'architecture model/view instaurée par Qt ne fournit pas de réel composant permettant de gérer les interactions avec l'utilisateur. De ce fait, ceci est géré par la vue elle-même.

Cependant, pour des raisons de flexibilité, l'interaction et les entrées utilisateurs ne sont non pas prises en compte par un composant totalement séparé, à savoir le contrôleur, mais par un composant interne à la vue : le Delegate. Ce composant est responsable de deux choses :

  • Personnaliser l'édition des éléments au moyen d'un editor ;

  • Personnaliser le rendu des éléments à l'intérieur d'une vue.

Ainsi, grace à un delegate, il vous est possible de personnaliser la manière dont les entrées utilisateurs seront gérées, ainsi que le rendu des éléments.
Pour exemple, lorsque vous utilisez une QTableView avec un modèle éditable, lorsque vous cliquez sur une cellule de votre table, il vous est possible de modifier la valeur de la cellule, grace à une LineEdit.
Cela est possible grace au Delegate qui a fourni à la vue un moyen d'éditer les données au travers d'un composant de type QLineEdit.

Utiliser un Delegate existant

Vous aurez sans douté remarqué, que, par défaut, même si vous n'avez paramétré aucun delegate, votre vue se charge toute seule de fournir un moyen d'éditer vos données. Cela est du au fait que les vues sont déjà dotées d'un delegate par défaut : QStyledItemDelegate. En effet, de base, Qt fournit deux delegate par défaut :

  • QItemDelegate ;

  • QStyledItemDelegate.

La seule différence résidant entre ces deux Delegate est que QStyledItemDelegate utilise le style courant pour le rendu des données. Ces deux Delegate héritent cependant de la même classe, à savoir QAbstractItemDelegate, fournissant une interface de base générique à tous les delegate.
Qt fournit en outre QSqlRelationalDelegate, permettant d'éditer et afficher des données d'un QSqlRelationalTableModel.

Cependant, même si les vues sont dotés d'un delegate par défaut, il est bien entendu possible de modifier et paramétrer celui-ci grâce à la fonction void QAbstractItemView::setItemDelegate ( QAbstractItemDelegate * delegate ). Il est aussi possible de récupérer le delegate courant grâce à la fonction QAbstractItemDelegate * QAbstractItemView::itemDelegate () const

Jetons un coup d'oeil à la mécanique interne

Nous avons dit que le delegate était en partie chargé de fournir à l'utilisateur un moyen d'éditer les données au sein d'une vue, en fournissant un editor. Cet editor est tout simplement un composant de type QWidget. Même si précédemment nous avons évoqué l'exemple d'une QLineEdit, le delegate par défaut ne crée pas que des QLineEdit. En effet, celui-ci crée le composant adapté au type de données de la cellule. Par exemple, si la cellule contient un int, le delegate par défaut créera un composant de type QSpinBox. Pour un composant de type double, il s'agira d'un QDoubleSpinBox.

Regardons le code de %QTDIR%/src/gui/itemviews/qstyleditemdelegate.cpp, et notamment la méthode createEditor :

QVariant::Type t = static_cast<QVariant::Type>(index.data(Qt::EditRole).userType()); return d->editorFactory()->createEditor(t, parent);

Que font ces deux lignes ? Tout d'abord, le type de la donnée contenue dans la cellule est stoqué dans la variable t. Souvenez-vous, la fonction data() de QAbstractItemModel renvoyant une variable de type QVariant, celle-ci est capable de gérer de nombreux types de données. La fonction userType() renvoie ce type sous forme d'un int (ce qui justifie l'emploi de static_cast). Si la variable est de type QString, t serait donc égal à QVariant::String, QVariant::Date pour une variable de type QDate, ... La liste complète étant disponible ici

La deuxième ligne fait appel à la méthode createEditor de editorFactory().
La méthode editorFactory() étant définie comme :

const QItemEditorFactory *editorFactory() const { return factory ? factory : QItemEditorFactory::defaultFactory(); }

On y comprend donc que cette méthode retourne la factory par défaut si aucune n'a été définie. Mais qu'est-ce donc que cette fameuse factory ? QItemEditorFactory est une classe dite de fabrique (factory), permettant entre autre de créer l'editor correspondant au type de données que l'on a évoqué plus haut.

Par défaut, voici les composants crées en fonction du type :

Type

Widget

bool

QComboBox

int / unsigned int

QSpinBox

double

QDoubleSpinBox

QDate

QDateEdit

QDateTime

QDateTimeEdit

QPixmap

QLabel

QString

QLineEdit

QTime

QTimeEdit

Le delegate par défaut est donc capable de gérer une grande partie des types de données. Cependant, il se peut que nous ayons besoin d'un comportement autre que celui par défaut. Pour cela, il suffit de créer notre propre delegate, c'est ce que nous allons voir dans le chapitre suivant.

Créer son propre Delegate

Comme nous l'avons vu dans la partie précédente, nous disposons de trois classes de base déjà fournies par Qt :

  • QAbstractItemDelegate ;

  • QItemDelegate ;

  • QStyledItemDelegate.

Depuis Qt 4.4, il est recommandé d'utiliser QStyledItemDelegate. Nous l'utiliserons donc comme classe de base de nos delegate.

Lorsque l'on souhaite uniquement personnaliser l'édition des éléments dans notre vue, et non pas le rendu, nous devons redéfinir quatre méthodes :

createEditor()QWidget * QStyledItemDelegate::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const

Cette fonction retourne le widget (editor) pour éditer l'item se trouvant à l'index index. Le paramètre option permet de contrôler comment le widget apparait.

Cette fonction sera par exemple appelée lorsque l'utilisateur effectuera un double-clic sur la cellule d'une QTableView. L'editor retourné par la fonction sera alors présenté à l'utilisateur

setEditorData()void QStyledItemDelegate::setEditorData ( QWidget * editor, const QModelIndex & index ) const

Cette fonction permet de transmettre à l'editor editor les données à afficher à partir du modèle, se trouvant à l'index index

Une fois l'editor crée et ouvert, cette fonction sera ensuite appelée afin de remplir l'editor avec la donnée concernée, à partir du modèle. La donnée sera alors extraite du modèle puis affichée...

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Apostille au MVC : les Delegate

Prix sur demande