Exemple de délégué à l'étoile
L'exemple du délégué étoile montre comment créer un délégué qui peut se peindre lui-même et qui prend en charge l'édition.

Lors de l'affichage de données dans une page QListView, QTableView ou QTreeView, les différents éléments sont dessinés par un délégué. De plus, lorsque l'utilisateur commence à modifier un élément (par exemple, en double-cliquant sur l'élément), le délégué fournit un widget d'édition qui est placé au-dessus de l'élément pendant que l'édition a lieu.
Les délégués sont des sous-classes de QAbstractItemDelegate. Qt fournit QStyledItemDelegate, qui hérite de QAbstractItemDelegate et gère les types de données les plus courants (notamment int et QString). Si nous devons prendre en charge des types de données personnalisés, ou si nous voulons personnaliser le rendu ou l'édition des types de données existants, nous pouvons sous-classer QAbstractItemDelegate ou QStyledItemDelegate. Voir Classes de délégués pour plus d'informations sur les délégués, et Programmation modèle/vue si vous avez besoin d'une introduction de haut niveau à l'architecture modèle/vue de Qt (y compris les délégués).
Dans cet exemple, nous allons voir comment mettre en œuvre un délégué personnalisé pour rendre et modifier un type de données "étoiles", qui peut stocker des valeurs telles que "1 sur 5 étoiles".
L'exemple se compose des classes suivantes :
StarRatingest le type de données personnalisé. Il stocke une évaluation exprimée en étoiles, telle que "2 sur 5 étoiles" ou "5 sur 6 étoiles".StarDelegatehérite de QStyledItemDelegate et prend en chargeStarRating(en plus des types de données déjà gérés par QStyledItemDelegate).StarEditorhérite de QWidget et est utilisé parStarDelegatepour permettre à l'utilisateur de modifier un classement par étoiles à l'aide de la souris.
Pour montrer l'action de StarDelegate, nous allons remplir un QTableWidget avec des données et y installer le délégué.
Définition de la classe StarDelegate
Voici la définition de la classe StarDelegate:
class StarDelegate : public QStyledItemDelegate { Q_OBJECT public: using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; private slots: void commitAndCloseEditor(); };
Toutes les fonctions publiques sont des fonctions virtuelles réimplémentées à partir de QStyledItemDelegate pour fournir un rendu et une édition personnalisés.
Implémentation de la classe StarDelegate
La fonction paint() est réimplémentée à partir de QStyledItemDelegate et est appelée chaque fois que la vue doit repeindre un élément :
void StarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); if (option.state & QStyle::State_Selected) painter->fillRect(option.rect, option.palette.highlight()); starRating.paint(painter, option.rect, option.palette, StarRating::EditMode::ReadOnly); } else { QStyledItemDelegate::paint(painter, option, index); }
La fonction est invoquée une fois pour chaque élément, représenté par un objet QModelIndex du modèle. Si les données stockées dans l'élément sont des StarRating, nous les peignons nous-mêmes ; sinon, nous laissons QStyledItemDelegate les peindre pour nous. Cela permet de s'assurer que StarDelegate peut gérer les types de données les plus courants.
Si l'élément est un StarRating, nous dessinons l'arrière-plan si l'élément est sélectionné et nous dessinons l'élément à l'aide de StarRating::paint(), que nous examinerons plus tard.
StartRatingLes s peuvent être stockés dans un QVariant grâce à la macro Q_DECLARE_METATYPE() qui apparaît dans starrating.h. Nous y reviendrons plus tard.
La fonction createEditor() est appelée lorsque l'utilisateur commence à modifier un élément :
QWidget *StarDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *editor = new StarEditor(parent); connect(editor, &StarEditor::editingFinished, this, &StarDelegate::commitAndCloseEditor); return editor; } return QStyledItemDelegate::createEditor(parent, option, index); }
Si l'élément est un StarRating, nous créons un StarEditor et connectons son signal editingFinished() à notre slot commitAndCloseEditor(), afin de pouvoir mettre à jour le modèle lorsque l'éditeur se ferme.
Voici l'implémentation de commitAndCloseEditor():
void StarDelegate::commitAndCloseEditor() { StarEditor *editor = qobject_cast<StarEditor *>(sender()); emit commitData(editor); emit closeEditor(editor); }
Lorsque l'utilisateur a fini d'éditer, nous émettons commitData() et closeEditor() (tous deux déclarés dans QAbstractItemDelegate), pour indiquer au modèle qu'il y a des données éditées et pour informer la vue que l'éditeur n'est plus nécessaire.
La fonction setEditorData() est appelée lorsqu'un éditeur est créé pour l'initialiser avec les données du modèle :
void StarDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); StarEditor *starEditor = qobject_cast<StarEditor *>(editor); starEditor->setStarRating(starRating); } else { QStyledItemDelegate::setEditorData(editor, index); } }
Nous appelons simplement setStarRating() sur l'éditeur.
La fonction setModelData() est appelée pour transférer les données de l'éditeur vers le modèle lorsque l'édition est terminée :
void StarDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarEditor *starEditor = qobject_cast<StarEditor *>(editor); model->setData(index, QVariant::fromValue(starEditor->starRating())); } else { QStyledItemDelegate::setModelData(editor, model, index); } }
La fonction sizeHint() renvoie la taille préférée d'un article :
QSize StarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (index.data().canConvert<StarRating>()) { StarRating starRating = qvariant_cast<StarRating>(index.data()); return starRating.sizeHint(); } return QStyledItemDelegate::sizeHint(option, index); }
Nous transférons simplement l'appel à StarRating.
Définition de la classe StarEditor
La classe StarEditor a été utilisée lors de l'implémentation de StarDelegate. Voici la définition de la classe :
class StarEditor : public QWidget { Q_OBJECT public: StarEditor(QWidget *parent = nullptr); QSize sizeHint() const override; void setStarRating(const StarRating &starRating) { myStarRating = starRating; } StarRating starRating() { return myStarRating; } signals: void editingFinished(); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: int starAtPosition(int x) const; StarRating myStarRating; };
Cette classe permet à l'utilisateur de modifier une page StarRating en déplaçant la souris au-dessus de l'éditeur. Elle émet le signal editingFinished() lorsque l'utilisateur clique sur l'éditeur.
Les fonctions protégées sont réimplémentées à partir de QWidget pour gérer les événements liés à la souris et à la peinture. La fonction privée starAtPosition() est une fonction d'aide qui renvoie le numéro de l'étoile située sous le pointeur de la souris.
Mise en œuvre de la classe StarEditor
Commençons par le constructeur :
StarEditor::StarEditor(QWidget *parent) : QWidget(parent) { setMouseTracking(true); setAutoFillBackground(true); }
Nous activons mouse tracking sur le widget afin de pouvoir suivre le curseur même si l'utilisateur ne maintient aucun bouton de la souris enfoncé. Nous activons également la fonction auto-fill background de QWidget pour obtenir un arrière-plan opaque. (Sans cet appel, l'arrière-plan de la vue transparaîtrait à travers l'éditeur).
La fonction paintEvent() est réimplémentée à partir de QWidget:
void StarEditor::paintEvent(QPaintEvent *) { QPainter painter(this); myStarRating.paint(&painter, rect(), palette(), StarRating::EditMode::Editable); }
Nous appelons simplement StarRating::paint() pour dessiner les étoiles, comme nous l'avons fait lors de l'implémentation de StarDelegate.
void StarEditor::mouseMoveEvent(QMouseEvent *event) { const int star = starAtPosition(event->position().toPoint().x()); if (star != myStarRating.starCount() && star != -1) { myStarRating.setStarCount(star); update(); } QWidget::mouseMoveEvent(event); }
Dans le gestionnaire d'événement de la souris, nous appelons setStarCount() sur le membre de données privé myStarRating pour refléter la position actuelle du curseur, et nous appelons QWidget::update() pour forcer une nouvelle peinture.
void StarEditor::mouseReleaseEvent(QMouseEvent *event) { emit editingFinished(); QWidget::mouseReleaseEvent(event); }
Lorsque l'utilisateur relâche le bouton de la souris, nous émettons simplement le signal editingFinished().
int StarEditor::starAtPosition(int x) const { const int star = (x / (myStarRating.sizeHint().width() / myStarRating.maxStarCount())) + 1; if (star <= 0 || star > myStarRating.maxStarCount()) return -1; return star; }
La fonction starAtPosition() utilise l'algèbre linéaire de base pour déterminer quelle étoile se trouve sous le curseur.
Définition de la classe StarRating
class StarRating { public: enum class EditMode { Editable, ReadOnly }; explicit StarRating(int starCount = 1, int maxStarCount = 5); void paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const; QSize sizeHint() const; int starCount() const { return myStarCount; } int maxStarCount() const { return myMaxStarCount; } void setStarCount(int starCount) { myStarCount = starCount; } void setMaxStarCount(int maxStarCount) { myMaxStarCount = maxStarCount; } private: QPolygonF starPolygon; QPolygonF diamondPolygon; int myStarCount; int myMaxStarCount; }; Q_DECLARE_METATYPE(StarRating)
La classe StarRating représente une note sous la forme d'un nombre d'étoiles. En plus de contenir les données, elle est également capable de peindre les étoiles sur un site QPaintDevice, qui, dans cet exemple, est soit une vue, soit un éditeur. La variable membre myStarCount stocke la note actuelle et myMaxStarCount stocke la note la plus élevée possible (généralement 5).
La macro Q_DECLARE_METATYPE() fait connaître le type StarRating à QVariant, ce qui permet de stocker les valeurs StarRating dans QVariant.
Mise en œuvre de la classe StarRating
Le constructeur initialise myStarCount et myMaxStarCount, et met en place les polygones utilisés pour dessiner les étoiles et les diamants :
StarRating::StarRating(int starCount, int maxStarCount) : myStarCount(starCount), myMaxStarCount(maxStarCount) { starPolygon << QPointF(1.0, 0.5); for (int i = 1; i < 5; ++i) starPolygon << QPointF(0.5 + 0.5 * std::cos(0.8 * i * 3.14), 0.5 + 0.5 * std::sin(0.8 * i * 3.14)); diamondPolygon << QPointF(0.4, 0.5) << QPointF(0.5, 0.4) << QPointF(0.6, 0.5) << QPointF(0.5, 0.6) << QPointF(0.4, 0.5); }
La fonction paint() peint les étoiles de cet objet StarRating sur un dispositif de peinture :
void StarRating::paint(QPainter *painter, const QRect &rect, const QPalette &palette, EditMode mode) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(Qt::NoPen); painter->setBrush(mode == EditMode::Editable ? palette.highlight() : palette.windowText()); const int yOffset = (rect.height() - PaintingScaleFactor) / 2; painter->translate(rect.x(), rect.y() + yOffset); painter->scale(PaintingScaleFactor, PaintingScaleFactor); for (int i = 0; i < myMaxStarCount; ++i) { if (i < myStarCount) painter->drawPolygon(starPolygon, Qt::WindingFill); else if (mode == EditMode::Editable) painter->drawPolygon(diamondPolygon, Qt::WindingFill); painter->translate(1.0, 0.0); } painter->restore(); }
Nous commençons par définir le stylo et le pinceau que nous utiliserons pour peindre. Le paramètre mode peut être Editable ou ReadOnly. Si mode est modifiable, nous utilisons la couleur Highlight au lieu de la couleur WindowText pour dessiner les étoiles.
Ensuite, nous dessinons les étoiles. Si nous sommes en mode Edit, nous peignons des losanges à la place des étoiles si l'évaluation est inférieure à l'évaluation la plus élevée.
La fonction sizeHint() renvoie la taille préférée d'une zone sur laquelle peindre les étoiles :
La taille préférée est juste suffisante pour peindre le nombre maximum d'étoiles. La fonction est appelée par StarDelegate::sizeHint() et StarEditor::sizeHint().
La fonction main()
Voici la fonction main() du programme :
int main(int argc, char *argv[]) { QApplication app(argc, argv); QTableWidget tableWidget(4, 4); tableWidget.setItemDelegate(new StarDelegate); tableWidget.setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked); tableWidget.setSelectionBehavior(QAbstractItemView::SelectRows); tableWidget.setHorizontalHeaderLabels({"Title", "Genre", "Artist", "Rating"}); populateTableWidget(&tableWidget); tableWidget.resizeColumnsToContents(); tableWidget.resize(500, 300); tableWidget.show(); return app.exec(); }
La fonction main() crée un QTableWidget et lui associe un StarDelegate. DoubleClicked et SelectedClicked sont définis comme edit triggers, de sorte que l'éditeur s'ouvre d'un simple clic lorsque l'élément de classement par étoiles est sélectionné.
La fonction populateTableWidget() remplit le site QTableWidget avec des données :
void populateTableWidget(QTableWidget *tableWidget) { static constexpr struct { const char *title; const char *genre; const char *artist; int rating; } staticData[] = { { "Mass in B-Minor", "Baroque", "J.S. Bach", 5 }, ... { nullptr, nullptr, nullptr, 0 } }; for (int row = 0; staticData[row].title != nullptr; ++row) { QTableWidgetItem *item0 = new QTableWidgetItem(staticData[row].title); QTableWidgetItem *item1 = new QTableWidgetItem(staticData[row].genre); QTableWidgetItem *item2 = new QTableWidgetItem(staticData[row].artist); QTableWidgetItem *item3 = new QTableWidgetItem; item3->setData(0, QVariant::fromValue(StarRating(staticData[row].rating))); tableWidget->setItem(row, 0, item0); tableWidget->setItem(row, 1, item1); tableWidget->setItem(row, 2, item2); tableWidget->setItem(row, 3, item3); } }
Remarquez l'appel à QVariant::fromValue pour convertir un StarRating en QVariant.
Extensions possibles et suggestions
Il existe de nombreuses façons de personnaliser le cadre modèle/vue de Qt. L'approche utilisée dans cet exemple est appropriée pour la plupart des délégués et éditeurs personnalisés. Voici des exemples de possibilités non utilisées par le délégué et l'éditeur star :
- Il est possible d'ouvrir les éditeurs de manière programmatique en appelant QAbstractItemView::edit(), au lieu de s'appuyer sur les déclencheurs d'édition. Cela pourrait être utilisé pour prendre en charge d'autres déclencheurs d'édition que ceux proposés par l'énumération QAbstractItemView::EditTrigger. Par exemple, dans l'exemple de Star Delegate, le fait de survoler un élément avec la souris pourrait être un moyen judicieux d'ouvrir un éditeur.
- En réimplémentant QAbstractItemDelegate::editorEvent(), il est possible d'implémenter l'éditeur directement dans le délégué, au lieu de créer une sous-classe QWidget distincte.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.