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); } }
我们已经看到了如何从小部件获取绘制请求到样式完成绘制,以设置复选框的自定义样式。 要详细了解每个小部件的绘制方式,您需要按照此处的步骤逐步进行编码。
但是,通常足以知道小部件绘制哪些样式元素。 该小部件将构建一个样式选项,并调用该样式一次或多次以绘制其组成的样式元素。
通常,知道小部件可能处于的状态以及样式选项的其他内容(即我们在下一节中列出的内容)也就足够了。