QStyle Check Boxes 示例(七)
QCheckBox构建样式选项,即复选框的QStyleOptionButton:
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()设置所有小部件共有的信息。
当用户按下框时,down boolean为true;否则,为false。 无论是否选中该复选框,都是如此。
当我们具有三态复选框时,将设置State_NoChange状态,并对其进行部分检查。 如果选中此框,则具有State_On;如果未选中,则具有State_Off。
如果鼠标悬停在复选框上方且窗口小部件具有属性Qt :: WA_Hover设置,则会设置State_MouseOver-您可以在QStyle :: polish()中进行设置。
此外,样式选项还包含按钮的文本,图标和图标大小。
initFrom()使用所有小部件的公共属性设置样式选项。 我们在此处打印其实现:
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;
#ifdef Q_WS_MAC
extern bool qt_mac_can_clickThrough(const QWidget *w); //qwidget_mac.cpp
if (!(state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget))
state &= ~QStyle::State_Enabled;
#endif
#ifdef QT_KEYPAD_NAVIGATION
if (widget->hasEditFocus())
state |= QStyle::State_HasEditFocus;
#endif
direction = widget->layoutDirection();
rect = widget->rect();
palette = widget->palette();
fontMetrics = widget->fontMetrics();
启用小部件时设置State_Enabled。 当窗口小部件具有焦点时,将设置State_HasFocus标志。
同样,当小部件是活动窗口的子级时,设置State_Active标志。 仅当窗口小部件设置了WA_HoverEnabled Windows标志时,才设置State_MouseOver。
请注意,必须在Qt中启用小键盘导航,才能包含State_HasEditFocus。 默认情况下不包含它。
除了设置状态标志之外,QStyleOption还包含有关小部件的其他信息:
direction是布局的布局方向,rect是小部件的边界矩形(绘制区域),palette是应用于以下用途的QPalette 绘制窗口小部件,fontMetrics是窗口小部件使用的字体的度量。
我们提供一个复选框图像和与之匹配的样式选项。
上面的复选框在其样式选项中将具有以下状态标志:

| State flag | Set |
|---|---|
| State_Sunken | Yes |
| State_NoChange | No |
| State_On | Yes |
| State_Off | No |
| State_MouseOver | Yes |
| State_Enabled | Yes |
| State_HasFocus | Yes |
| State_KeyboardFocusChange | No |
| State_Active | Yes |
QCheckBox使用样式选项opt和QStylePainter p在QWidget :: paintEvent()中进行绘制。
QStylePainter类是绘制样式元素的便捷类。 最值得注意的是,它包装了用于绘画的QStyle中的方法。
QCheckBox绘制自己如下:
QStylePainter p(this); QStyleOptionButton opt = d->getStyleOption(); p.drawControl(QStyle::CE_CheckBox, opt);
QCommonStyle处理CE_CheckBox元素。
QCheckBox有两个子元素:SE_CheckBoxIndicator(已选中的指示器)和SE_CheckBoxContents(内容,用于复选框标签)。
QCommonStyle还实现了这些子元素边界矩形。 我们来看一下QCommonStyle代码:
QStyleOptionButton subopt = *btn;
subopt.rect = subElementRect(SE_CheckBoxIndicator, btn, widget);
drawPrimitive(PE_IndicatorCheckBox, &subopt, p, widget);
subopt.rect = subElementRect(SE_CheckBoxContents, btn, widget);
drawControl(CE_CheckBoxLabel, &subopt, p, widget);
if (btn->state & State_HasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*btn);
fropt.rect = subElementRect(SE_CheckBoxFocusRect, btn, widget);
drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
}
const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt);
uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter);
if (!styleHint(SH_UnderlineShortcut, btn, widget))
alignment |= Qt::TextHideMnemonic;
QPixmap pix;
QRect textRect = btn->rect;
if (!btn->icon.isNull()) {
pix = btn->icon.pixmap(btn->iconSize, btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
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()){
drawItemText(p, textRect, alignment | Qt::TextShowMnemonic,
btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText);
}
visualAlignment()根据布局方向调整文本的对齐方式。 然后我们绘制一个图标(如果存在),并调整文本的剩余空间。
drawItemText()绘制文本时要考虑对齐方式,布局方向和助记符。 它还使用调色板以正确的颜色绘制文本。
我们来看一下drawControl()中CE_CheckBoxIndicator的实现:
case PE_IndicatorCheckBox: {
painter->save();
drawButtonBackground(option, painter, true);
if (option->state & State_Enabled &&
option->state & State_MouseOver &&
!(option->state & State_Sunken)) {
painter->setPen(option->palette.color(QPalette::Button));
QRect rect = option->rect.adjusted(1, 1, -2, -2);
painter->drawRect(rect);
rect = rect.adjusted(1, 1, -1, -1);
painter->drawRect(rect);
}
if (option->state & State_On) {
QImage image(":/images/checkboxchecked.png");
painter->drawImage(option->rect.topLeft(), image);
}
painter->restore();
break;
我们首先保存painter的状态。 然后,我们使用drawButtonBackground()绘制复选框指示器的背景。 这是一个辅助功能,可绘制背景以及按钮和复选框的框。
我们在下面查看该功能。 然后,我们检查鼠标是否悬停在复选框上方。 如果是,则当框未按下且鼠标悬停在框上时,我们将绘制复选框具有的框架。
在这里,我们使用png图片作为指标。 我们还可以在此处检查该小部件是否已禁用。 然后,我们将不得不使用指示器禁用颜色的其他图像。
void CustomStyle::drawButtonBackground(const QStyleOption *option,
QPainter *painter, bool isCheckbox) const
{
QBrush buttonBrush = option->palette.button();
bool sunken = option->state & State_Sunken;
bool disabled = !(option->state & State_Enabled);
bool on = option->state & State_On;
if (!sunken && !disabled && (!on || isCheckbox))
buttonBrush = gradientBrush(option->rect);
painter->fillRect(option->rect, buttonBrush);
QRect rect = option->rect.adjusted(0, 0, -1, -1);
if (disabled)
painter->setPen(option->palette.color(QPalette::Disabled,
QPalette::WindowText));
else
painter->setPen(option->palette.color(QPalette::Mid));
painter->drawRect(rect);
if (sunken && !disabled) {
drawSunkenButtonShadow(painter, rect,
option->palette.color(QPalette::Mid),
option->direction == Qt::RightToLeft);
}
}
我们已经看到了如何从小部件获取绘制请求到样式完成绘制,以设置复选框的自定义样式。 要详细了解每个小部件的绘制方式,您需要按照此处的步骤逐步进行编码。
但是,通常足以知道小部件绘制哪些样式元素。 该小部件将构建一个样式选项,并调用该样式一次或多次以绘制其组成的样式元素。
通常,知道小部件可能处于的状态以及样式选项的其他内容(即我们在下一节中列出的内容)也就足够了。

浙公网安备 33010602011771号