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 flagSet
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);
     }
 
从代码提取中可以看出,通用样式获取CE_CheckBox的两个子元素的边界矩形,然后绘制它们。 如果复选框具有焦点,则还会绘制焦点框。
     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);
     }
 }

  我们已经看到了如何从小部件获取绘制请求到样式完成绘制,以设置复选框的自定义样式。 要详细了解每个小部件的绘制方式,您需要按照此处的步骤逐步进行编码。

但是,通常足以知道小部件绘制哪些样式元素。 该小部件将构建一个样式选项,并调用该样式一次或多次以绘制其组成的样式元素。

通常,知道小部件可能处于的状态以及样式选项的其他内容(即我们在下一节中列出的内容)也就足够了。

 

posted @ 2020-11-03 22:21  流浪侠客  阅读(1308)  评论(0编辑  收藏  举报