样式和样式感知小部件

样式(继承自 QStyle 的类)代表小部件绘制,封装了 GUI 的外观和感觉。QStyle 类是一个封装了 GUI 外观和感觉的抽象基类。Qt 内置小部件使用它来执行几乎所有绘图操作,确保其外观与等效的本地小部件完全相同。

Qt 包装了一系列内置样式。某些样式仅在特定平台上可用。自定义样式可以作为插件提供,也可以通过使用 QStyleFactory::create() 创建特定样式类的实例,并使用 QApplication::setStyle() 设置它。

自定义样式

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

实现自定义样式

QCommonStyle 为完整的自定义样式实现提供了一个方便的基础。该方法与 QProxyStyle 相同,但应继承 QCommonStyle 并重写适当的虚拟方法。实现完整的自定义样式较为复杂,因此我们提供了本概述。我们将逐步说明如何为单个 Qt 小部件进行样式设计。我们将检查 QStyle 的虚拟函数、成员变量和枚举。

本文档中不涉及单个小部件样式设计部分旨在按顺序阅读,因为后续部分通常取决于早期部分。小部件的描述可用于参考样式实现。然而,在某些情况下,可能需要查阅 Qt 源代码。在阅读本文档后,样式处理过程的流程应变得清晰,这有助于您定位相关代码。

要开发样式感知的小部件(即,符合其绘制样式的样式),需要使用当前样式绘制小部件。本文档演示了小部件如何进行自绘,以及样式为它们提供了哪些可能性。

小部件样式设计类

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

QColor

基于 RGB、HSV 或 CMYK 值的颜色

QColorSpace

颜色空间抽象

QColorTransform

颜色空间之间的转换

QCommonStyle

封装了 GUI 的通用外观和感觉

QCursor

具有任意形状的光标

QFont

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

QFontDatabase

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

QFontInfo

有关字体的一般信息

QGraphicsAnchor

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

QGraphicsAnchorLayout

在图形视图中可以锚点一起的布局

QPalette

包含每个小部件状态的色彩组

QStyle

封装 GUI 看法和感觉的抽象基类

QStyleFactory

创建 QStyle 对象

QStyleHintReturn

返回更多基本数据类型的风格提示

QStyleHintReturnMask

返回 QRegion 的风格提示

QStyleHintReturnVariant

返回 QVariant 的风格提示

QStyleOption

存储 QStyle 函数使用的参数

QStylePainter

用于在窗口内绘制 QStyle 元素的便利类

QStyle 实现方法

QStyle API 包含绘制小部件的函数、执行常见和困难任务的静态辅助函数(例如,计算滑动条的适当位置)以及绘图时所需的必要计算函数(例如,用于小部件计算大小提示的函数)。样式还帮助某些小部件布局其内容。此外,它创建一个包含用于绘制的 QBrushQPalette

QStyle 绘制图形元素;元素是窗口小部件或小部件的一部分,如推钮斜面、窗口边框或滚动条。大多数绘图函数现在接受四个参数

  • 指定要绘制的图形元素的枚举值
  • 指定如何和在何处渲染该元素的 QStyleOption
  • 用于绘制元素的 QPainter
  • 绘制的小部件(可选)

当小部件请求样式绘制一个元素时,它向样式提供了一个 QStyleOption,这是一个包含绘制所需信息的类。由于 QStyleOption,可以无需将任何代码链接到小部件使 QStyle 绘制小部件。这使得可以在任何绘图设备上使用 QStyle 的绘图函数,即您可以在任何小部件上绘制下拉列表,而不仅仅是 QComboBox

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

在本节中,我们将查看风格元素、风格选项和 QStyle 的功能。最后,我们描述如何使用调色板。

项目视图中的项是通过 Qt 中的 代理人 绘制的。项目视图标题仍然由样式绘制。Qt 的默认代理,QStyledItemDelegate,部分通过当前样式绘制其项;它绘制复选框指示符并计算项组成的元素的外接矩形。在本文档中,我们仅描述如何实现 QStyle 子类。如果您想添加对 QStyledItemDelegate 支持的除其他类型之外的其他数据类型支持,则需要实现一个自定义代理。请注意,代理必须用于每个单独的小部件进行编程设置(即,不能将默认代理作为插件提供)。

风格元素

样式元素是GUI的一个图形部分。小部件由一系列样式元素组成。例如,当样式元素收到绘制按钮(如QPushButton)请求时,它会绘制标签(文本和图标)、按钮边缘和焦点框架。按钮边缘由围绕边缘的框架和另外两个元素组成,我们将在稍后讨论。以下是一个压按钮元素的概念性说明。当我们在单独的部件上进行操作时,我们将看到QPushButton的实际树结构。

小部件不一定只通过请求样式绘制一个元素。小部件可以多次调用样式来绘制不同的元素。一个例子是QTabWidget,它分别绘制其标签和框架。

有三种元素类型:基本元素、控件元素和复杂控件元素。这些元素由ComplexControlControlElementPrimitiveElement枚举定义。每个元素枚举值都有一个前缀以标识它们的类型:CC_表示复杂元素,CE_表示控件元素,PE_表示基本元素。在接下来的三个部分中,我们将了解什么定义了不同的元素,并查看使用它们的小部件的例子。

QStyle类的描述包含了一个这些元素的列表以及它们在样式化部件中的作用。当我们在单独的部件上操作样式时,我们将看到如何使用它们。

基本元素

基本元素是通用且多个小部件经常使用的GUI元素。例如,框架、按钮边缘和用于微调框、滚动条和组合框的箭头。基本元素不能独立存在:它们总是更大结构的一部分。它们不参与用户交互,但在GUI中是被动装饰。

控件元素

控件元素执行动作或将信息显示给用户。控件元素的例子包括按钮、复选框以及表格和树视图中表头部分。控件元素不一定是非完整的小部件,如按钮,但它也可以是部件的某些部分,如标签栏标签和滚动条滑块。与基本元素不同,控件不是被动的,而是在用户交互中起功能作用。由多个元素组成的控件通常使用样式来计算元素的边界矩形。可用子元素由SubElement枚举定义。此枚举只用于计算边界矩形;子元素不是如图形、控件和复杂元素那样被绘制的。

复杂控件元素

复杂控件元素包含子控件。复杂控件的行为根据用户用鼠标点击的位置以及按下的哪些键盘键而不同。这取决于鼠标是否在某个子控件(如果有的话)上方或按下。复杂数控的例子包括滚动条和组合框。在滚动条中,您可以使用鼠标移动滑块并按line up和line down按钮。可用子控件由SubControl枚举定义。

除了绘制之外,样式还需要向小部件提供有关鼠标点击在哪个子控件上(如果有的话)的信息。例如,QScrollBar需要知道用户是否按下了滑块、滑块凹槽或按钮之一。

注意,子控件与上一节中描述的控制元素不同。您不能使用样式来绘制子控件;样式只会计算应该绘制子控件的边框矩形。然而,复杂元素通常使用控件和基本元素来绘制其子控件,这是一种在 Qt 内置样式和 Java 样式中都常用到的方法。例如,Java 样式使用 PE_IndicatorCheckBox 来绘制分组框中的复选框(它是一个 CC_GroupBox 的子控件)。一些子控件有等效的控制元素,例如滚动条滑块(SC_SCrollBarSliderCE_ScrollBarSlider)。

其他 QStyle 任务

如前所述,样式元素和小部件使用样式来计算子元素和子控件的边框矩形。像素度量,即屏幕像素中的样式依赖于大小的尺寸,也用于绘图时的测量。可用矩形和像素度量由 QStyle 中的三个枚举表示:SubElementSubControlPixelMetric。枚举值的标识很容易,它们以 SE_、SC_ 和 PM_ 开头。

样式还包含一组样式提示,这由 StyleHint 枚举中的值表示。所有小部件在不同样式中的功能和外观都不相同。例如,当菜单中的菜单项无法适配屏幕的单列时,一些样式支持滚动,而其他样式则绘制多列以适应所有项。

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

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

样式选项

QStyleOption 的子类包含对风格个别元素所有必要的信息。样式选项通常被实例化 - 通常在堆栈上 -并由 QStyle 函数的调用者填充。根据绘制的具体内容,样式将期望不同样式选项类。例如,QStyle::PE_FrameFocusRect 元素期望一个 QStyleOptionFocusRect 参数,并且可以创建自定义子类,以便特定样式可以使用。为了性能原因,样式选项保留了公共变量。

小部件可以处于多种不同的状态,这些状态由 State 枚举定义。某些状态标志的含义取决于小部件,但其他标志对所有小部件都是通用的,如 State_Disabled。是 QStyleOption 通过 QStyleOption::initFrom() 设置公共状态的;其余的状态由各个小部件设置。

最显著的是,风格选项包含要绘制的小部件的调色板和外接矩形。大多数小部件都有专门的风格选项。例如,QPushButtonQCheckBox使用QStyleOptionButton作为其风格选项,其中包含文本、图标及其图标的大小。所有选项的详细内容将在我们遍历个别小部件时描述。

在重新实现接受QStyleOption参数的QStyle函数时,您通常需要将QStyleOption强制转换为子类(例如,QStyleOptionFocusRect)。出于安全考虑,您可以使用qstyleoption_cast()来确保指针类型正确。如果对象不是正确的类型,qstyleoption_cast()返回nullptr。例如

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

以下代码片段说明了如何使用QStyle从自定义小部件的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的指针,通常是绘制元素的控件。

并非所有控件都发送指向自身的指针。如果发送到函数的风格选项不包含所需的信息,您应该检查控件实现以查看它是否发送指向自身的指针。

QStyle类还提供了用于绘制元素时的辅助函数。函数drawItemText()在指定矩形内绘制文本,参数为一个QPalette。函数drawItemPixmap()帮助在指定的外接矩形内对齐Pixmap。

其他QStyle函数为绘制函数执行各种计算。如果小部件自身绘制多个风格元素,它们也将使用这些函数来计算大小提示和外接矩形。与绘制元素的函数一样,辅助函数通常接受相同的参数。

  • 函数subElementRect()接受一个SubElement枚举值,并计算子元素的边界矩形。风格使用此函数来确定要绘制元素的不同部分的位置。这主要是为了重用;如果您创建一个新的风格,您可以使用子元素与超类相同的同一位置。
  • 函数subControlRect()用于计算复杂控件中子控件的边界矩形。当您实现一个新的风格时,重新实现subControlRect()并计算与超类不同的矩形。
  • 函数 pixelMetric() 返回像素度量,这是一种基于样式并在屏幕像素中给出的大小。它接受一个 PixelMetric 枚举值,并返回正确的度量值。请注意,像素度量不一定是静态测量,但可以使用,例如,样式选项进行计算。
  • 函数 hitTestComplexControl() 返回鼠标指针在复杂控件上所在的子控件。通常,这只是一个使用 subControlRect() 获取子控件的边界矩形,然后查看哪个矩形包含光标位置的问题。

QStyle 还具有 polish() 和 unpolish() 函数。在显示所有小部件之前,所有小部件都会发送到 polish() 函数,在它们被隐藏时发送到 unpolish()。您可以使用这些函数来设置小部件上的属性或执行您所需样式的工作。例如,如果您需要知道鼠标是否悬停在控件上,您需要设置 WA_Hover 控件属性。然后,将在控件样式选项中设置 State_MouseOver 状态标志。

QStyle 有一些静态辅助函数执行一些常识性和困难的任务。它们可以从滑块的值计算滑块手的位缐,并考虑反向布局对矩形进行转换和绘制文本;有关详细信息,请参阅 QStyle 类文档。

重新实现 QStyle 虚拟函数时的常规方法是:对于与超类不同的元素做工作;对于所有其他元素,您只需使用超类实现即可。

调色板

每种样式都提供了一种颜色,即用于绘制小部件的 QBrush 调色板。对于不同的控件状态(QPalette::ColorGroup)有一套颜色:活动(窗口中具有键盘焦点的控件),非活动(用于其他窗口的控件),以及禁用(已禁用的控件)。可以查询 State_ActiveState_Enabled 状态标志来找到状态。每个集合包含由 QPalette::ColorRole 枚举给出的某些颜色角色。角色描述了应在何种情况下使用颜色(例如,绘制控件背景、文本或按钮)。

颜色角色的使用取决于样式。例如,如果样式使用渐变,则可以使用调色板颜色,并通过使用 QColor::darker() 和 QColor::lighter() 使其变暗或变亮来创建渐变。通常,如果您需要一个调色板未提供的画笔,您应尝试从中派生出它。

提供调色板的 QPalette 存储不同控件状态和颜色角色的颜色。样式的调色板由 standardPalette() 返回。在将新样式设置为应用程序(QApplication::setStyle)或小部件(QWidget::setStyle)时,标准调色板不会自动安装,因此您必须自己使用(《a href="qapplication.html#setPalette" translate="no">QApplication::setPalette())或(QWidget::setPalette())来设置调色板。

不建议硬编码颜色,因为应用程序和单个小部件可以设置自己的调色板,并且也可以使用自己样式的调色板进行绘制。请注意,Qt的所有小部件都没有设置自己的调色板。Java样式硬编码了一些颜色,但这只是作者的决定;不建议这样做。当然,并不打算让样式与任何调色板都搭配得很好。

实现问题

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

在实现样式时,必须查看小部件和基本类及其祖先的代码。这是因为小部件使用样式的方式不同,因为不同的样式的虚拟函数实现可能会影响绘制的状态(例如,通过没有恢复的状态改变QPainter并使用适当的像素度量绘制一些元素)。

建议样式不要使用QStyle::sizeFromContents()函数改变小部件的提议大小,而是让QCommonStyle的实现来处理它。如果需要做出更改,应尽量保持更改较小;如果小部件的布局在各个样式中看起来明显不同,可能会使应用程序开发变得困难。

Java 样式

我们已经实现了一个类似Java默认外观和感受(以前称为Metal)的样式。我们之所以这样做,是因为它相对简单实现,我们希望为这个概述文档创建一个样式。为了保持简单且不过于庞大,我们对样式做了一些简化。然而,Qt能够完美地复制这个样式。但是,目前没有具体计划将此样式作为Qt的一部分来实现。

在本节中,我们将讨论一些实现问题。最后,我们将看到一个Java小部件样式的完整示例。在整个文档中,我们将继续使用Java样式进行示例和部件图像。实现本身相对复杂,并不希望您阅读它。

设计和实现

设计样式的第一步是选择基本类。我们选择子类QCommonStyle。这个类实现了我们需要的功能,除了实际的绘制。

样式在一个类中实现。我们这样做是因为我们发现将所有代码放在一个文件中很方便。此外,它对于优化也有优势,因为我们可以实例化更少的对象。我们还通过在函数中使用switch来识别要绘制的元素来维持函数的个数最少。这导致函数很大,但由于我们在switch中分割每个元素的代码,代码的阅读仍然应该是容易的。

限制和与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()使用对所有具有通用信息的控件的信息来设置选项。我们很快会看到这一点。

QStyleOptiondown变量为true当用户按下框时;这适用于复选框,不管框是否被勾选。当我们有一个三态复选框并且它部分勾选时,我们设置State_NoChange状态。如果框被勾选,则为State_On状态;如果未勾选,则为State_Off状态。如果鼠标悬停在复选框上并且控件有Qt::WA_Hover属性设置为真 - 你在QStyle::polish()中设置它 - 则设置State_MouseOver。此外,样式选项还包含按钮的文字、图标和图标大小。

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中启用键盘导航,以便包括State_HasEditFocus;它不是默认包含的。

除了设置状态标志,QStyleOption 还包含有关小部件的其他信息:direction 是布局方向,rect 是小部件的边界矩形(要绘制区域的框),palette 是用于绘制小部件的 QPalette,而 fontMetrics 是小部件用的字体的指标。

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

A Java style checkbox

上面的复选框在其样式选项中将有以下状态标志

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

QCheckBoxQWidget::paintEvent() 中使用样式选项 optQStylePainter p 绘制自己。 QStylePainter 类是一个方便的类,用于绘制样式元素。它特别地,它封装了用于绘制的方法。最值得注意的是,它封装了用于绘画的 QStyle 中的方法。复选框如以下所示绘制

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

QCommonStyle 处理 CE_CheckBox 元素。复选框有两个子元素: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 的两个子元素的边界矩形,然后绘制它们。如果复选框具有焦点,焦点框也会绘制。

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()) {
        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() 绘制文本,考虑对齐、布局方向和快捷键。它还使用调色板将文本以正确颜色绘制。

标签的绘制通常相当复杂。幸运的是,这通常可以由基类处理。Java 样式实现它自己的按钮标签,因为当按钮具有图标时,Java也将按钮内容居中。如果您需要重新实现标签绘制的示例,可以检查该实现。

我们现在将查看 Java 在 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;

我们首先保存绘制器的状态。这不总是必要的,但在这种情况下,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);
    }
}

我们已经看到了从小部件接收到绘图请求到样式完成绘制的这段时间内复选框是如何得到样式的。要详细了解每个小部件是如何绘制的,你需要在代码上一步一步地进行,就像我们在这里做的那样。然而,通常,了解小部件绘制哪些样式元素就足够了。小部件建立一个样式选项,并调用样式一次或多次来绘制它所包含的样式元素。通常,了解小部件可能处于的状态以及样式选项的其他内容(即,我们在下一节中列出的内容)也就足够了。

小部件概述

在本节中,我们将探讨大多数Qt小部件的样式。希望这可以在你开发自己的样式和小部件时节省一些时间和精力。这里的信息不会在这里以外的地方出现(即,通过检查源代码或与样式相关的类的类描述)。

我们主要使用Java样式的Widget作为例子。Java样式不会在元素树中绘制每个元素。这是因为它们在Java样式中对该小部件是不可见的。我们仍然确保所有元素都按照Java样式以符合的方式进行实现,因为自定义小部件可能需要它们(这并不意味着可以将实现留给QCommonStyle)。

以下是对每个小部件的说明

  • 一个包含其样式选项成员(变量等)的表格。
  • 可以设置在Widget上的和何时设置状态的状态标志(QStyle::StateFlag)表格。
  • 其元素树(见章节The Style Elements)。
  • Widget的图像,其中元素被勾画出来。

元素树包含原始元素、控件和复杂样式元素。通过从上到下遍历元素树,你可以得到元素应该绘制的顺序。在节点中,我们写出了子元素矩形、子控件和绘制节点元素时应考虑的像素度量。

我们在样式方面的方法集中在Widget的绘制。在绘制过程中使用的子元素矩形、子控件和像素度量的计算只作为元素树中的内容列出。请注意,有些矩形和像素度量仅供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如果小部件具有编辑焦点则设置

小部件的另外一些通用成员

成员内容
rect绘制元素的包围矩形。这被设置为小部件的包围矩形(QWidget::rect())。
direction布局方向;Qt::LayoutDirection枚举中的一个值。
palette在绘制元素时要使用的QPalette。这被设置为小部件的配乐(QWidget::palette())。
fontMetrics在控件上绘制文本时要使用的QFontMetrics

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

如前所述,样式计算小部件内容的尺寸,小部件根据这些尺寸提示计算其大小提示。此外,复合控件还使用样式来测试鼠标悬停的子控件。

小部件参考

无需进一步延迟,我们展示了小部件的浏览。每个小部件都有自己的子部分。

按钮

以下显示按钮的样式结构。通过自顶向下的遍历树,您将得到元素应该绘制的顺序。

The style structure for push buttons

就元素边界而言,按钮的布局在不同的样式之间各不相同。这使得展示这个概念图像很困难。此外,元素甚至可能有相同的边界;例如,PE_PushButtonBevelQCommonStyle中用于绘制它包含的元素:PE_FrameDefaultButtonPE_FrameButtonBevelPE_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中,QTabBar使用风格绘制其标签页。标签页存在于QTabWidget中,它包含一个QTabBar,或者作为一个独立的栏。如果栏不是标签组件的一部分,它将绘制自己的基。

QTabBar排列了标签页,因此风格不控制标签的位置。但是,在排列其标签页时,栏会向风格请求PM_TabBarTabHSpacePM_TabBarTabVSpace,这是标签栏标签(图标和文本)的最小尺寸之外的额外宽度和高度。风格还可以在标签栏排列之前进一步影响标签尺寸,因为标签栏会请求CT_TabBarTab。如果标签组件是组件的一部分,由标签组件决定的栏的边界矩形也考虑了CT_TabBarTab

标签栏负责绘制所有标签未能适应时出现在标签栏上的按钮。它们的放置不由风格控制,但按钮是QToolButton,因此由风格绘制。

以下是QTabWidgetQTabBar的风格结构。

虚线表示QTabWidget 包含标签栏,但不绘制标签栏本身。当不是标签控件的一部分时,QTabBar 只绘制其基准线,并保持两个滚动工具按钮,当所有标签不能适应时可以滚动;有关工具按钮的元素树,请参阅工具按钮。另外请注意,因为按钮是标签栏的子项,所以它们在标签栏之后绘制。标签的边界矩形与基准重叠了PM_TabBarBaseOverlap

这是一个Java样式的标签控件

在Java样式中,标签栏形状和标签的边界矩形与CE_TabBarTab相同。注意,标签与标签控件框架重叠。当绘制时,标签栏的底部是标签和框架重叠的区域。

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

由于Java样式标签不重叠,我们还展示了一个普通样式的标签控件图像。请注意,如果您希望标签水平重叠,则在绘制标签时在CE_TabBarTabShape中设置它;标签的边界矩形不会被标签栏改变。在北标签栏形状中,标签从左到右绘制,在东标签栏形状中,从上到下绘制等。最后绘制选中标签,以便容易将其绘制在其他标签之上(如果它更大)。

下表列出了标签栏可以对其标签设置的几种状态

状态设置状态的时间
State_Sunken使用鼠标按下标签。
状态_selected如果它是当前标签。
State_HasFocus标签栏有焦点且选中标签。

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

以下列出了QStyleOptionTab的成员

成员内容
cornerWidgetsCornerWidget枚举的标志,指示标签栏是否有角控件。
图标标签的QIcon
图标大小图标的QSize
position一个TabPosition枚举值,指示标签在栏上的位置相对于其他标签。
row包含标签所在的行。
selectedPositionSelectedPosition枚举的值,指示选中标签是否紧挨着或就是标签。
shape一个QTabBar::Shape枚举值,指示标签是否有圆角或三角形角和标签的朝向。
文本标签文本。

对于标签控件框架,使用QStyleOptionTabWidgetFrame作为样式选项。我们在此列出其成员。除了常见的标志外,未设置状态。

成员content
leftCornerWidgetSize如果有左角控件,其QSize
rightCornerWidgetSize如果有右角控件,其QSize
lineWidth包含用于绘制面板的线的宽度。
midLineWith此值目前始终为0。
shape标签栏上标签的形状。
tabBarSize标签栏的QSize

滚动条

以下是滚动条的样式结构

QScrollBar 简单地创建其样式选项,然后绘制 CC_ScrollBar。一些样式使用 PE_PanelButtonBevel 绘制添加页面和减小页面的背景,并且也使用指示箭头在下一行和上一行的指示器中绘制箭头;我们没有将它们包括在树中,因为它们的使用取决于个别样式。样式的 PM_MaximumDragDistance 是鼠标可以从滚动条边界移动的最大像素数,并且仍可移动滑块。

以下是 Java 样式的滚动条的图片

您可能会注意到,滚动条与 Java 的稍有不同,因为它有两个行上指示器。我们这样做是为了表明您可以为一个单一的子控件有两次分离的边界矩形。滚动条是一个完全由 Java 样式实现的小部件的例子 - QCommonStyle 没有涉及到绘制。

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

状态设置状态的时间
State_Horizontal滚动条是水平方向的。

QScrollBar 的样式选项是 QStyleOptionSlider。其成员列表在以下表中。此选项由所有 QAbstractSlider 使用;我们只在此处描述与滚动条相关的成员。

成员内容
maximum滚动条的最大值。
minimum滚动条的最小值。
notchTarget凹口之间的像素数。
orientation指定滚动条是垂直还是水平方向的 Qt::Orientation 枚举值。
pageStep在页面步骤中增加或减少滑块值的数量(相对于滑块的大小及其值范围)。
singleStep单个(或行)步骤中增加或减少滑块值的数量。
sliderValue滑块值。
sliderPosition滑块手的位移。如果滚动条是 QAbstractSlider::tracking,则与 sliderValue 相同。如果不是,则鼠标释放手柄之前滚动条不会更新其值。
upsideDown保存滚动条值增加的方向。对于所有抽象滑块,使用此方向代替 QStyleOption::direction

滑块

计算滑块的尺寸提示时,将从样式查询 PM_SliderThicknessPM_SliderLength。与滚动条一样,QSlider 仅允许用户在鼠标位于 PM_MaximumDragDistance 以内时移动手柄。在绘制自身时,它创建样式选项,并用 CC_Slider 调用 drawComplexControl()

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

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

成员内容
maximum滑块的最大值。
minimum滑块的最小值。
notchTarget这是每个凹口之间的像素数。
orientation指示滑块是垂直还是水平方向的 Qt::Orientation 枚举值。
pageStep在页面步骤中增加或减少滑块值的数量。
singleStep单个(或行)步骤中增加或减少滑块值的数量。
sliderValue滑块值。
sliderPosition滑块的位置由滑块值给出。如果滑块处于跟踪状态,则这将等于 sliderValue;如果不处于跟踪状态,则滑块的值不会改变,直到鼠标释放滑块柄。
upsideDown此成员用于所有抽象滑块,而不是QStyleOption::direction

请注意,滑块不使用方向进行反向布局;它使用 upsideDown

微调框

QSpinBox进行绘制时,它创建一个QStyleOptionSpinBox并要求样式绘制CC_SpinBox。编辑字段是微调框的子线编辑。字段的尺寸由样式使用SC_SpinBoxEditField计算。

以下是微调框的样式树。样式不一定要使用按钮面板原语绘制指示器背景。您可以看到以下树下方显示了Java样式中QSpinBox的子元素图像。

QStyleOptionSpinBox是微调框的样式选项。它可以设置以下滑块状态

状态设置状态的时间
State_Sunken如果用鼠标按下了子控件CC_SpinUpCC_SpinDown,则设置为。

微调框样式选项中的其余成员是

属性功能
框架如果微调框要绘制框架,则为true
按钮符号决定上下按钮符号的ButtonSymbols枚举值。
步进启用步进启用的枚举值,指示哪个微调框按钮被按下。

标题栏

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

该栏在CC_TitleBar中没有使用任何子元素的情况下绘制。各个样式如何绘制它们的按钮由它们自己决定,但样式应该提供按钮的标准位图。

在Java样式中,一个标题栏上的图像显示了Java样式支持的子元素的边界矩形(所有这些都用标准位图绘制)。通常使用PE_PanelButtonTool绘制按钮背景,但这不是强制性的。

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

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

组合框

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

用户点击组合框时弹出的列表是由一个代理绘制的,这部分我们在此概述中不做介绍。然而,您可以使用SC_ComboBoxListBoxPopup这个子元素来控制列表的大小和位置,并且样式决定了可编辑框的编辑字段位置,即通过SC_ComboBoxEditField;该字段本身是一个组合框的子元素QLineEdit

我们在Java风格的组合框上展示了一张图像,其中我们已标出其子元素和子元素矩形。

Java组合框不使用焦点矩形;当它获取焦点时,它改变其背景颜色。《SC_ComboBoxEdit》字段既被QComboBox用来计算编辑字段的大小,也被用来计算组合框标签的大小。

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

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

其他样式选项成员包括:

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

分组框

在计算大小提示时,QGroupBox从样式中获取三个像素度量:PM_IndicatorWidthPM_CheckBoxLabelSpacingPM_IndicatorHeightQGroupBox有以下的样式元素树

Qt不对复选框的绘制做限制;Java样式用 CE_IndicatorCheckBox 绘制复选框。有关完整树结构的详细信息,请参阅检查框和单选按钮

我们还提供了一张带有子控件和子控件矩形的控件图像。

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

状态设置条件
State_On复选框被勾选。
State_Sunken复选框被按下。
State_Off复选框未勾选(或者没有复选框)。

QStyleOptionGroupBox的其余成员包括:

成员内容
特性描述分组框框架的QStyleOptionFrame::FrameFeatures枚举的标志。
lineWidth用来绘制面板的线宽。这始终是1。
文本分组框的文本。
textAlignment分组框标题的对齐方式。
textColor文本的 QColor

分隔器

由于分隔器的结构很简单,不包含任何子元素,我们没有包括任何分隔器的图像。CE_Splitter不使用其他元素或度量。

对于其样式选项,分隔器使用基本类 QStyleOption。可以在其中设置以下状态标志:

状态设置条件
State_Horizontal设置如果它是水平分隔器。

QSplitter不使用initFrom()来设置其选项;它自己设置State_MouseOverState_Disabled标志。

进度条

QEProgressDialog元素被QProgressBar使用,它是此小部件唯一使用的元素。我们从一个样式结构开始

这是一个常见的风格的进度条(Java样式的边界矩形相等)

QProgressBar的样式选项是QStyleOptionProgressBar。该栏不设置任何状态标志,但选项的其他成员是

成员内容
minimum栏的最小值。
maximum栏的最大值。
进度栏的当前值。
textAlignment在标签中的文本对齐方式。
textVisible是否绘制标签。
文本标签文本。
orientation进度条可以是垂直或水平。
invertedAppearance进度是反转的(即在水平栏中从右向左)。
bottomToTop布尔值,如果设置为true,则将垂直进度条的标签旋转90度。

工具按钮

工具按钮可以独立存在或者作为工具栏的一部分。无论哪种方式绘制,它们的绘制方式都是相同的。只有QToolButton绘制了单独的样式元素:CC_ToolButton

以下是小部件样式结构的树

请注意,PE_FrameButtonToolPE_IndicatorArrowDown包含在树中,因为Java样式绘制它们,但如果您愿意,可以安全地省略。结构可能也不同。《a href="qcommonstyle.html" translate="no">QCommonStyle,例如,在CE_ToolButton中绘制PE_IndicatorButtonDropDownPE_IndicatorArrowDown

我们还有一个工具按钮的图像,我们在其中描绘了子元素边界矩形和子控件。

以下是工具按钮的状态表

状态设置条件
State_AutoRise工具按钮具有自动升起属性设置。
_State_Raised按钮未被按下(即,鼠标检查或点击)。
State_Sunken按钮已按下。
State_On按钮是可复选的和被勾选的。

QStyleOptionToolButton 还包含以下成员

成员内容
arrowType一个Qt::ArrowType枚举值,它包含按钮箭头的方向(如果用箭头代替图标)。
特性描述按钮是否有箭头、菜单以及/或是否具有弹出延迟的Qt::ToolButtonStyle枚举标志。
Font按钮标签的QFont
图标工具按钮的QIcon
图标大小按钮图标的尺寸大小。
位置按钮位置,由QWidget::pos()指定。
文本按钮文本。
toolButtonStyle一个Qt::ToolButtonStyle枚举值,它决定按钮是否显示图标、文本或两者都显示。

工具栏

工具栏是主窗口框架的一部分,并且在构建其样式选项时与它们所属的QMainWindow合作。主窗口有4个区域可以放置工具栏,它们位于窗口的四个边缘(即,北、南、东和西)。在每个区域内可以有多个工具栏行;一行是相邻放置的、具有相同布局(垂直或水平)的工具栏。

Qt中的Toolbars包含三个元素:CE_ToolBarPE_IndicatorToolBarHandlePE_IndicatorToolBarSeparator。是QMainWindowLayout计算工具栏(即,工具栏及其内容的定位和大小)。主窗口在计算工具栏大小时也使用了工具栏中项的sizeHint()

以下是QToolBar的元素树。

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

以下是Java样式的工具栏的图像。

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

QToolBar的样式选项是QStyleOptionToolBar。除了常见的标志外,唯一设置的标志是当条带是水平时(即在北或南工具栏区域)的State_Horizontal。样式选项的成员变量如下:

成员内容
特性持有是否在ToolBarFeature的值中将条带可移动,该值可以是Movable或None。
lineWidth工具栏框架的宽度。
midLineWidth该变量目前未使用,始终为0。
positionOfLine工具栏线条在其所属的工具栏区域内的位置。
positionWithinLine工具栏在其线条内的位置。
toolBarArea工具栏所居住的工具栏区域。

Qt中的菜单在QMenu中实现。该QMenu保留了一个动作列表,将其绘制为菜单项。当QMenu收到绘制事件时,它会计算每个菜单项的大小,并使用CE_MenuItem单独绘制它们。菜单项没有用于其标签(内容)的单独元素,因此所有绘制都在CE_MenuItem中进行。菜单还使用PE_FrameMenu绘制菜单的框架。如果样式支持滚动,它还会绘制CE_MenuScroller。如果菜单太大而无法适合其边界框,则会绘制CE_MenuTearOff

我们还在样式结构树中也包括QMenu,因为它也执行与样式相关的操作。为菜单的大小提示计算菜单项的边界矩形,并且当菜单显示或调整大小时。

CE_MenuScrollerCE_MenuTearOff元素由QCommonStyle处理,并且除非菜单太大而无法适合屏幕,否则不显示。PE_FrameMenu只为弹出菜单绘制。

QMenu根据其动作计算矩形,并根据样式支持调用CE_MenuItemCE_MenuScroller

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

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

状态设置条件
状态_selected鼠标位于动作上方,且动作不是一个分隔符。
State_Sunken鼠标在菜单项上按下。
State_DownArrow如果菜单项是一个菜单滚动条并且它滚动菜单向下,则设置。
成员内容
checkTypeCheckType枚举的值,它可以是NotCheckable、Exclusive或NonExclusive。
checked布尔值,如果菜单项被选中则为true。
Font用于菜单项文本的QFont
图标菜单项的QIcon
maxIconWidth图标允许的最大宽度。
menuHasCheckableItems一个布尔值,如果菜单中至少有一个可检查的项,则为true
menuItemType菜单项的类型。它是MenuItemType的值。
menuRect菜单项所在的QMenu的边界矩形。
tabWidth这是菜单项文本和快捷键之间的距离。
文本菜单项的文本。

对于CE_MenuTearOffCE_MenuScroller的样式设置也使用了QStyleOptionMenuItem;它们除了使用QStyleOptioninitFrom()的常见设置外,还设置了menuRect变量。

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

后绘制面板和空白区域。QMenuBar发送给样式的QPainter剪除了项的边界矩形(即剪裁区域),因此您不必担心覆盖项。在计算菜单栏项的边界矩形时使用QMenuBar中的像素度。

QStyleOptionMenuItem用于菜单栏项。以下表格中描述了QMenuBar使用的成员

成员内容
menuRect项所属的整个菜单栏的边界矩形。
文本项的文本。
图标菜单项的图标(样式通常不会绘制此图标)。

QStyleOptionMenuItem也用于绘制CE_EmptyMenuBarArea

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

项目视图标题

它是样式绘制Qt的项目视图标题。项目视图保持各个部分的尺寸。请注意,代理可能使用样式在项目周围绘制装饰和框架。例如,QItemDelegate绘制PE_FrameFocusRectPE_IndicatorItemViewItemCheck

QTableWidget显示了Java标题的边界矩形状。

QHeaderView使用CT_HeaderSectionPM_HeaderMarginPM_HeaderGripMargin进行大小和点击测试的计算。《代码 translate="no">PM_HeaderMarkSize目前在Qt中未使用。QTableView将按钮绘制在左上角(即垂直和水平标题相交的区域)作为CE_Header

头部视图的样式选项是QStyleOptionHeader。视图一次绘制一个标题部分,因此数据是为绘制的部分。其内容如下

成员内容
图标头部(正在绘制的部分)的图标。
iconAlignment图标在头部中的对齐方式(Qt::Alignment)。
orientation一个决定表头是位于视图上方的水平表头还是左侧的垂直表头的 Qt::Orientation 值。
position一个提供表头部分相对于其他部分位置的 QStyleOptionHeader::SectionPosition 值。
部分包含正在绘制的部分。
selectedPosition一个提供所选择部分相对于正在绘制部分位置的 QStyleOptionHeader::SelectedPosition 值。
排序指示器一个描述部分排序指示器应绘制方向 的 QStyleOptionHeader::SortIndicator 值。
文本当前正在绘制的部分文本。
textAlignment表头部分中文本的对齐方式。

树分支指示器

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

状态设置条件
状态_Sibling树中的节点有一个兄弟(即,同一列中还有另一个节点)。
状态_Item这个分支指示器有一个项。
状态_Children分支有子节点(即在分支处可以打开一个新的子树)。
状态_Open分支指示器有一个打开的子树。

TreeView(和树小部件)使用样式绘制树的分支(节点)。

QStyleOption 用于 PE_IndicatorBranch 的样式,它设置状态标志以根据分支的类型。

由于没有分支指示器的树结构,我们只提供了一个Java样式的树图。每个状态在图中用特定颜色的矩形标记(即,矩形不是边框矩形)。图中代表了你必须注意的所有状态组合。

工具框

PM_SmallIconSize 用于大小提示。

QToolBox 是一个容器,它保存了一个小部件集合。它为每个小部件有一个标签,一次显示一个小部件。工具框将其显示的组件(工具框按钮和所选小部件)整齐地放置在 QVBoxLayout 中。工具框的样式树如下所示。

我们展示了Plastique样式的工具框图片。

在Plastique样式以及其他内置Qt样式中,所有元素都有相同的边框矩形。

工具框的样式选项是 QStyleOptionToolBox。它包含了工具框内容的文本和图标。由 QToolBox 设置的唯一状态是 State_Sunken,它是当用户使用鼠标按下一个标签时设置的。其余的 QStyleOptionToolBox 成员是

成员内容
图标工具框标签上的图标。
文本工具框标签上的文本。

大小 grip

大小grip用 CT_SizeGrip 计算其大小提示。像素度量 PM_SizeGripSize 目前在Qt中未使用。在Plastique样式的 QSizeGrip 中的图像元素树如下。

我们在QMainWindow的右下角展示了大小 grip。

尺寸握把样式选项,QStyleOptionSizeGrip,除了来自QStyleOption的公共成员外,还有一个成员。

成员内容
表示握把所在窗口(或等效)的哪个角的Qt::Corner值。

橡皮筋

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

我们展示了一个Java样式窗口在QMdiArea中通过橡皮筋移动的图像

橡皮筋的样式选项是QStyleOptionRubberBand。其成员包括

成员内容
不透明布尔值,当该橡皮筋必须以不透明样式(即颜色)绘制时为true
shape表示带形状的QRubberBand::Shape枚举值(带形状要么是矩形要么是线条)。

浮动小部件

当浮动小部件布局其内容时,它会询问样式以下像素度量:PM_DockWidgetSeparatorExtentPM_DockWidgetTitleBarButtonMarginPM_DockWidgetFrameWidthPM_DockWidgetTitleMargin。它还通过SE_DockWidgetCloseButtonSE_DockWidgetFloatButton计算浮动和关闭按钮的边界矩形。

虚线表示发件人保留箭头接收者的实例(即,它不是绘制样式元素)。当浮动小部件与主窗口分离时(即,它是一个顶级窗口),它只绘制PE_frameDockWidget。如果它已停靠,它绘制指示停靠小部件大小的手柄。我们在塑形样式中显示了已停靠和浮动状态下的浮动小部件

样式选项是QStyleOptionDockWidget

成员内容
可关闭布尔值,表示是否可以关闭浮动窗口。
可浮动布尔值,表示是否符合浮动窗口(即可以脱离其居住的主窗口)。
可移动布尔值,表示窗口是否可移动(即可以移动到其他浮动小部件区域)。
标题浮动窗口的标题文本。

对于按钮,使用QStyleOptionButton(有关内容描述,请参阅工具按钮)。浮动小部件大小调整手柄有一个纯QStyleOption

© 2024 Qt公司有限公司。此处包含的文档贡献是各自所有者的版权。此处提供的文档是根据自由软件基金会发布的<有名 href="http://www.gnu.org/licenses/fdl.html">GNU自由文档许可1.3版的条款许可的。Qt及其相关标志是芬兰以及/或其他国家的Qt公司有限公司的商标。所有其他商标均为其各自所有者的财产。