Personnalisation d’une case à cocher : guide pas à pas
Cette page suit le parcours d’une case à cocher, depuis le moment où elle reçoit un événement de dessin jusqu’à ce que le style soit entièrement tracé. Elle montre comment le widget construit son option de style, comment l’ QCommonStyle e décompose l’élément en parties, et comment un QProxyStyle remplace l’une de ces parties. La plupart des widgets suivent la même structure ; la même stratégie de lecture s’applique donc à tous : identifiez l’option de style générée par le widget, identifiez les éléments qu’il dessine, puis examinez la mise en œuvre de ces éléments par l’ QCommonStyle.
Le widget crée une option de style
QCheckBox à l’aide de ` QStyleOptionButton`. Sa fonction ` initStyleOption() ` remplit l’option comme suit, de manière légèrement simplifiée :
opt.initFrom(q);
if (down)
opt.state |= QStyle::State_Sunken;
if (tristate && noChange)
opt.state |= QStyle::State_NoChange;
else
opt.state |= checked ? QStyle::State_On : QStyle::State_Off;
if (q->testAttribute(Qt::WA_Hover) && q->underMouse()) {
if (hovering)
opt.state |= QStyle::State_MouseOver;
else
opt.state &= ~QStyle::State_MouseOver;
}
opt.text = text;
opt.icon = icon;
opt.iconSize = q->iconSize();QStyleOption::initFrom() définit les informations communes à tous les widgets. Son implémentation se résume à ceci :
state = QStyle::State_None;
if (widget->isEnabled())
state |= QStyle::State_Enabled;
if (widget->hasFocus())
state |= QStyle::State_HasFocus;
if (widget->window()->testAttribute(Qt::WA_KeyboardFocusChange))
state |= QStyle::State_KeyboardFocusChange;
if (widget->underMouse())
state |= QStyle::State_MouseOver;
if (widget->window()->isActiveWindow())
state |= QStyle::State_Active;
direction = widget->layoutDirection();
rect = widget->rect();
palette = widget->palette();
fontMetrics = widget->fontMetrics();State_Enabled est défini lorsque le widget est activé, ` State_HasFocus ` lorsqu’il a le focus, ` State_KeyboardFocusChange ` lorsque l’utilisateur a changé de focus pour la dernière fois à l’aide du clavier, et ` State_Active ` lorsque la fenêtre du widget est la fenêtre active. ` State_MouseOver ` est défini lorsque le curseur de la souris survole le widget. En plus de l’état, ` initFrom() ` stocke dans l’option le sens de mise en page, le rectangle du widget, sa palette et ses métriques de police.
QCheckBox puis ajoute son propre état. La valeur « State_Sunken » est définie lorsque l’utilisateur appuie sur la case, qu’elle soit cochée ou non. La valeur « State_NoChange » est définie pour une case à trois états partiellement cochée ; sinon, « State_On » ou « State_Off » reflète l’état de la case. L’option contient également le texte, l’icône et la taille de l’icône. « QCheckBox » ne conserve la valeur « State_MouseOver » que tant que le widget possède l’attribut « WA_Hover », que l’on définit généralement via la fonction « polish() ».
Supposons que l’utilisateur clique avec la souris sur la case à cocher cochée de la capture d’écran suivante alors qu’elle a le focus. La capture d’écran montre également un bouton radio, qui utilise la même structure avec PE_IndicatorRadioButton et CE_RadioButtonLabel:

À ce moment-là, son option présente les indicateurs d’état suivants :
| Indicateur d’état | Activé |
|---|---|
State_Sunken | Oui |
State_NoChange | Non |
State_On | Oui |
State_Off | Non |
State_MouseOver | Oui |
State_Enabled | Oui |
State_HasFocus | Oui |
State_KeyboardFocusChange | Non |
State_Active | Oui |
Le widget demande au style de dessiner
QCheckBox se dessine lui-même dans ` paintEvent()` à l'aide d'un ` QStylePainter`, qui encapsule les fonctions de dessin de ` QStyle` :
QStylePainter p;
QStyleOptionButton opt;
initStyleOption(&opt);
p.drawControl(QStyle::CE_CheckBox, opt);C'est tout ce que fait le widget. Tout le reste se passe dans le style.
QCommonStyle divise l’élément en parties
QCommonStyle gère l’ CE_CheckBox en demandant les rectangles de ses deux sous-éléments, SE_CheckBoxIndicator et SE_CheckBoxContents, puis en dessinant un élément dans chacun d’eux. Si la case à cocher a le focus, il dessine également le cadre de focus :
QStyleOptionButton subopt = *btn;
subopt.rect = subElementRect(QStyle::SE_CheckBoxIndicator, btn, widget);
proxy()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &subopt, p, widget);
subopt.rect = subElementRect(QStyle::SE_CheckBoxContents, btn, widget);
proxy()->drawControl(QStyle::CE_CheckBoxLabel, &subopt, p, widget);
if (btn->state & State_HasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*btn);
fropt.rect = subElementRect(QStyle::SE_CheckBoxFocusRect, btn, widget);
proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, p, widget);
}Notez les appels à ` proxy() `. Un style dessine toujours ses éléments enfants via ` proxy()` afin qu’un ` QProxyStyle ` qui l’encadre puisse les redéfinir.
CE_CheckBoxLabel est également implémentée dans QCommonStyle:
const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt);
uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);
if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget))
alignment |= Qt::TextHideMnemonic;
QPixmap pix;
QRect textRect = btn->rect;
if (!btn->icon.isNull()) {
const auto dpr = p->device()->devicePixelRatio();
pix = btn->icon.pixmap(btn->iconSize, dpr,
btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
proxy()->drawItemPixmap(p, btn->rect, alignment, pix);
if (btn->direction == Qt::RightToLeft)
textRect.setRight(textRect.right() - btn->iconSize.width() - 4);
else
textRect.setLeft(textRect.left() + btn->iconSize.width() + 4);
}
if (!btn->text.isEmpty()){
proxy()->drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
}visualAlignment() ajuste l’alignement en fonction du sens de mise en page. Le style dessine l’icône, s’il y en a une, et réduit le rectangle de texte restant en conséquence. drawItemText() dessine le texte en tenant compte de l’alignement, de la direction de mise en page et du mnémonique, et utilise la palette pour choisir la couleur du texte. Le dessin d’une étiquette implique de nombreux détails, et la classe de base les gère bien ; un style personnalisé n’a donc que rarement besoin de la réimplémenter.
Un style proxy remplace l’indicateur
L’indicateur, PE_IndicatorCheckBox, est l’élément qui différencie les styles ; c’est donc la partie à remplacer. La sous-classe QProxyStyle suivante dessine un indicateur arrondi à partir des couleurs de la palette de l’option et transmet tous les autres éléments au style de base :
class CheckBoxStyle : public QProxyStyle
{
public:
using QProxyStyle::QProxyStyle;
void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const override;
};
void CheckBoxStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const
{
if (element != PE_IndicatorCheckBox) {
QProxyStyle::drawPrimitive(element, option, painter, widget);
return;
}
const bool enabled = option->state & State_Enabled;
const QPalette::ColorGroup group = !enabled ? QPalette::Disabled
: option->state & State_Active ? QPalette::Active
: QPalette::Inactive;
const QPalette &palette = option->palette;
const QRect rect = option->rect.adjusted(1, 1, -1, -1);
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
// Frame and background: highlight the frame while the mouse hovers over
// the indicator, and darken the background while it's pressed.
QColor frameColor = palette.color(group, QPalette::Mid);
if (enabled && (option->state & State_MouseOver))
frameColor = palette.color(group, QPalette::Highlight);
const QPalette::ColorRole fillRole =
option->state & State_Sunken ? QPalette::Mid : QPalette::Base;
painter->setPen(frameColor);
painter->setBrush(palette.brush(group, fillRole));
painter->drawRoundedRect(rect, 2, 2);
// Check mark for State_On, a filled square for the partially checked
// State_NoChange, nothing for State_Off.
const QRect inner = rect.adjusted(3, 3, -3, -3);
if (option->state & State_On) {
painter->setPen(QPen(palette.color(group, QPalette::Text), 2));
painter->drawLine(inner.left(), inner.center().y(),
inner.center().x(), inner.bottom());
painter->drawLine(inner.center().x(), inner.bottom(),
inner.right(), inner.top());
} else if (option->state & State_NoChange) {
painter->fillRect(inner, palette.brush(group, QPalette::Text));
}
painter->restore();
}L’implémentation lit toutes ses informations à partir de l’option. Elle sélectionne le groupe de couleurs à partir de State_Enabled et State_Active, met en surbrillance le cadre lorsque State_MouseOver est activé, assombrit l’arrière-plan lorsque State_Sunken est activé, et dessine une coche pour State_On ou un carré rempli pour State_NoChange. Comme toutes les couleurs proviennent de la palette, l’indicateur suit la palette de l’application et fonctionne aussi bien avec les thèmes clairs que sombres. Il enregistre et restaure également le « painter », de sorte que la classe de base retrouve le « painter » dans l’état attendu.
Le même indicateur apparaît partout où le style dessine PE_IndicatorCheckBox: dans les cases à cocher, dans les zones de groupe à cocher et dans les vues d’éléments qui l’utilisent pour les coches. C’est cette réutilisation qui justifie l’existence des éléments primitifs.
Pour utiliser ce style, installez-le avant que l’application ne crée ses widgets :
int main(int argc, char *argv[])
{
QApplication::setStyle(new CheckBoxStyle);
QApplication app(argc, argv);
QCheckBox box("Send me updates");
box.show();
return app.exec();
}Appliquez la même approche aux autres widgets
Pour comprendre comment un widget est dessiné, il n’est pas nécessaire de lire l’intégralité du code. Il suffit généralement de savoir quels éléments de style le widget dessine, quels états il définit et ce que contient son option de style. Le widget construit une option et appelle le style une ou plusieurs fois ; le style dessine les éléments demandés par le widget. La « Référence des styles de widgets » répertorie précisément ces informations pour chaque widget.
Voir également « Comment un style dessine un widget », « Référence des styles de widgets », QProxyStyle et QStyleOptionButton.
© 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.