Logo Spiria

Combo-box dans une liste avec Qt

18 avril 2019.

Qt est un cadre puissant permettant de construire des interfaces utilisateur pour des applications. Néanmoins, il est parfois difficile de trouver toutes les informations nécessaires pour atteindre un objectif précis. Il peut être frustrant de sentir que la solution est à portée de main et sans pourtant résoudre tous les problèmes.

Le but

J’ai récemment voulu mettre un combo-box à l’intérieur des éléments d’une table à multiples colonnes. Bien que la façon standard de procéder soit simple, les résultats ne sont pas satisfaisants. Je voulais améliorer la solution normale pour rendre l’expérience plus agréable pour l’utilisateur. J’ai commencé par chercher des solutions toutes faites sur Internet. Bien que j’ai trouvé quelques idées dans le wiki de Qt et sur Stack Overflow, il n’y avait pas de solution complète qui fonctionne à ma satisfaction.

C’est pourquoi je veux partager la solution que j’ai finalement trouvée récemment. Voilà ce que je voulais faire et pourquoi.

1. Je voulais mettre un combo-box directement dans un tableau multicolonnes.

Pourquoi : afin de pouvoir changer rapidement une valeur pour un élément de la liste. Ce n’est en fait pas difficile et il y a des exemples directement dans la documentation de Qt.

2. Je voulais que le combo-box soit toujours visible.

Pourquoi : pour que l’utilisateur sache que l’élément peut être édité directement. Par défaut, Qt garde le combo-box caché. L’utilisateur doit double-cliquer sur l’élément pour afficher la liste déroulante. Ce n’est pas facile à découvrir par l’utilisateur normal. Le résultat est que l’utilisateur ne sait même pas qu’un élément est modifiable !

3. Je voulais que le combo-box affiche le contenu de la liste déroulante dès le premier clic.

Pourquoi : normalement, dans Qt, le premier clic est utilisé pour sélectionner l’élément dans la liste. D’autre part, pour un combo-box normal, le premier clic affiche son contenu. Cette différence de comportement fait que le combo-box intégré dans la table fonctionne différemment d’un combo-box en dehors d’une table. Cela semble anormal pour l’utilisateur.

4. Je voulais que le clic sur le combo-box sélectionne la ligne du tableau.

Pourquoi : normalement, le clic sur le combo-box serait absorbé par le combo-box et la ligne ne serait pas sélectionnée, ce qui semblerait étrange pour l’utilisateur.

5. Je voulais que le combo-box termine l’édition de l’élément dès que l’utilisateur sélectionne un élément.

Pourquoi : normalement, Qt laisse l’élément de la liste en mode édition jusqu’à ce que l’utilisateur s’en éloigne avec le clavier ou en cliquant ailleurs. Encore une fois, ceci est incohérent comparé à la façon dont les combo-box fonctionnent en dehors d’un tableau.

La solution

Voici comment chaque objectif est atteint.

1. Placez le combo-box directement dans le widget de la table multicolonne.

Comment : en utilisant un QStyledItemDelegate qui crée un combo-box comme éditeur. C’est du code Qt standard et il est expliqué dans le wiki Qt.

2. Rendre le combo-box toujours visible.

Comment : C’est aussi du code Qt standard : remplacer les fonctions paint et sizeHint du délégué. Le problème est que le code nécessaire n’est pas évident dans la documentation. En particulier, il n’est pas évident d’obtenir le style standard. Voir le code sur GitHub pour les détails.

3. Afficher le contenu de la liste déroulante au premier clic.

Comment : Cela nécessite trois astuces. La première est d’augmenter la fonction mousePressEvent de la table. Ceci permet d’entrer en mode édition avec le premier clic de souris. Dans la fonction mousePressEvent, nous lançons une minuterie qui appelle la fonction d’édition du widget de table avec l’index de l’élément sélectionné. (L’utilisation d’une minuterie est nécessaire pour que l’astuce de sélection de la ligne fonctionne plus tard.) Le second est de présélectionner l’élément courant dans le combo-box avec un appel à setCurrentIndex dans la fonction setEditorData du délégué. Le troisième est d’appeler la fonction showPopup du combo-box pour qu’il affiche son contenu immédiatement, dans la fonction setEditorData.

4. Sélectionnez la ligne du tableau en cliquant sur la liste déroulante.

Comment : nous devons à la fois sélectionner la ligne et lancer l’éditeur. C’est pourquoi nous retardons l’affichage de l’éditeur, sinon la sélection des lignes interférerait. Ainsi, dans la fonction mousePressEvent, nous appelons setCurrentIndex sur le modèle de sélection de table.

5. Terminez l’édition de l’élément dès que l’utilisateur sélectionne un élément.

Comment : Dans la fonction createEditor du délégué, quand nous créons le combo-box, nous nous connectons immédiatement à son signal currentTextChanged. La fonction connectée permet de valider les données et de terminer l’éditeur. Cela se fait en appelant la fonction commitData et la fonction closeEditor du délégué.

Le code

Le code C++ nécessaire pour avoir un combo-box toujours présent dans un widget de table est disponible dans ce dépôt public sur GitHub.

(L’exemple est fourni en tant que solution Visual Studio 2017.)