样式和样式感知部件

样式(继承QStyle 的类)代表部件绘制,并封装图形用户界面的外观和感觉。QStyle 类是一个抽象基类,它封装了图形用户界面的外观和感觉。Qt 的内置 widget 几乎全部使用该类进行绘制,确保它们看起来与本地 widget 一模一样。

Qt 有多种内置样式可供选择。某些样式只能在特定平台上使用。自定义样式可通过插件或使用QStyleFactory::create() 创建特定样式类的实例并使用QApplication::setStyle() 进行设置。

自定义样式

要定制现有样式,可继承QProxyStyle 并重新实现所需的虚拟方法。QProxyStyle 允许指定特定的基本样式,或者在未指定基本样式时自动使用应用程序样式。前者提供了对基本样式的完全控制,在定制期望某种样式行为时效果最佳,而后者提供了一种与平台无关的定制应用程序样式的方法,默认为本地平台样式。

实现自定义样式

QCommonStyle 为完全自定义样式的实现提供了方便的基础。方法与 相同,只是继承 并重新实现相应的虚拟方法。实现完整的自定义样式涉及到一些问题,因此我们提供了本概述。我们将逐步介绍如何为单个 Qt Widget 创建样式。我们将研究 虚拟函数、成员变量和枚举。QProxyStyle QCommonStyle QStyle

本文档中与单个部件样式无关的部分应按顺序阅读,因为后面的部分往往依赖于前面的部分。在实现样式时,可以参考部件的说明。不过,在某些情况下,您可能需要查阅 Qt 源代码。阅读本文档后,样式化过程的顺序应该会变得清晰,这将有助于您找到相关代码。

要开发样式感知的部件(即符合其绘制样式的部件),您需要使用当前样式来绘制它们。本文档介绍了部件的绘制方式,以及样式赋予它们的可能性。

部件样式类

这些类用于自定义应用程序的外观和样式。

QColor

基于 RGB、HSV 或 CMYK 值的颜色

QColorSpace

色彩空间抽象

QColorTransform

色彩空间之间的转换

QCommonStyle

封装图形用户界面的通用外观和感觉

QCursor

任意形状的鼠标指针

QFont

指定用于绘制文本的字体查询

QFontDatabase

关于底层窗口系统中可用字体的信息

QFontInfo

有关字体的一般信息

QGraphicsAnchor

代表 QGraphicsAnchorLayout 中两个项目之间的锚点

QGraphicsAnchorLayout

可在图形视图中将部件锚定在一起的布局

QPalette

包含每个部件状态的颜色组

QStyle

封装图形用户界面外观的抽象基类

QStyleFactory

创建 QStyle 对象

QStyleHintReturn

返回基本数据类型以外的样式提示

QStyleHintReturnMask

返回 QRegion 的样式提示

QStyleHintReturnVariant

返回 QVariant 的样式提示

QStyleOption

存储 QStyle 函数使用的参数

QStylePainter

在 widget 中绘制 QStyle 元素的便利类

QStyle 实现

QStyle 的 API 包含绘制窗口小部件的函数、完成常见和困难任务(如计算滑块手柄的位置)的静态辅助函数,以及在绘制时进行各种必要计算(如窗口小部件计算尺寸提示)的函数。该样式还能帮助某些部件进行内容布局。此外,它还会创建一个包含QBrushes 的QPalette ,用于绘图。

QStyle 绘制函数用于绘制图形元素;元素是一个部件或部件的一部分,如按钮斜面、窗口边框或滚动条。现在,大多数绘制函数都需要四个参数:

  • 一个枚举值,指定要绘制的图形元素
  • 一个QStyleOption ,指定如何以及在何处渲染该元素
  • 用于绘制元素的QPainter
  • 进行绘制的QWidget (可选)

当 widget 要求样式绘制元素时,它会向样式提供QStyleOption ,这是一个包含绘制所需信息的类。有了QStyleOption ,就可以让QStyle 绘制部件,而无需为部件链接任何代码。这使得在任何绘画设备上使用QStyle 的绘制函数成为可能,也就是说,您可以在任何部件上绘制组合框,而不仅仅是在QComboBox 上。

部件作为最后一个参数传递,以防样式需要它来执行特殊效果(如 macOS 上的默认按钮动画),但这并不是强制性的。

在本节中,我们将了解样式元素、样式选项以及QStyle 的功能。最后,我们将介绍如何使用调色板。

项目视图中的项目由 Qt 中的委托绘制。项目视图的标题仍由样式绘制。Qt 的默认委托(QStyledItemDelegate )部分通过当前样式绘制项目;它绘制复选框指示符,并为项目所包含的元素计算边界矩形。在本文档中,我们仅介绍如何实现QStyle 子类。如果您希望为QStyledItemDelegate 以外的其他数据类型添加支持,则需要实现自定义委托。请注意,必须以编程方式为每个部件设置委托(即默认委托不能作为插件提供)。

样式元素

样式元素是图形用户界面的图形部分。部件由样式元素的层次结构(或树形结构)组成。例如,当一个样式接收到绘制按钮的请求时(例如从QPushButton ),它会绘制一个标签(文本和图标)、一个按钮斜面和一个焦点框。按钮斜面则由斜面周围的边框和其他两个元素组成,我们稍后将讨论这两个元素。下面是按钮元素树的概念图。我们将在浏览各个小部件时看到QPushButton 的实际树形图。

小部件的绘制并不一定只要求样式绘制一个元素。部件可以多次调用样式来绘制不同的元素。QTabWidget 就是一个例子,它可以单独绘制标签和框架。

有三种元素类型:原始元素、控制元素和复杂控制元素。元素由ComplexControlControlElementPrimitiveElement 枚举定义。每个元素枚举的值都有一个前缀来标识其类型:CC_ 表示复杂元素,CE_ 表示控制元素,PE_ 表示原始元素。我们将在以下三节中了解不同元素的定义,并观看使用这些元素的 widget 示例。

QStyle 类说明中包含这些元素的列表以及它们在设计 widget 风格中的作用。我们将在为单个部件设计样式时了解它们是如何使用的。

原始元素

原始元素是常见的 GUI 元素,经常被多个 Widget 使用。例如框架、按钮斜面以及用于旋转框、滚动条和组合框的箭头。原始元素不能独立存在:它们始终是更大结构的一部分。它们不参与与用户的交互,只是图形用户界面中的被动装饰。

控制元素

控制元素向用户执行操作或显示信息。控制元素的例子包括按钮、复选框以及表格和树状视图中的标题部分。控制元素不一定是完整的部件,如按钮,也可以是部件的一部分,如标签栏标签和滚动条滑块。它们与原始元素的不同之处在于,它们不是被动的,而是在与用户的交互中发挥着作用。由多个元素组成的控件通常使用样式来计算元素的边界矩形。可用的子元素由SubElement 枚举定义。该枚举仅用于计算矩形边界;子元素并不像原始元素、控件元素和复杂元素那样是要绘制的图形元素。

复杂控制元素

复杂控制元素包含子控制。复杂控件会根据用户使用鼠标的位置和按下的键盘键而有不同的行为。这取决于鼠标在哪个子控件(如果有)上移动或按下哪个子控件。复杂控件的例子有滚动条和组合框。使用滚动条时,可以用鼠标移动滑块并按下上行和下行按钮。可用的子控件由SubControl 枚举定义。

除了绘制之外,样式还需要为部件提供按下鼠标时所指向的子控件(如果有的话)的信息。例如,QScrollBar 需要知道用户按下的是滑块、滑块槽还是其中一个按钮。

请注意,子控件与上一节描述的控件元素不同。您不能使用样式来绘制子控件;样式只会计算绘制子控件的边界矩形。不过,复杂元素通常使用控件和基元元素来绘制子控件,这也是 Qt 内置样式和 Java 样式经常使用的方法。例如,Java 风格使用 PE_IndicatorCheckBox 来绘制组框中的复选框(它是CC_GroupBox 的子控件)。某些子控件具有等效的控件元素,例如滚动条滑块 (SC_SCrollBarSliderCE_ScrollBarSlider)。

其他 QStyle 任务

如前所述,样式元素和部件使用样式来计算子元素和子控件的边界矩形。像素度量是以屏幕像素为单位的与样式相关的尺寸,也用于绘制时的测量。可用的矩形和像素度量由QStyle 中的三个枚举表示:SubElementSubControlPixelMetric 。这些枚举的值以 SE_、SC_ 和 PM_ 开头,因此很容易识别。

样式还包含一组样式提示,用StyleHint 枚举中的值表示。在不同的样式中,所有部件的功能和外观都不尽相同。例如,当菜单中的菜单项在屏幕上不适合单列显示时,一些样式支持滚动,而另一些样式则绘制多列来显示所有项目。

一种样式通常有一组标准图像(如警告、问题和错误图像),用于消息框、文件对话框等。QStyle 提供了StandardPixmap 枚举。其值代表标准图像。Qt 的 Widgets 使用这些图像,因此当您实现自定义样式时,应提供正在实现的样式所使用的图像。

样式计算布局中部件之间的间距。有两种方法可以处理这些计算。您可以设置PM_LayoutHorizontalSpacingPM_LayoutVerticalSpacing ,这也是 Java 样式的处理方式(通过QCommonStyle )。或者,如果您需要对布局的这一部分进行更多控制,可以实现QStyle::layoutSpacing() 和 QStyle::layoutSpacingImplementation() 函数。在这些函数中,您可以根据控件类型 (QSizePolicy::ControlType) 计算不同尺寸策略 (QSizePolicy::Policy) 的间距,还可以计算相关 widget 的样式选项。

样式选项

QStyleOption 的子类包含样式化单个元素所需的所有信息。样式选项由QStyle 函数的调用者实例化(通常在堆栈中)和填写。根据所绘制的样式,样式选项类也会有所不同。例如,QStyle::PE_FrameFocusRect 元素需要一个QStyleOptionFocusRect 参数,而且可以创建自定义样式可以使用的自定义子类。出于性能考虑,样式选项保留了公共变量。

部件可以处于多种不同的状态,这些状态由State 枚举定义。有些状态标志根据部件的不同而有不同的含义,但有些则是所有部件通用的,如State_DisabledQStyleOption 通过QStyleOption::initFrom() 设置通用状态;其余状态由各个部件设置。

最值得注意的是,样式选项包含要绘制的 widget 的调色板和边界矩形。例如,QPushButtonQCheckBox 使用QStyleOptionButton 作为样式选项,其中包含文本、图标和图标大小。所有选项的具体内容将在我们介绍各个部件时进行说明。

在重新实现使用QStyleOption 参数的QStyle 函数时,通常需要将QStyleOption 转换为子类(如QStyleOptionFocusRect )。为了安全起见,可以使用qstyleoption_cast() 来确保指针类型正确。如果对象类型不正确,qstyleoption_cast() 将返回nullptr 。例如

const QStyleOptionFocusRect *focusRectOption =
        qstyleoption_cast<const QStyleOptionFocusRect *>(option);
if (focusRectOption) {
    ...
}

下面的代码片段说明了如何使用QStyle 从自定义 widget 的 paintEvent() 中绘制焦点矩形:

void MyWidget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    ...

    QStyleOptionFocusRect option(1);
    option.init(this);
    option.backgroundColor = palette().color(QPalette::Window);

    style().drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter,
                          this);
}

下一个示例展示了如何从现有样式派生以自定义图形元素的外观:

class CustomStyle : public QProxyStyle
{
    Q_OBJECT

public:
    CustomStyle(const QWidget *widget);
    ~CustomStyle() {}

    void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                       QPainter *painter, const QWidget *widget) const override;
};

void CustomStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                                QPainter *painter, const QWidget *widget) const
{
    if (element == PE_IndicatorSpinUp || element == PE_IndicatorSpinDown) {
        QPolygon points(3);
        int x = option->rect.x();
        int y = option->rect.y();
        int w = option->rect.width() / 2;
        int h = option->rect.height() / 2;
        x += (option->rect.width() - w) / 2;
        y += (option->rect.height() - h) / 2;

        if (element == PE_IndicatorSpinUp) {
            points[0] = QPoint(x, y + h);
            points[1] = QPoint(x + w, y + h);
            points[2] = QPoint(x + w / 2, y);
        } else { // PE_SpinBoxDown
            points[0] = QPoint(x, y);
            points[1] = QPoint(x + w, y);
            points[2] = QPoint(x + w / 2, y + h);
        }

        if (option->state & State_Enabled) {
            painter->setPen(option->palette.mid().color());
            painter->setBrush(option->palette.buttonText());
        } else {
            painter->setPen(option->palette.buttonText().color());
            painter->setBrush(option->palette.mid());
        }
        painter->drawPolygon(points);
    } else {
        QProxyStyle::drawPrimitive(element, option, painter, widget);
    }
}

QStyle 函数

QStyle 类定义了三个函数,用于绘制原始元素、控件和复杂元素:drawPrimitive()、drawControl()和drawComplexControl()。这些函数需要以下参数:

  • 要绘制元素的枚举值。
  • 包含绘制元素所需信息的QStyleOption
  • 用于绘制元素的QPainter
  • 一个指向QWidget 的指针,通常是绘制元素的 widget。

并非所有部件都会发送指向自己的指针。如果发送给函数的样式选项不包含您需要的信息,您应该检查部件的实现,看看它是否发送了指向自身的指针。

QStyle 类还提供了用于绘制元素的辅助函数。drawItemText() 函数将QPalette 作为参数,在指定的矩形范围内绘制文本。drawItemPixmap() 函数有助于在指定的边界矩形内对齐像素图。

其他QStyle 函数为绘图函数进行各种计算。如果窗口小部件自己绘制多个样式元素,它们也会使用这些函数计算尺寸提示和边界矩形。与绘制元素的函数一样,辅助函数通常使用相同的参数。

  • subElementRect() 函数获取SubElement 枚举值,并计算子元素的边界矩形。样式使用该函数知道在哪里绘制元素的不同部分。这样做主要是为了重复使用;如果创建新样式,可以使用与超类相同的子元素位置。
  • subControlRect() 函数用于计算复杂控件中子控件的边界矩形。实现新样式时,需要重新实现subControlRect() 并计算与超类不同的矩形。
  • pixelMetric() 函数返回一个像素度量值,这是一个与样式相关的尺寸,单位为屏幕像素。它接收PixelMetric 枚举的值,并返回正确的度量值。请注意,像素度量并不一定是静态度量,也可以通过样式选项等进行计算。
  • hitTestComplexControl() 函数返回复杂控件中鼠标指针所在的子控件。通常,只需使用subControlRect() 获取子控件的边界矩形,然后查看哪个矩形包含光标的位置即可。

QStyle 此外,ASP.NET 还具有 () 和 () 函数。所有部件在显示前都会发送到 函数,隐藏时则发送到 。您可以使用这些函数来设置部件的属性,或执行样式所需的其他工作。例如,如果需要知道鼠标何时悬停在部件上,就需要设置 部件属性。然后, 状态标志将在 widget 的样式选项中设置。polish unpolish polish() unpolish() WA_Hover State_MouseOver

QStyle 在 Windows XP 中,有一些静态辅助函数可以完成一些常见和困难的任务。它们可以根据滑块的值计算滑块手柄的位置,并根据反向布局变换矩形和绘制文本;更多详情请参见 类文档。QStyle

在重新实现QStyle 虚拟函数时,通常的做法是在与超类不同的元素上执行工作;对于所有其他元素,可以简单地使用超类的实现。

调色板

每种样式都提供一种颜色(即QBrush )调色板,用于绘制部件。不同的部件状态 (QPalette::ColorGroup) 有一组颜色:活动(窗口中键盘焦点所在的部件)、非活动(用于其他窗口的部件)和禁用(被禁用的部件)。这些状态可通过查询State_ActiveState_Enabled 状态标志找到。每一组都包含由QPalette::ColorRole 枚举给出的特定颜色角色。这些角色描述了在何种情况下应使用这些颜色(例如,用于绘制部件背景、文本或按钮)。

如何使用颜色角色取决于样式。例如,如果样式使用渐变,可以使用调色板颜色,并通过QColor::darker() 和QColor::lighter() 使其变深或变浅,从而创建渐变。一般来说,如果需要调色板不提供的笔刷,应尽量从调色板中派生出来。

QPalette调色板(palette)提供了用于存储不同部件状态和颜色角色的颜色。样式的调色板由standardPalette() 返回。在应用程序 (QApplication::setStyle()) 或部件 (QWidget::setStyle()) 上设置新样式时,标准调色板不会自动安装,因此必须使用 (QApplication::setPalette()) 或 (QWidget::setPalette()) 自行设置调色板。

不建议硬编码颜色,因为应用程序和单个部件可以设置自己的调色板,也可以使用其样式的调色板进行绘制。请注意,Qt 的部件都不设置自己的调色板。Java 样式确实对某些颜色进行了硬编码,但这只是作者的决定,我们并不建议这样做。当然,我们并不希望该样式在使用任何调色板时都能保持良好的外观。

实现问题

在实现样式时,有几个问题需要考虑。我们将在这里给出一些实施方面的提示和建议。

在实现样式时,有必要查看部件的代码和基类及其祖先的代码。这是因为部件使用样式的方式不同,因为不同样式的虚函数中的实现方式会影响绘制状态(例如,改变QPainter 状态而不恢复,绘制某些元素而不使用适当的像素度量和子元素)。

建议样式不要使用QStyle::sizeFromContents() 函数改变部件的建议尺寸,而是让QCommonStyle 实现来处理。如果需要更改,应尽量将更改幅度控制在较小范围内;如果在各种样式中,窗口小部件的布局看起来差别很大,应用程序的开发可能会很困难。

Java 风格

我们实现了一种类似于 Java 默认外观和感觉(以前称为 Metal)的样式。我们这样做是因为它的实现相对简单,而且我们想为这份概述文档建立一种风格。为了保持简洁而不过于广泛,我们在一定程度上简化了样式,但 Qt 完全可以完全复制该样式。不过,目前还没有具体计划将该样式作为 Qt 的一部分来实现。

在本节中,我们将探讨一些实现问题。最后,我们将看到一个关于 Java 部件样式的完整示例。在整个文档中,我们将继续在示例和 widget 图像中使用 Java 样式。实现本身涉及的内容较多,不建议您通读。

设计与实现

设计样式的第一步是选择基类。我们选择了子类QCommonStyle 。该类实现了我们所需的大部分功能,但不包括实际绘制。

样式在一个类中实现。我们之所以这样做,是因为我们发现将所有代码保存在一个文件中非常方便。此外,这也是优化方面的一个优势,因为我们实例化的对象更少。我们还通过使用开关来确定在函数中绘制哪个元素,从而将函数的数量保持在最低水平。这将导致函数数量庞大,但由于我们在开关中为每个元素划分了代码,因此代码仍然易于阅读。

限制和与 Java 的差异

我们没有完全实现 Java 风格中的每个元素。这样,我们就减少了代码的数量和复杂性。总的来说,该样式只是作为本样式概述文档的一个实用示例,而不是 Qt 本身的一部分。

并非所有部件都实现了所有状态。常见的状态也是如此,例如State_Disabled 。不过,至少有一个部件实现了每种状态。

我们只在滑块下方实现了刻度。扁平按钮也不包括在内。我们没有处理标题栏和停靠窗口标题对其内容而言过小的情况,而只是在彼此之上绘制了子控件。

我们没有尝试模拟 Java 字体。Java 和 Qt 使用的字体引擎大相径庭,因此我们认为不值得为此付出努力,因为我们只是将这种样式作为本概述的一个示例。

我们对线性渐变的颜色进行了硬编码(我们没有使用QPalette ),例如用于按钮斜面、工具栏和复选框的颜色。这是因为 Java 调色板无法生成这些颜色。无论如何,Java 不会根据部件颜色组或角色改变这些颜色(它们不依赖于调色板),因此在任何情况下都不会造成问题。

风格化的是 Qt 的部件。有些部件在 Java 中根本不存在,如QToolBox 。还有一些部件包含 Java 部件不包含的元素。树部件就是后者的一个例子,Java 的 JTree 没有标题。

样式不处理反向布局。我们假定布局方向是从左到右。QCommonStyle 可处理反向部件;如果我们实现了反向布局,那么我们改变子元素位置或自己处理标签中文本对齐方式的部件就需要更新。

Java 复选框的样式

作为一个例子,我们将研究 Java 样式中复选框的样式。我们将描述整个过程,并打印 Java 样式和 Qt 类中涉及的所有代码。在本文的其余部分,我们将不检查单个窗口小部件的源代码。如果您需要检查具体的实现细节,希望这能为您提供一个搜索代码的思路;大多数部件都遵循与复选框相同的结构。我们对QCommonStyle 代码进行了一些编辑,删除了与复选框样式不直接相关的代码。

我们首先来看看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() 中所有部件的通用信息设置选项。我们很快就会看到这一点。

QStyleOption当用户按下复选框时,down 变量为true ;无论复选框是否被选中,都是如此。当我们使用三态复选框且复选框被部分选中时,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 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 窗口标志时,才会设置State_MouseOver 。请注意,必须在 Qt XML 中启用键盘导航,State_HasEditFocus 才会包含在内;默认情况下并不包含。

除了设置状态标志外,QStyleOption 还包含有关 widget 的其他信息:direction 是布局的布局方向,rect 是 widget 的边界矩形(绘制区域),palette 是绘制 widget 时应使用的QPalettefontMetrics 是 widget 使用的字体的度量。

我们给出一个复选框的图片和与之匹配的样式选项。

Java 风格复选框

上述复选框的样式选项中将包含以下状态标志:

状态标志设置
State_Sunken设置
State_NoChange
State_On
State_Off
State_MouseOver
State_Enabled
State_HasFocus
State_KeyboardFocusChange
State_Active

QCheckBoxQWidget::paintEvent() 中绘制自己的样式选项optQStylePainter pQStylePainter 类是用于绘制样式元素的方便类。最值得注意的是,它封装了QStyle 中用于绘制的方法。QCheckBox 的绘制过程如下:

    QStylePainter p(this);
    QStyleOptionButton opt = d->getStyleOption();
    p.drawControl(QStyle::CE_CheckBox, opt);

QCommonStyle 处理 CE_CheckBox 元素。 有两个子元素: 还实现了这些子元素的边界矩形。接下来,我们来看看 代码:QCheckBox 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 两个子元素的边界矩形,然后绘制它们。如果复选框有焦点,也会绘制焦点框。

Java 样式绘制 CE_CheckBoxIndicator,而QCommonStyle 则处理 CE_CheckboxLabel。我们将检查每种实现,并从 CE_CheckBoxLabel 开始:

    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()) {
        const auto dpr = p->device()->devicePixelRatio();
        pix = btn->icon.pixmap(btn->iconSize, dpr,
                               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() 将对齐方式、布局方向和助记符考虑在内,绘制文本。它还会使用调色板将文字绘制成合适的颜色。

标签的绘制通常比较复杂。幸运的是,这通常可以由基类来处理。Java 风格实现了自己的按钮标签,因为当按钮有图标时,Java 也会将按钮内容居中。如果您需要重新实现标签绘制的示例,可以查看该实现。

现在我们来看看drawControl() 中 CE_CheckBoxIndicator 的 Java 实现:

        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;

我们首先要保存绘制器的状态。这并不总是必要的,但在本例中,QCommonStyle 需要画图器处于调用 PE_IndicatorCheckBox 时的状态(当然,我们也可以通过函数调用来设置状态)。然后,我们使用drawButtonBackground() 绘制复选框指示器的背景。这是一个辅助函数,用于绘制背景以及按钮和复选框的边框。下面我们就来看看这个函数。然后,我们检查鼠标是否悬停在复选框上。如果是,我们就绘制复选框未按下且鼠标悬停在复选框上时 Java 复选框所具有的边框。您可能会注意到,Java 不处理三态框,因此我们没有实现它。

在这里,我们使用 PNG 图像作为指示器。我们还可以检查窗口小部件是否被禁用。这样,我们就必须使用另一张图片,并将指示器设置为禁用颜色。

void JavaStyle::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);
    }
}

我们已经了解了在 Java 样式中,从部件收到绘制请求到完成绘制样式,复选框是如何被绘制的。要详细了解每个部件是如何绘制的,您需要像我们在这里所做的那样,一步一步地查看代码。不过,通常只要知道部件绘制的样式元素就足够了。部件会建立一个样式选项,并调用该样式一次或多次,以绘制由其组成的样式元素。通常情况下,只要知道部件可以处于的状态以及样式选项的其他内容(即我们在下一节中列出的内容)就足够了。

小部件演练

在本节中,我们将研究 Qt 的大多数 Widget 是如何进行样式化的。希望这能为您在开发自己的样式和部件时节省一些时间和精力。您不会在这里找到其他地方无法获得的信息(例如,通过检查源代码或样式相关类的类描述)。

我们主要使用 Java 样式部件作为示例。Java 样式不会绘制元素树中的每个元素。这是因为在 Java 样式中,这些元素对该 widget 来说是不可见的。我们仍然确保所有元素的实现方式都符合 Java 风格,因为自定义 widget 可能需要它们(但这并不排除将实现方式留给QCommonStyle )。

每个部件都有以下内容:

  • 样式选项成员(变量等)表。
  • 可在 widget 上设置的状态标志表 (QStyle::StateFlag) 以及设置状态的时间。
  • 元素树(参见 "样式元素"部分)。
  • 勾画元素的 widget 图像。

元素树包含原始、控制和复杂样式元素。通过自顶向下遍历元素树,可以获得元素的绘制顺序。在节点中,我们写入了子元素矩形、子控制元素以及绘制节点元素时应考虑的像素指标。

我们的样式设计方法以绘制小部件为中心。在绘制过程中使用的子元素矩形、子控件和像素指标的计算仅作为元素树的内容列出。需要注意的是,有些矩形和像素度量仅用于部件。这使得这些计算未在演练中处理。例如,subControlRect() 和sizeFromContents() 函数经常调用subElementRect() 来计算它们的边界矩形。我们也可以为此绘制树。不过,如何进行这些计算完全取决于各个样式,它们不必遵循特定的结构(Qt 并不强加特定的结构)。不过,您仍应确保使用适当的像素度量。因此,为了限制文件的大小,我们选择不包含树或描述 Java(或任何其他)样式的计算。

在检查树时,您可能会对如何使用不同的像素度量、子元素矩形和子控制矩形感到困惑。如果您在阅读QStyle 枚举说明后仍有疑问,我们建议您检查QCommonStyle 的实现。

我们在部件图像中勾画的一些边界矩形是相等的。原因是有些元素绘制的是背景,而有些元素绘制的是框架和标签。如有疑问,请查看QStyle 中对每个元素的描述。此外,有些元素是用来布局的,即决定在哪里绘制其他元素。

通用部件属性

有些状态和变量对所有部件都是通用的。这些状态和变量可通过QStyleOption::initFrom() 设置。并非所有元素都使用该函数;是部件创建了样式选项,对于某些元素来说,initFrom() 中的信息并不是必需的。

下面的表格列出了常用的状态:

状态状态
State_Enabled当窗口部件未禁用时设置(参见QWidget::setEnabled()
State_Focus在部件有焦点时设置(参见QWidget::hasFocus()
State_KeyboardFocusChange用户使用键盘更改焦点时设置(请参阅Qt::WA_KeyboardFocusChange)
State_MouseOver当鼠标光标位于窗口小部件上方时设置
State_Active设置窗口部件是否是活动窗口的子窗口。
State_HasEditFocus设置窗口部件是否具有编辑焦点

部件的其他常用成员包括

成员内容
矩形要绘制的元素的边界矩形。它被设置为 widget 的边界矩形 (QWidget::rect()).
方向布局方向;Qt::LayoutDirection 枚举的值。
调色板绘制元素时使用的QPalette 。它被设置为 widgets 调色板 (QWidget::palette()).
字体度量绘制 widget 上的文本时使用的QFontMetrics

用于复杂样式元素的复杂样式选项(继承QStyleOptionComplex 的类)共享两个变量:subControlsactiveSubControls 。这两个变量都是QStyle::SubControl 枚举值的 OR 编辑组合。它们表示复杂控件由哪些子控件组成,以及这些控件中哪些当前处于活动状态。

如前所述,样式会计算 widget 内容的大小,而 widget 会根据这些内容计算其大小提示。此外,复合控件还使用样式来测试鼠标移到了哪个子控件上。

部件参考

接下来,我们将为您介绍 widget 指南;每个 widget 都有自己的子章节。

按钮

按钮的样式结构如下所示。通过自上而下地遍历树状结构,可以获得元素的绘制顺序。

按钮的样式结构

在元素边界方面,按钮的布局因样式而异。因此很难显示相关的概念图像。此外,元素可能--甚至本意是--具有相同的边界;例如,PE_PushButtonBevel ,在QCommonStyle 中用于绘制它所包含的元素:PE_FrameDefaultButton PE_FrameButtonBevel PE_PanelButtonCommand PE_PushButtonBevel 还负责绘制菜单指示器( 绘制 )。QCommonStyle PE_IndicatorArrowDown

下面是一张 Java 样式的按钮图像,显示了各元素的边界矩形。颜色是用来分隔图像中的边界矩形的,并没有其他用途。其他部件的类似图像也是如此。

Java 风格以及 Qt 中实现的所有其他风格都不使用PE_FrameButtonBevel 。通常,带有PE_DefaultFrame 的按钮会通过PM_ButtonDefaultIndicator 调整PE_PanelButtonCommand 的矩形。通过调整PM_DefaultFrameWidth 的矩形,可以找到CE_PushButtonLabel

现在我们来看看按钮的样式选项 -QStyleOptionButton 。下面是QPushButton 可以在样式选项上设置的状态表:

状态设置状态
State_Sunken按钮向下或菜单按下时 显示
State_On按钮被选中
State_Raised按钮未平且未按下

QStyleOptionButton 的其他成员是:

成员内容
功能QStyleOptionButton::ButtonFeatures 枚举的标志,用于描述各种按钮属性(参见枚举)
图标按钮QIcon (如果有)
图标大小图标的QSize
文本包含按钮文本的QString

单选按钮和复选按钮

单选按钮和复选按钮的结构完全相同。我们使用QCheckBox 元素和像素度量名称来显示结构:

QStyleOptionButton 作为复选和单选按钮的样式选项。我们首先列出了选项中可设置的状态:

状态设置状态
State_sunken方框被按下
State_NoChange复选框被部分选中(用于三态复选框。)
State_On复选框被选中
State_Off复选框未选中

有关QStyleOptionButton 类中其他成员的列表,请参阅 "按钮"

选项卡

在 Qt XML 中,QTabBar 使用样式来绘制标签。标签既可以存在于包含QTabBarQTabWidget 中,也可以作为单独的栏存在。如果条形图不是标签部件的一部分,它将绘制自己的底图。

QTabBar 因此,样式无法控制标签页的位置。不过,在布局标签时,条形图会要求样式提供 和 ,这是在标签栏标签(图标和文本)最小尺寸之外的额外宽度和高度。样式还可以在布局之前进一步影响标签页的大小,因为标签栏会要求 。标签栏的边界矩形由标签部件决定,当它是部件的一部分时(仍考虑 )。PM_TabBarTabHSpace PM_TabBarTabVSpace CT_TabBarTab CT_TabBarTab

标签栏负责绘制按钮,当所有标签都不合适时,按钮就会出现在标签栏上。按钮的位置不受样式控制,但按钮是QToolButtons,因此由样式绘制。

以下是QTabWidgetQTabBar 的样式结构:

虚线表示QTabWidget 包含一个选项卡栏,但并不绘制选项卡栏本身。QTabBar 只在不属于选项卡 widget 时绘制其基线,并保留两个工具按钮,在所有选项卡都不合适时滚动选项卡栏;有关其元素树,请参阅工具按钮。还需注意的是,由于按钮是选项卡栏的子项,因此它们是在栏之后绘制的。标签页的边界矩形与基底重叠PM_TabBarBaseOverlap

下面是一个 Java 风格的标签部件:

在 Java 样式中,选项卡栏形状和标签的边界矩形与CE_TabBarTab 相同。请注意,标签与标签 Widget 框架重叠。标签栏的底部(如果绘制)是标签与框架重叠的区域。

制表符的样式选项 (QStyleOptionTab) 包含绘制制表符的必要信息。该选项包含标签在标签栏中的位置、所选标签的位置、标签的形状、文本、图标和图标的大小。

由于 Java 样式的选项卡不会重叠,因此我们还提供了常用样式的选项卡部件图像。请注意,如果您希望标签在水平方向上重叠,请在CE_TabBarTabShape 中绘制标签时进行;标签的边界矩形不会因标签栏而改变。选项卡从左到右绘制为北选项卡栏形状,从上到下绘制为东选项卡栏形状,等等。选中的选项卡最后绘制,以便于在其他选项卡上绘制(如果它要变大的话)。

下面的表格列出了标签栏可以在其标签上设置的状态:

状态设置状态
State_Sunken标签页被鼠标按下。
State_Selected如果是当前标签页。
State_HasFocus选项卡栏有焦点,选项卡被选中。

请注意,即使标签栏未被禁用,单个标签页也可能被禁用。如果选项卡栏处于活动状态,则选项卡将处于活动状态。

下面是QStyleOptionTab 的成员表:

成员内容
角部件CornerWidget 枚举的标志,用于指示标签栏是否有以及有哪些角部件。
图标选项卡的QIcon
图标大小图标的QSize
位置一个 TabPosition 枚举值,表示相对于其他标签页,该标签页在条形图上的位置。
标签页所在的行。
选中位置SelectedPosition 枚举的值,用于指示所选选项卡是与该选项卡相邻还是属于该选项卡。
形状QTabBar::Shape 枚举的一个值,用于指示制表符是圆角还是三角角,以及制表符的方向。
文本标签页文本。

标签部件的框架使用QStyleOptionTabWidgetFrame 作为样式选项。我们在此列出其成员。除了常用标志外,它没有状态设置。

成员内容
左角小部件尺寸左角 widget 的QSize (如果有)。
右角部件大小右角部件(如果有)的QSize
线宽保存绘制面板的线宽。
中间线宽度该值目前始终为 0。
形状标签栏上标签的形状。
标签栏大小标签栏的QSize

滚动条

下面是滚动条的样式结构:

QScrollBar 只需创建其样式选项,然后绘制 。有些样式使用 绘制添加页面和子页面的背景,还使用指示箭头绘制下一行和上一行的指示箭头;我们没有将这些样式包含在树中,因为它们的使用由各个样式自行决定。样式的 是鼠标从滚动条边界移动并仍能移动手柄的最大距离(以像素为单位)。CC_ScrollBar PE_PanelButtonBevel PM_MaximumDragDistance

下面是一张 Java 样式的滚动条图片:

您可能会注意到,该滚动条与 Java 的滚动条略有不同,因为它有两个直线指示器。我们这样做是为了说明,您可以为一个子控件设置两个独立的边界矩形。滚动条是一个完全由 Java 风格实现的部件示例--QCommonStyle 不参与绘制。

我们来看看滚动条在样式选项上可以设置的不同状态:

状态设置状态
State_Horizontal滚动条处于水平状态。

QScrollBar 的样式选项是QStyleOptionSlider 。下表列出了其成员。所有QAbstractSliders 都使用该选项;我们在此仅描述与滚动条相关的成员。

成员内容
最大值滚动条的最大值。
最小值滚动条的最小值。
凹槽目标凹槽之间的像素数。
方向Qt::Orientation 枚举的一个值,用于指定滚动条是垂直方向还是水平方向。
页面步数按页面步数增加或减少滑块值(相对于滑块大小及其值范围)的数值。
单步按单步(或行)增加或减少滑块值的数字。
滑块值滑块的值。
滑块位置滑块手柄的位置。如果滚动条是QAbstractSlider::tracking ,则与sliderValue 相同。否则,在鼠标释放手柄之前,滚动条不会更新其值。
上下颠倒保留滚动条增加值的方向。对于所有抽象滑动条,该值将代替QStyleOption::direction

滑块

在计算滑块的尺寸提示时,PM_SliderThicknessPM_SliderLength 是通过样式查询的。与滚动条一样,只有当鼠标位于滑块边界PM_MaximumDragDistance 范围内时,QSlider 才允许用户移动手柄。在绘制自身时,它会创建样式选项,并调用drawComplexControl()CC_Slider

我们还展示了一张 Java 样式的滑块图片。我们显示了子元素的边界矩形,因为所有绘制都是在CC_Slider 中完成的。

QSlider 与所有 一样,我们使用 。我们列出了影响 的成员表:QAbstractSlider QStyleOptionSlider QSlider

成员内容
最大值滑块的最大值。
最小值滑块的最小值。
凹槽目标每个凹槽之间的像素数。
方向Qt::Orientation 枚举值,用于指示滑块是垂直方向还是水平方向。
页面步数按页面步数增加或减少滑块值的数字。
单步按单步(或行)增加或减少滑块值的数字。
滑块值滑块的值。
滑块位置作为滑块值给出的滑块位置。如果滑块是tracking ,该值将等于sliderValue ;如果不是,滑块的值将不会改变,直到用鼠标释放手柄。
上下颠倒对于所有抽象滑块,该成员将代替QStyleOption::direction

请注意,滑块在反向布局时不使用方向,而是使用upsideDown

旋转框

QSpinBox 自绘时,它会创建一个QStyleOptionSpinBox 并要求样式绘制CC_SpinBox 。编辑栏是一个行编辑栏,是旋转框的子栏位。字段的尺寸由SC_SpinBoxEditField 样式计算。

下面是旋转框的样式树。并不要求样式使用按钮面板基元来绘制指示器背景。您可以在树的下面看到一张图片,显示了QSpinBox 中 Java 样式的子元素。

QStyleOptionSpinBox ,这是旋转框的样式选项。它可以在自旋框上设置以下状态:

状态设置状态
State_Sunken如果CC_SpinUpCC_SpinDown 中的一个子控件被鼠标按下,则会被设置。

自旋框样式选项中的其他成员包括

属性功能
框架布尔值,如果自旋框要绘制框架,则设置为true
按钮符号ButtonSymbols 枚举的值,用于决定上/下按钮的符号。
已启用步骤StepEnabled 枚举的值,表示哪些旋转框按钮被按下。

标题栏

标题栏复合控件CC_TitleBar 用于在QMdiArea 中绘制内部窗口的标题栏。它通常由窗口标题以及关闭、最小化、系统菜单和最大化按钮组成。某些样式还提供了窗口阴影按钮以及上下文相关帮助按钮。

条形图在CC_TitleBar 中绘制,不使用任何子元素。如何绘制按钮由各个样式自行决定,但样式应提供按钮的标准像素图。

在 Java 样式标题栏上方的图片中,我们显示了 Java 样式支持的子元素的边界矩形(所有子元素都使用标准像素图绘制)。通常使用PE_PanelButtonTool 绘制按钮背景,但这不是必须的。

标题栏的样式选项是QStyleOptionTitleBar 。其成员包括

成员内容
图标标题栏的图标。
文本标题栏标签的文本。
窗口标志Qt::WindowFlag 枚举的标志。QMdiArea 用于窗口管理的窗口标志。
标题栏状态这是包含标题栏的窗口的QWidget::windowState() 状态。

组合框

QComboBox 使用CC_ComboBoxCE_ComboBoxLabel 样式绘制不可编辑框的按钮和标签。

用户单击组合框时弹出的列表是由委托绘制的,我们在本概述中不会涉及。不过,您可以通过子元素SC_ComboBoxListBoxPopup 使用样式来控制列表的大小和位置。该样式还可以通过SC_ComboBoxEditField 决定可编辑框的编辑字段的位置;字段本身是QLineEdit ,是组合框的子元素。

我们展示了一张 Java 风格组合框的图片,其中我们勾画出了它的子元素和子元素矩形:

Java 组合框不使用焦点矩形;有焦点时,它的背景颜色会改变。SC_ComboBoxEdit 字段既被QComboBox 用于计算编辑字段的大小,也被样式用于计算组合框标签的大小。

组合框的样式选项是QStyleOptionComboBox 。它可以设置以下状态:

状态设置当
State_Selected框不可编辑且有焦点。
State_SunkenSC_ComboBoxArrow 处于活动状态。
State_on框的容器(列表)可见。

其他成员的样式选项有

成员内容
当前图标组合框中当前(选中)项目的图标。
当前文本框中当前项目的文本。
可编辑组合框是否可编辑。
框架组合框是否有边框。
图标大小当前项目图标的大小。
弹出矩形组合框弹出列表的边界矩形。

组框

计算尺寸提示时,QGroupBox 会从样式中获取三个像素指标:PM_IndicatorWidth PM_CheckBoxLabelSpacing PM_IndicatorHeight QGroupBox 具有以下样式元素树:

Qt XML 不对复选框的绘制方式进行限制;Java 样式则通过CE_IndicatorCheckBox 绘制复选框。请参阅 "复选框和单选按钮",了解完整的元素树。

我们还给出了一张绘制了子控件和子控件矩形的 widget 图像:

组框的样式选项是QStyleOptionGroupBox 。可对其设置以下状态:

状态设置当
State_On复选框被选中。
State_Sunken复选框被按下。
State_Off复选框未选中(或没有复选框)。

QStyleOptionGroupBox 的其余成员是

成员内容
特征QStyleOptionFrame::FrameFeatures 枚举中描述组框框架的标志。
线宽绘制面板的线宽。始终为 1。
文本组框的文本。
文本对齐方式组框标题的对齐方式。
文本颜色文本的QColor

分隔符

由于分割器结构简单,不包含任何子元素,因此我们不包含任何分割器图片。CE_Splitter 不使用任何其他元素或度量标准。

对于样式选项,分割器使用基类QStyleOption 。它可以设置以下状态标志:

状态设置 当
State_Horizontal如果是水平分割器,则设置。

QSplitter 不使用 () 设置其选项;它自己设置 和 标志。initFrom State_MouseOver State_Disabled

进度条

CE_ProgressBar 元素由QProgressBar 使用,也是该窗口小部件使用的唯一元素。我们从样式结构开始:

这是一个普通样式的进度条(Java 样式的边界矩形相等):

QProgressBar 的样式选项是QStyleOptionProgressBar 。进度条没有设置任何状态标志,但该选项的其他成员如下:

成员内容
最小值条形图的最小值。
最大值条形图的最大值。
进度条形图的当前值。
文本对齐方式文本在标签中的对齐方式。
文本可见是否绘制标签。
文本标签文本。
方向进度条可以是垂直或水平的。
倒置外观进度倒置(即在水平条中从右到左)。
从下至上布尔值,如果true ,垂直进度条的标签会旋转 90 度。

工具按钮

工具按钮既可以独立存在,也可以作为工具栏的一部分存在。无论哪种方式,它们的绘制效果都相同。QToolButton 只绘制一个样式元素:CC_ToolButton

下面是 widget 的样式结构树:

请注意,PE_FrameButtonToolPE_IndicatorArrowDown 包括在树状结构中,因为 Java 样式绘制了它们,但如果您愿意,也可以省略它们。结构也可能不同。QCommonStyle例如,我们在CE_ToolButton 中绘制了PE_IndicatorButtonDropDownPE_IndicatorArrowDown

我们还有一张工具按钮的图片,其中我们勾画了子元素边界矩形和子控件。

以下是工具按钮的状态表:

状态设置时间
State_AutoRise工具按钮设置了自动上升属性。
State_Raised按钮未下沉(即被选中或被鼠标按下)。
State_Sunken按钮处于下沉状态。
State_On按钮可选中并被选中。

QStyleOptionToolButton 还包含以下成员:

成员内容
箭头类型Qt::ArrowType 枚举值,包含按钮箭头的方向(如果使用箭头代替图标)。
特征QStyleOptionToolButton::ButtonFeature 枚举的标志,描述按钮是否有箭头、菜单和/或弹出延迟。
字体按钮标签的QFont
图标工具按钮的QIcon
图标大小按钮图标的大小。
位置按钮的位置,如QWidget::pos() 所示
文本按钮文本。
工具按钮样式Qt::ToolButtonStyle 枚举值,用于决定按钮是显示图标、文本还是同时显示图标和文本。

工具栏

工具栏是main window framework 的一部分,在建立样式选项时与所属的QMainWindow 合作。主窗口有 4 个可放置工具栏的区域。它们分别位于窗口的四边(即北、南、东和西)。每个区域内可以有多行工具栏;一行工具栏由方向相同(垂直或水平)的工具栏组成,这些工具栏相邻放置。

Toolbars 在 Qt 中,工具栏由三个元素组成:QMainWindowLayout 负责计算边界矩形(即工具栏及其内容的位置和大小)。在计算工具栏大小时,主窗口还使用了工具栏中项目的 。CE_ToolBar PE_IndicatorToolBarHandle PE_IndicatorToolBarSeparator sizeHint()

以下是QToolBar 的元素树:

虚线表示QToolBar 保留了 QToolBarLayout 的实例,QToolBarLayout 保留了 QToolBarSeparators。当工具栏是浮动的(即有自己的窗口)时,会绘制PE_FrameMenu 元素,否则QToolBar 会绘制CE_ToolBar

下面是一张 Java 风格工具栏的图片:

QToolBarSaparator 使用QStyleOption 作为其样式选项。如果它所在的工具栏是水平的,它就会设置State_Horizontal 标志。除此之外,它们使用initFrom() 。

QToolBar 的样式选项是QStyleOptionToolBar 。如果工具栏是水平的(即在工具栏的北部或南部区域),则设置的唯一状态标志(除常用标志外)是State_Horizontal 。样式选项的成员变量如下

成员内容
特征根据工具栏特性(ToolBarFeature)的值保存工具栏是否可移动,该值要么是可移动,要么是无。
线宽工具栏框架的宽度。
中间线宽度此变量目前未使用,始终为 0。
行位置工具栏线条在工具栏区域内的位置。
positionWithinLine工具栏在工具栏线中的位置。
工具栏区域工具栏所在的工具栏区域。

Qt 中的菜单通过QMenu 实现。QMenu 会保存一个操作列表,并将其绘制为菜单项。当QMenu 接收到绘制事件时,它会计算每个菜单项的大小,并通过CE_MenuItem 单独绘制它们。菜单项没有单独的标签(内容)元素,因此所有绘制都在CE_MenuItem 中完成。菜单还通过PE_FrameMenu 绘制菜单的边框。如果样式支持滚动,它还会绘制CE_MenuScroller 。如果菜单太大,无法容纳其边界矩形,则会绘制CE_MenuTearOff

在样式结构树中,我们还包括QMenu ,因为它也做与样式相关的工作。菜单项的边界矩形是根据菜单的大小提示以及菜单显示或调整大小时计算出来的。

CE_MenuScrollerCE_MenuTearOff 元素由QCommonStyle 处理,除非菜单太大而无法在屏幕上显示,否则不会显示。PE_FrameMenu 只用于弹出式菜单。

QMenu 如果样式支持 和 ,"...... "将根据其操作计算矩形。CE_MenuItem CE_MenuScroller

通常也会使用PE_IndicatorCheckBox (而不是PE_IndicatorMenuCheckMark )和PE_IndicatorRadioButton 来绘制可选中的菜单项;我们没有将它们包含在样式树中,因为这是可选项,而且因样式而异。

菜单项的样式选项是QStyleOptionMenuItem 。下表描述了其状态标志和其他成员。

状态设置 当
State_Selected鼠标位于操作上方,且操作不是分隔符。
State_Sunken鼠标按下菜单项时。
State_DownArrow如果菜单项是菜单滚动器,并且向下滚动菜单,则设置。
成员内容
检查类型CheckType 枚举的值,即 NotCheckable、Exclusive 或 NonExclusive。
已检查布尔值,如果菜单项被选中,则该布尔值为true
字体用于菜单项文本的QFont
图标菜单项的QIcon
最大图标宽度图标允许的最大宽度。
menuHasCheckableItems(菜单有可检查项目布尔值,如果菜单中至少有一个项目是可检查的,则该布尔值为true
菜单项类型菜单项的类型。这是MenuItemType 的值。
菜单矩形菜单项所在QMenu 的边界矩形。
标签宽度菜单项文本与快捷方式之间的距离。
文本菜单项的文本。

CE_MenuTearOffCE_MenuScroller 的样式选项设置也使用QStyleOptionMenuItem ;除了QStyleOption'sinitFrom() 的常用设置外,它们只设置menuRect 变量。

QMenuBar 使用该样式绘制每个菜单栏项目和菜单栏的空白区域。下拉菜单本身是 s(请参阅QMenu菜单)。菜单栏的样式元素树如下:

面板和空白区域绘制在菜单项之后。QMenuBar 发送给样式的QPainter 已剪切出项目的边界矩形(即剪切区域),因此不必担心在项目上方绘制。在计算菜单栏项目的边界矩形时,会使用QMenuBar 中的像素度量。

QStyleOptionMenuItem 用于菜单栏项目。 使用的成员如下表所示:QMenuBar

成员内容
菜单矩形项目所属的整个菜单栏的边界矩形。
文本项目的文本。
图标菜单项的图标(样式绘制该图标并不常见)。

QStyleOptionMenuItem 也用于绘制 。CE_EmptyMenuBarArea

QStyleOptionFrame 用于绘制面板框架。 设置为 。 目前始终设置为 0。lineWidth PM_MenuBarPanelWidth midLineWidth

项目视图页眉

它是绘制 Qt 项目视图页眉的样式。项目视图保留了各个部分的尺寸。还需注意的是,委托人可以使用该样式在项目周围绘制装饰和框架。QItemDelegate例如,Qt 的代表会绘制PE_FrameFocusRectPE_IndicatorItemViewItemCheck

下面是显示 Java 页眉边界矩形的QTableWidget

QHeaderView 使用CT_HeaderSectionPM_HeaderMarginPM_HeaderGripMargin 进行大小和命中测试计算。PM_HeaderMarkSize 目前未被 Qt XML 使用。QTableView 将左上角的按钮(即垂直和水平页眉相交的区域)绘制为CE_Header

页眉视图的样式选项是QStyleOptionHeader 。该视图一次绘制一个页眉部分,因此数据只针对正在绘制的部分。其内容如下

成员内容
图标标题的图标(针对正在绘制的部分)。
图标对齐方式标题中图标的对齐方式(Qt::Alignment )。
方向Qt::Orientation 值,决定页眉是位于视图上方的水平页眉还是位于左侧的垂直页眉。
位置QStyleOptionHeader::SectionPosition 值,表示页眉部分相对于其他部分的位置。
部分显示正在绘制的部分。
选定位置一个QStyleOptionHeader::SelectedPosition 值,表示所选部分相对于正在绘制的部分的位置。
排序指示器一个QStyleOptionHeader::SortIndicator 值,用于描述截面排序指示器的绘制方向。
文本当前绘制部分的文本。
文本对齐方式标题部分中文本的Qt::Alignment

树形分支指示符

树形视图中的分支指示符由PE_IndicatorBranch 样式绘制。在这里,我们将指示符视为描述树中节点关系的指示符。通用QStyleOption 发送给样式,用于绘制这些元素。各种分支类型由状态来描述。由于没有特定的样式选项,我们只需提供状态表:

状态设置 当
State_Sibling树中的节点有一个同级节点(即同一列中有另一个节点)。
State_Item该分支指标有一个项目。
State_Children该分支有子树(即可以在该分支上打开一个新的子树)。
State_Open分支指示器有一个已打开的子树。

树视图(和树部件)使用该样式绘制树的分支(节点)。

QStyleOption 由于 的样式根据分支的类型设置了状态标志,因此使用了"...... "样式。PE_IndicatorBranch

由于树枝指示器没有树形结构,因此我们只用 Java 样式呈现树的图像。图像中的每个状态都用特定颜色的矩形标记(即矩形不是边界矩形)。您必须注意的所有状态组合都在图像中有所体现。

工具箱

PM_SmallIconSize 用于尺寸提示。

QToolBox 是一个保存部件集合的容器。每个部件都有一个选项卡,每次显示一个部件。工具箱将其显示的部件(工具箱按钮和所选部件)放置在 中。工具箱的样式树如下所示:QVBoxLayout

我们展示的是 Plastique 风格工具箱的图片:

在 Plastique 风格和其他内置 Qt 风格中,所有元素都有相同的边界矩形。

工具箱的样式选项是QStyleOptionToolBox 。它包含工具箱内容的文本和图标。QToolBox 设置的唯一状态是State_Sunken ,当用户用鼠标按下选项卡时,就会设置该状态。QStyleOptionToolBox 的其余成员是

成员内容
图标工具箱选项卡上的图标。
文本工具箱选项卡上的文本。

尺寸手柄

尺寸手柄通过CT_SizeGrip 计算其尺寸提示。像素度量PM_SizeGripSize 目前未被 Qt XML 使用。QSizeGrip 的 Plastique 风格图像的元素树如下:

我们在QMainWindow 的右下角显示了尺寸控件。

尺寸手柄样式选项QStyleOptionSizeGrip 除了与QStyleOption 相同的成员外,还有一个成员:

成员内容
Qt::Corner 值,用于描述手柄位于窗口(或类似窗口)的哪个角落。

橡皮筋

QRubberBand 的样式树由两个节点组成。

我们展示了一张在QMdiArea 中使用橡皮筋移动 Java 风格窗口的图片:

橡皮筋的样式选项为QStyleOptionRubberBand 。其成员为

成员内容
不透明布尔值,如果橡皮筋必须以不透明的样式(即颜色)绘制,则该布尔值为true
形状QRubberBand::Shape 枚举值,用于保存橡皮筋的形状(矩形或直线)。

停靠部件

当 Dock Widget 布局其内容时,它会要求样式提供这些像素指标:PM_DockWidgetSeparatorExtent PM_DockWidgetTitleBarButtonMargin PM_DockWidgetFrameWidth PM_DockWidgetTitleMargin它还会使用SE_DockWidgetCloseButtonSE_DockWidgetFloatButton 计算浮动按钮和关闭按钮的边界矩形。

虚线表示发送者保留了箭头接收者的实例(即它不是要绘制的样式元素)。停靠窗口部件只有在脱离主窗口(即顶层窗口)时才会绘制PE_frameDockWidget 。如果它被停靠,则会绘制停靠窗口部件调整大小的指示符。我们以 plastique 风格显示停靠和浮动状态下的停靠窗口部件:

样式选项为QStyleOptionDockWidget

成员内容
可关闭表示 dock 窗口是否可以关闭的布尔值。
浮动用于判断停靠窗口是否可以浮动(即从其所在的主窗口分离)的布尔值。
可移动判断窗口是否可以移动(即可以移动到其他 dock widget 区域)的布尔值。
标题dock 窗口的标题文本。

对于按钮,QStyleOptionButton (内容说明请参阅工具按钮)。调整停靠窗口部件大小的句柄是QStyleOption

© 2025 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.