Mis à jour le 04/12/2018
  • 50 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Découvrez l''architecture MVC avec les widgets complexes

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous attaquons maintenant un des chapitres les plus intéressants de ce cours sur Qt, mais aussi un des plus difficiles.

Dans ce chapitre, nous apprendrons à manipuler 3 widgets complexes :

  • QListView : une liste d'éléments à un seul niveau.

  • QTreeView : une liste d'éléments à plusieurs niveaux, organisée en arbre.

  • QTableView : un tableau.

On ne peut pas utiliser ces widgets sans un minimum de théorie. Et c'est justement cette partie théorique qui me fait dire que ce chapitre sera l'un des plus intéressants : nous allons découvrir l'architecture MVC, une façon de programmer (on parle de design pattern) très puissante qui va nous donner énormément de flexibilité.

Présentation de l'architecture MVC

Avant de commencer à manipuler les 3 widgets complexes dont je vous ai parlé en introduction, il est indispensable que je vous présente l'architecture MVC.

Qu'est-ce que l'architecture MVC ? A quoi ça sert ? Quel rapport avec la création de GUI ?

MVC est l'abréviation de Model-View-Controller, ce qui signifie en français : "Modèle-Vue-Contrôleur".

...

... ça ne vous avance pas trop, j'ai l'impression. :p

Il s'agit d'un design pattern, une technique de programmation. C'est une "façon de programmer et d'organiser son code" bien pensée. Vous n'êtes pas obligés de programmer de cette manière-là, mais si vous le faites votre code sera plus lisible, plus clair et plus souple.

L'architecture MVC vous propose de séparer les éléments de votre programme en 3 parties :

  • Le modèle : c'est la partie qui contient les données. Le modèle peut par exemple contenir la liste des élèves d'une classe, avec leurs noms, prénoms, âges...

  • La vue : c'est la partie qui s'occupe de l'affichage. Elle affiche ce que contient le modèle.
    Par exemple, la vue pourrait être un tableau. Ce tableau affichera la liste des élèves si c'est ce que contient le modèle.

  • Le contrôleur : c'est la partie "réflexion" du programme. Lorsque l'utilisateur sélectionne 3 élèves dans le tableau et appuie sur la touche "Supprimer", le contrôleur est appelé et se charge de supprimer les 3 élèves du modèle.

C'est dur à imaginer au début. Mais rassurez-vous, vous allez comprendre au fur et à mesure de ce chapitre l'intérêt de séparer le code en 3 parties et le rôle de chacune de ces parties. Ce n'est pas grave si vous ne voyez pas de suite comment ces parties interagissent entre elles.

Commençons par un schéma, visuel et simple à retenir, qui présente le rôle de chacune de ces parties :

MVC
MVC

Comme on peut le voir sur ce schéma :

  • Le modèle est la partie qui contient les données (comment, on verra ça après). Les données sont généralement récupérées en lisant un fichier ou une base de données.

  • La vue est juste la partie qui affiche le modèle, ce sera donc un widget dans notre cas.
    Si un élément est ajouté au modèle (par exemple un nouvel élève apparaît) la vue se met à jour automatiquement pour afficher le nouveau modèle.

  • Le contrôleur est la partie la plus algorithmique, c'est-à-dire le cerveau de votre programme. S'il y a des calculs à faire, c'est là qu'ils sont faits.

L'architecture simplifiée modèle/vue de Qt

En fait (vous allez me tuer je le sens :D ), Qt n'utilise pas vraiment MVC mais une version simplifiée de ce système : l'architecture modèle/vue.

Et le contrôleur on en fait quoi ? Poubelle ? Notre programme ne réfléchit pas ?

Si si, je vous rassure. En fait, le contrôleur est intégré à la vue avec Qt.
Grâce à ça, les données sont toujours séparées de leur affichage, mais on diminue un peu la complexité du modèle MVC en évitant au programmeur d'avoir à gérer les 3 parties.

On s'éloigne donc un petit peu de la théorie pure sur MVC ici, pour s'intéresser à la façon dont Qt utilise ce principe en pratique.
Vous venez donc d'apprendre que Qt adaptait ce principe à sa manière, pour garder les bonnes idées principales sans pour autant vous obliger à "trop" découper votre code ce qui aurait pu être un peu trop complexe et répétitif à la longue.

Nous n'allons donc plus parler ici que de modèle et de vue. Comment sont gérés chacun de ces éléments avec Qt ?
Cette question... avec des classes, comme d'habitude ! :p

Les classes gérant le modèle

Il y a plusieurs types de modèles différents. En effet : on ne stocke pas de la même manière une liste d'élèves qu'une liste de villes !

Vous avez 2 possibilités :

  • Soit vous créez votre propre classe de modèle. Il faut créer une classe héritant de QAbstractItemModel. C'est la solution la plus flexible mais aussi la plus complexe, nous ne la verrons pas ici.

  • Soit vous utilisez une des classes génériques toutes prêtes offertes par Qt :

    • QStringListModel : une liste de chaînes de caractères, de type QString. Très simple, très basique. Ca peut suffire pour les cas les plus simples.

    • QStandardItemModel : une liste d'éléments organisés sous forme d'arbre (chaque élément peut contenir des sous-éléments). Ce type de modèle est plus complexe que le précédent, car il gère plusieurs niveaux d'éléments. Avec QStringListModel, c'est un des modèles les plus utilisés.

    • QDirModel : la liste des fichiers et dossiers stockés sur votre ordinateur. Ce modèle va analyser en arrière-plan votre disque dur, et restitue la liste de vos fichiers sous la forme d'un modèle prêt à l'emploi.

    • QSqlQueryModel, QSqlTableModel et QSqlRelationalTableModel : données issues d'une base de données. On peut s'en servir pour accéder à une base de données (ceux qui ont déjà utilisé MySQL, Oracle ou un autre système du genre seront probablement intéressés).
      Je ne vais pas rentrer dans les détails de la connexion à une base de données dans ce chapitre, ce serait un peu hors-sujet.

Toutes ces classes proposent donc des modèles prêts à l'emploi, qui héritent de QAbstractItemModel.
Si aucune de ces classes ne vous convient, vous devrez créer votre propre classe en héritant de QAbstractItemModel.

Héritage des modèles
Héritage des modèles

Notez que je n'ai pas représenté toutes les classes de modèles ici.

Les classes gérant la vue

Pour afficher les données issues du modèle, il nous faut une vue. Avec Qt, la vue est un widget, puisqu'il s'agit d'un affichage dans une fenêtre.

Tous les widgets de Qt ne sont pas bâtis autour de l'architecture modèle/vue, loin de là (et ça n'aurait pas d'intérêt pour les plus simples d'entre eux que nous avons vu jusqu'à présent).

On compte 3 widgets adaptés pour la vue avec Qt :

  • QListView
    QListView

    QListView : une liste d'éléments.

  • QTreeView
    QTreeView

    QTreeView : un arbre d'éléments, où chaque élément peut avoir des éléments enfants.

  • QTableView
    QTableView

    QTableView : un tableau.

Voilà donc les fameux "widgets" complexes que je vais vous présenter dans ce chapitre. Mais pour pouvoir les utiliser et les peupler de données, il faut d'abord créer un modèle !

Appliquez un modèle à la vue

Lorsqu'on utilise l'architecture modèle/vue avec Qt, cela se passe toujours en 3 temps. Il faut :

  1. Créer le modèle

  2. Créer la vue

  3. Associer la vue et le modèle

La dernière étape est essentielle. Cela revient en quelque sorte à "connecter" notre modèle à notre vue. Si on ne donne pas de modèle à la vue, elle ne saura pas quoi afficher, donc elle n'affichera rien. :p

La connexion se fait toujours avec la méthodesetModel() de la vue :

Modèle vue Qt
Modèle vue Qt

Voilà donc comment on connecte un modèle à une vue en pratique. :)
L'avantage de ce système, c'est qu'il est flexible. On peut avoir 2 modèles et appliquer soit l'un soit l'autre à la vue en fonction des besoins. Un gros intérêt est que dès que l'on modifie le modèle, la vue affiche instantanément les nouveaux éléments !

Plusieurs modèles ou plusieurs vues

On peut pousser le principe un peu plus loin. Essayons d'imaginer que l'on a plusieurs modèles ou plusieurs vues.

Plusieurs modèles et une vue

Imaginons que l'on ait 2 modèles : un qui contient une liste d'élèves, un autre qui contient une liste de capitales avec leur pays. Notre vue peut afficher soit l'un, soit l'autre :

Plusieurs modèles et une vue
Plusieurs modèles et une vue

Il faut bien sûr faire un choix : une vue ne peut afficher qu'un seul modèle à la fois !
L'avantage, c'est qu'au besoin on peut changer le modèle affiché par la vue en cours d'exécution, en appelant juste la méthodesetModel() !

Un modèle pour plusieurs vues

Imaginons le cas inverse. On a un modèle, mais plusieurs vues. Cette fois, rien ne nous empêche d'appliquer ce modèle à 2 vues en même temps !

Un modèle pour plusieurs vues
Un modèle pour plusieurs vues

On peut ainsi visualiser le même modèle de 2 façons différentes (ici sous forme de tableau ou de liste dans mon schéma).
Comme le même modèle est associé à 2 vues différentes, si le modèle change alors les 2 vues changent en même temps ! Par exemple, si je modifie l'âge d'un des élèves dans une cellule du tableau, l'autre vue (la liste) est automatiquement mise à jour sans avoir besoin d'écrire la moindre ligne de code !

Utilisation d'un modèle simple

Pour découvrir en douceur l'architecture modèle/vue de Qt, je vais vous proposer d'utiliser un modèle tout fait : QDirModel.

Sa particularité, c'est qu'il est très simple à utiliser. Il analyse votre disque dur et génère le modèle correspondant. Pour créer ce modèle, c'est tout bête :

QDirModel *modele = new QDirModel;

On possède désormais un modèle qui représente notre disque. On va l'appliquer à une vue.

Mais quelle vue utiliser ? Une liste, un arbre, un tableau ? Les modèles sont-ils compatibles avec toutes les vues ?

Oui, toutes les vues peuvent afficher n'importe quel modèle. C'est toujours compatible.

Par contre, même si ça marche avec toutes les vues, vous allez vous rendre compte que certaines sont plus adaptées que d'autres en fonction du modèle que vous utilisez.
Par exemple, pour un QDirModel, la vue la plus adaptée est sans aucun doute l'arbre (QTreeView). Nous essaierons toutefois toutes les vues avec ce modèle pour comparer le fonctionnement.

Le modèle appliqué à un QTreeView

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QDirModel *modele = new QDirModel;
    QTreeView *vue = new QTreeView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}

On crée le modèle, puis la vue, et on dit à la vue "Utilise ce modèle pour savoir quoi afficher" (ligne 9).

Le résultat est le suivant :

Vue du disque en arbre
Vue du disque en arbre

Une vue en forme d'arbre affiche le modèle de notre disque. Chaque élément peut avoir des sous-éléments dans un QTreeView, essayez de naviguer dedans :

Vue du disque en arbre
Vue du disque en arbre

Voilà un bel exemple d'arbre en action ! :D

Le modèle appliqué à un QListView

Maintenant, essayons de faire la même chose, mais avec une liste (QListView). On garde le même modèle, mais on l'applique à une vue différente :

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QDirModel *modele = new QDirModel;
    QListView *vue = new QListView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}
Vue du disque en liste
Vue du disque en liste

Ce type de vue ne peut afficher qu'un seul niveau d'éléments à la fois. Cela explique pourquoi je vois uniquement la liste de mes disques... et pourquoi je ne peux pas afficher leur contenu !

La vue affiche bêtement ce qu'elle est capable d'afficher, c'est-à-dire le premier niveau d'éléments.
Voilà la preuve qu'un même modèle marche sur plusieurs vues différentes, mais que certaines vues sont plus adaptées que d'autres !

Vous pouvez modifier la racine utilisée par la vue en vous inspirant du code suivant, que je ne détaillerai pas pour le moment :

vue->setRootIndex(modele->index("C:"));
Vue en liste
Vue en liste

Le modèle appliqué à un QTableView

Un tableau ne peut pas afficher plusieurs niveaux d'éléments (seul l'arbre QTreeView peut le faire). Par contre, il peut afficher plusieurs colonnes :

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QDirModel *modele = new QDirModel;
    QTableView *vue = new QTableView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}
Vue du disque en tableau
Vue du disque en tableau

Comme précédemment, on peut appeler setRootIndex() pour modifier la racine des éléments affichés par la vue.

Utilisation de modèles personnalisables

Le modèle QDirModel que nous venons de voir était très simple à utiliser. Rien à paramétrer, rien à configurer, il analyse automatiquement votre disque dur pour construire son contenu.

C'était une bonne introduction pour découvrir les vues avec un modèle simple. Cependant, dans la plupart des cas, vous voudrez utiliser vos propres données, votre propre modèle. C'est ce que nous allons voir ici, à travers 2 nouveaux modèles :

  • QStringListModel : une liste simple d'éléments de type texte, à un seul niveau.

  • QStandardItemModel : une liste plus complexe à plusieurs niveaux et plusieurs colonnes, qui peut convenir dans la plupart des cas.

Pour les cas simples, nous utiliserons donc QStringListModel, mais nous découvrirons aussi QStandardItemModel qui nous donne plus de flexibilité.

QStringListModel : une liste de chaînes de caractères QString

Ce modèle, très simple, vous permet de gérer une liste de chaînes de caractères. Par exemple, si l'utilisateur doit choisir son pays parmi une liste :

France
Espagne
Italie
Portugal
Suisse

Pour construire ce modèle, il faut procéder en 2 temps :

  • Construire un objet de type QStringList, qui contiendra la liste des chaînes.

  • Créer un objet de type QStringListModel et envoyer à son constructeur le QStringList que vous venez de créer pour l'initialiser.

QStringList surcharge l'opérateur "<<" pour vous permettre d'ajouter des éléments à l'intérieur simplement.
Un exemple de code sera sûrement plus parlant :

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QStringList listePays;
    listePays << "France" << "Espagne" << "Italie" << "Portugal" << "Suisse";
    QStringListModel *modele = new QStringListModel(listePays);

    QListView *vue = new QListView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}

Notre vue affiche maintenant la liste des pays qui se trouvent dans le modèle :

Modèle StringList
Modèle StringList

La surcharge de l'opérateur "<<" est très pratique comme vous pouvez le voir. Sachez toutefois qu'il est aussi possible d'utiliser la méthode append() :

listePays.append("Belgique");

Si, au cours de l'exécution du programme, un pays est ajouté, supprimé ou modifié, la vue (la liste) affichera automatiquement les modifications. Nous testerons cela en pratique un peu plus loin dans le chapitre.

QStandardItemModel : une liste à plusieurs niveaux et plusieurs colonnes

Ce type de modèle est beaucoup plus complet (et donc complexe :D ) que le précédent. Il permet de créer tous les types de modèles possibles et imaginables.

Pour bien visualiser les différents types de modèles que l'on peut concevoir avec un QStandardItemModel, voici un super schéma offert par la doc de Qt :

Différents modèles possibles
Différents modèles possibles
  • List Model : c'est un modèle avec une seule colonne et pas de sous-éléments. C'est le modèle utilisé par QStringList, mais QStandardItemModel peut aussi le faire (qui peut le plus peut le moins !).
    Ce type de modèle est en général adapté à un QListView.

  • Table Model : les éléments sont organisés avec plusieurs lignes et colonnes.
    Ce type de modèle est en général adapté à un QTableView.

  • Tree Model : les éléments ont des sous-éléments, ils sont organisés en plusieurs niveaux. Ce n'est pas représenté sur le schéma ci-dessus, mais rien n'interdit de mettre plusieurs colonnes dans un modèle en arbre aussi !
    Ce type de modèle est en général adapté à un QTreeView.

Gérez plusieurs lignes et colonnes

Pour construire un QStandardItemModel, on doit indiquer en paramètres le nombre de lignes et de colonnes qu'il doit gérer. Des lignes et des colonnes supplémentaires peuvent toujours être ajoutées par la suite au besoin.

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QStandardItemModel *modele = new QStandardItemModel(5, 3);

    QTableView *vue = new QTableView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}

Ici, un modèle à 5 lignes et 3 colonnes sera créé. Les éléments sont vides au départ, mais on a déjà un tableau :

Table view avec QStandardItemModel
Table view avec QStandardItemModel

Chaque élément est représenté par un objet de type QStandardItem.
Pour définir un élément, on utilise la méthode setItem() du modèle. Donnez-lui respectivement le numéro de ligne, de colonne, et un QStandardItem à afficher. Attention : la numérotation commence à 0.

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QStandardItemModel *modele = new QStandardItemModel(5, 3);
    modele->setItem(3, 1, new QStandardItem("Zéro !"));

    QTableView *vue = new QTableView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}

Ici, je crée un nouvel élément contenant le texte "Zéro !" à la 4ème ligne, 2nde colonne :

Table view avec QStandardItemModel
Table view avec QStandardItemModel

Voilà comment on peut peupler le modèle d'éléments. :)

Ajoutez des éléments enfants

Essayons maintenant d'ajouter des éléments enfants.
Pour pouvoir voir les éléments enfants, on va devoir changer de vue et passer par un QTreeView.

Il faut procéder dans l'ordre :

  1. Créer un élément (par exemple "item"), de type QStandardItem. Ligne 9

  2. Ajouter cet élément au modèle avec appendRow(). Ligne 10

  3. Ajouter un sous-élément à "item" avec appendRow(). Ligne 11

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QStandardItemModel *modele = new QStandardItemModel;

    QStandardItem *item = new QStandardItem("John Deuf");
    modele->appendRow(item);
    item->appendRow(new QStandardItem("17 ans"));

    QTreeView *vue = new QTreeView;
    vue->setModel(modele);

    layout->addWidget(vue);

    setLayout(layout);
}

Résultat, on a créé un élément "John Deuf" qui contient un élément enfant "17 ans" :

Arbre

Entraînez-vous à créer plusieurs éléments et des sous-éléments enfants, ce n'est pas compliqué si on est bien organisé mais il faut pratiquer. ;)

Gestion des sélections

Nous avons découvert comment associer un modèle à une vue, et comment manipuler plusieurs modèles : QDirModel, QStringListModel et QStandardItemModel.

Il nous reste à voir comment on peut récupérer le ou les éléments sélectionnés dans la vue, pour savoir quel est le choix de l'utilisateur.

Nous entrons dans une partie vraiment complexe où de nombreuses classes se mêlent les unes aux autres. L'architecture modèle/vue de Qt est extrêmement flexible (on peut faire ce qu'on veut avec), mais en contrepartie il est beaucoup plus délicat de s'en servir car il y a plusieurs étapes à suivre dans un ordre précis.

Par conséquent, et afin d'éviter de faire un chapitre beaucoup trop long et surtout trop complexe, j'ai volontairement décidé de limiter mes exemples ici aux sélections d'un QListView. Je vous laisserai le soin d'adapter ces exemples aux autres vues, en sachant que c'est relativement similaire à chaque fois (les principes sont les mêmes).

Nous allons rajouter un bouton "Afficher la sélection" à notre fenêtre. Elle va ressembler à ceci :

Gestion de la sélection

Lorsqu'on cliquera sur le bouton, il ouvrira une boîte de dialogue (QMessageBox) qui affichera le nom de l'élément sélectionné.

Nous allons apprendre à gérer 2 cas :

  • Lorsqu'on ne peut sélectionner qu'un seul élément à la fois.

  • Lorsqu'on peut sélectionner plusieurs éléments à la fois.

Une sélection unique

Nous allons devoir créer une connexion entre un signal et un slot pour que le clic sur le bouton fonctionne.

Modifions donc pour commencer le .h de la fenêtre :

#ifndef HEADER_FENPRINCIPALE
#define HEADER_FENPRINCIPALE

#include <QtWidgets>

class FenPrincipale : public QWidget
{
    Q_OBJECT

    public:
        FenPrincipale();

    private:
        QListView *vue;
        QStringListModel *modele;
        QPushButton *bouton;

    private slots:
        void clicSelection();
};


#endif

J'ai rajouté la macro Q_OBJECT, mis quelques éléments de la fenêtre en attributs (pour pouvoir y accéder dans le slot), et ajouté le slot clicSelection() qui sera appelé après un clic sur le bouton.

Maintenant retour au .cpp, où je fais la connexion et où j'écris le contenu du slot :

#include "FenPrincipale.h"

FenPrincipale::FenPrincipale()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QStringList listePays;
    listePays << "France" << "Espagne" << "Italie" << "Portugal" << "Suisse";
    modele = new QStringListModel(listePays);

    vue = new QListView ;
    vue->setModel(modele);

    bouton = new QPushButton("Afficher la sélection");

    layout->addWidget(vue);
    layout->addWidget(bouton);

    setLayout(layout);

    connect(bouton, SIGNAL(clicked()), this, SLOT(clicSelection()));
}

void FenPrincipale::clicSelection()
{
    QItemSelectionModel *selection = vue->selectionModel();
    QModelIndex indexElementSelectionne = selection->currentIndex();
    QVariant elementSelectionne = modele->data(indexElementSelectionne, Qt::DisplayRole);
    QMessageBox::information(this, "Elément sélectionné", elementSelectionne.toString());
}

Analysons le contenu du slot, qui contient les nouveautés (lignes 26 à 29). Voici ce que nous faisons ligne par ligne :

  1. On récupère un objet QItemSelectionModel qui contient des informations sur ce qui est sélectionné sur la vue. C'est la vue qui nous donne un pointeur vers cet objet grâce à vue->selectionModel().

  2. On appelle la méthode currentIndex() de l'objet qui contient des informations sur la sélection. Cela renvoie un index, c'est-à-dire en gros le numéro de l'élément sélectionné sur la vue.

  3. Maintenant qu'on connaît le numéro de l'élément sélectionné, on veut retrouver son texte. On appelle la méthode data() du modèle, et on lui donne l'index qu'on a récupéré (c'est-à-dire le numéro de l'élément sélectionné). On récupère le résultat dans un QVariant, qui est une classe qui peut aussi bien stocker des int que des chaînes de caractères.

  4. On n'a plus qu'à afficher l'élément sélectionné récupéré. Pour extraire la chaîne du QVariant, on appelle toString().

Ouf ! Ce n'est pas simple je le reconnais, il y a plusieurs étapes.

D'abord on récupère l'objet qui contient des informations sur les éléments sélectionnés sur la vue.
Ensuite on demande à cet objet quel est l'indice (numéro) de l'élément actuellement sélectionné.
On peut alors récupérer le texte contenu dans le modèle à cet indice.
On affiche ce texte avec la méthode toString() de l'objet de type QVariant.

Désormais, un clic sur le bouton vous indique quel élément est sélectionné :

Elément sélectionné
Elément sélectionné

Une sélection multiple

Par défaut, on ne peut sélectionner qu'un seul élément à la fois sur une liste. Pour changer ce comportement et autoriser la sélection multiple, rajoutez ceci dans le constructeur :

vue->setSelectionMode(QAbstractItemView::ExtendedSelection);

D'autres modes de sélection sont disponibles, mais je vous laisse aller voir la doc de QAbstractItemView pour en savoir plus. ;)
Avec ce mode, on peut sélectionner n'importe quels éléments. On peut utiliser la touche Shift du clavier pour faire une sélection continue, ou Ctrl pour une sélection discontinue (avec des trous).

Voici un exemple de sélection continue, désormais possible :

Sélection multiple
Sélection multiple

Pour récupérer la liste des éléments sélectionnés, c'est un peu plus compliqué ici parce qu'il y en a plusieurs. On ne peut plus utiliser la méthode currentIndex(), il va falloir utiliser selectedIndexes().

Je vous donne le nouveau code du slot, et on l'analyse ensuite ensemble. :)

void FenPrincipale::clicSelection()
{
    QItemSelectionModel *selection = vue->selectionModel();
    QModelIndexList listeSelections = selection->selectedIndexes();
    QString elementsSelectionnes;

    for (int i = 0 ; i < listeSelections.size() ; i++)
    {
        QVariant elementSelectionne = modele->data(listeSelections[i], Qt::DisplayRole);
        elementsSelectionnes += elementSelectionne.toString() + "<br />";
    }

    QMessageBox::information(this, "Eléments sélectionnés", elementsSelectionnes);
}

C'est un peu plus gros bien sûr. :D

  1. La première ligne ne change pas : on récupère l'objet qui contient des informations sur les éléments sélectionnés.

  2. Ensuite, au lieu d'appeler currentIndex(), on demande selectedIndexes() parce qu'il peut y en avoir plusieurs.

  3. On crée un QString vide dans lequel on stockera la liste des pays pour l'afficher ensuite dans la boîte de dialogue.

  4. Vient ensuite une boucle. En effet, l'objet listeSelections récupéré est un tableau (en fait c'est un objet de type QList, mais on peut faire comme si c'était un tableau). On parcourt donc ce tableau ligne par ligne, et on récupère à chaque fois le texte correspondant.

  5. On stocke ce texte à la suite du QString, qui se remplit au fur et à mesure.

  6. Une fois la boucle terminée, on affiche le QString qui contient la liste des pays sélectionnés. Et voilà le travail !

Sélection multiple et affichage
Sélection multiple et affichage

Ici, je me contente d'afficher la liste des éléments sélectionnés dans une boîte de dialogue, mais en pratique vous ferez sûrement quelque chose de beaucoup plus intelligent avec ça. ;)

En ce qui me concerne je vous ai donné la base pour démarrer, mais je serais bien incapable de vous montrer toutes les utilisations possibles de l'architecture modèle/vue de Qt. Nous ne pouvons pas tout voir, ce serait bien trop vaste.

A vous de voir, maintenant que vous commencez à connaître le principe, de quels outils vous avez besoin. Entraînez-vous avec les autres vues (arbre, tableau) et essayez d'en faire une utilisation plus poussée. Vous pouvez refaire un TP précédent et y intégrer un widget basé sur l'architecture modèle/vue pour vous entraîner.
Ce sera probablement difficile, mais bon, il faut bien des chapitres difficiles dans le cours sinon on va croire que c'est un site pour les débutants ici. :-°

N'oubliez pas de lire la doc surtout, elle contient toutes les informations dont vous avez besoin !

Exemple de certificat de réussite
Exemple de certificat de réussite