本页内容

样式如何绘制控件

QStyle API 包含三类函数:用于绘制控件的函数、用于处理常见及复杂任务(如计算滑块手柄位置)的静态辅助函数,以及用于控件绘制过程中所需计算的函数(例如计算其尺寸提示)。 样式还会协助某些控件布局其内容,并可调整控件绘制时使用的QPalette 。

本页介绍了样式实现所使用的构建块。“控件样式参考”列出了每个控件使用的具体构建块。

样式元素

QStyle 用于绘制图形元素。元素可以是控件本身,也可以是控件的某个部分,例如按钮的斜面、窗口边框或滚动条。大多数绘制函数接受四个参数:

  • 一个枚举值,用于指定要绘制的图形元素。
  • 一个QStyleOption ,用于指定如何以及在何处呈现该元素。
  • 用于绘制该元素的QPainter 。
  • 用于执行绘制的QWidget 。此参数为可选。

当小部件请求样式绘制某个元素时,它会向样式提供一个QStyleOption ——一个包含绘制所需信息的类。由于该选项包含了样式所需的一切信息,因此样式无需链接任何小部件代码即可绘制小部件。您可以在任何绘图设备上绘制组合框,而不仅仅是在QComboBox 上。

将小部件作为最后一个参数传递,是为了以防样式需要它来实现特殊效果(例如 macOS 上的动画默认按钮),但样式不能依赖它。那些自己绘制多个元素的小部件会传递自身,QStylePainter 也是如此。直接在绘图设备上绘制的代码可能会传递nullptr 。

一个小部件由一组风格元素的层次结构(或树)组成。例如,当一个样式接收到绘制按压按钮的请求时,它会绘制一个标签(文本和图标)、一个按钮斜边以及一个焦点框。 按钮斜面又由围绕斜面和面板的边框组成。下图所示的概念树按绘制顺序展示了按钮的各个元素,其中嵌套元素由其上层元素进行绘制:

  • 按钮
    • 按钮斜面
      • 默认按钮边框
      • 按钮面板
    • 标签(图标和文本)
    • 焦点框

参考中的“按钮”部分展示了QPushButton 的实际树结构,其中包含元素名称。

控件并不一定要求样式仅绘制一个元素。一个控件可能会多次调用样式来绘制不同的元素。例如,QTabWidget 会分别绘制其选项卡和框架。

元素分为三种类型:基本元素、控件元素和复杂控件元素。PrimitiveElement 、ControlElement 和ComplexControl 这三个枚举类型分别定义了它们。每个枚举值的名称前都有一个前缀,用于标识其类型:基本元素为PE_ ,控件元素为CE_ ,复杂控件为CC_ 。QStyle 类的文档列出了这些元素及其在小部件样式设置中的作用。

基本元素

基本元素是常见的 GUI 元素,常被多个控件所使用。例如:框架、按钮斜面,以及旋转框、滚动条和组合框中的箭头。 基本元素不能独立存在;它们总是作为更大结构的一部分。它们不参与与用户的交互,而是图形用户界面中的被动装饰。

控件元素

控件元素用于执行操作或向用户显示信息。控件元素的示例包括按钮、复选框,以及表格和树形视图中的标题部分。 控件元素并不总是像按钮那样是一个完整的控件;它也可以是控件的组成部分,例如标签栏中的标签或滚动条上的滑块。控件元素与基本元素的区别在于它们并非被动元素:它们会参与与用户的交互。

由多个元素组成的控件通常使用样式来计算这些元素的边界矩形。SubElement 枚举定义了可用的子元素。该枚举仅用于计算边界矩形。子元素并非像基本元素、控件和复杂元素那样需要绘制的图形元素。

复杂控件

复杂控件包含子控件。复杂控件的行为会根据用户使用鼠标操作的位置以及按下的键盘键而有所不同。这取决于鼠标悬停或点击的是哪个子控件(如有)。 复杂控件的示例包括滚动条和组合框。对于滚动条,您可以使用鼠标移动滑块,并点击向上和向下按钮。SubControl 枚举定义了可用的子控件。

除了绘制功能外,该样式还会告知控件用户点击了哪个子控件(如有)。例如,QScrollBar 需要知道用户点击的是滑块、滑块凹槽还是其中一个按钮。

子控件与控件元素并不相同。您无法直接使用样式来绘制子控件;该样式仅用于计算子控件应绘制的边界矩形。不过,复杂元素通常会使用控件和基本元素来绘制其子控件。 Qt 的内置样式经常采用这种做法。例如,QCommonStyle 使用PE_IndicatorCheckBox 来绘制组框中的复选框,而该复选框是CC_GroupBox 的子控件。某些子控件具有等效的控件元素,例如滚动条的滑块(SC_ScrollBarSlider 和CE_ScrollBarSlider )。

子元素、子控件和像素度量

样式元素和控件使用样式来计算子元素和子控件的边界矩形。像素度量(即以屏幕像素为单位的、依赖于样式的尺寸)在绘制时也会用于测量。QStyle 中的三个枚举值代表了可用的边界矩形和像素度量:SubElement 、SubControl 和PixelMetric 。它们的值分别以SE_ 、SC_ 和PM_ 开头。

样式提示

该样式还会响应一组样式提示,这些提示由StyleHint 枚举的值表示。并非所有小部件在不同样式下的功能和外观都相同。 例如,当菜单中的菜单项无法在屏幕上以单列形式显示时,某些样式支持滚动,而其他样式则会绘制多列以容纳所有项目。控件通过 `styleHint()` 方法查询样式提示。

标准图标

一种样式通常包含一组标准图标,例如用于消息框、文件对话框和标题栏按钮的警告、问号和错误图标。StandardPixmap 枚举为其命名,而standardIcon()会返回某个值的QIcon 。Qt Widgets使用这些图标,因此在实现样式时,请提供这些图标。

布局间距

样式会计算布局中控件之间的间距。处理这些计算有两种方法。 你可以通过pixelMetric() 方法为PM_LayoutHorizontalSpacing 和PM_LayoutVerticalSpacing 返回间距,QCommonStyle 就是这样实现的。或者,如果你需要更多控制权,可以重写layoutSpacing() 方法。在该函数中,你可以根据两个相邻小部件的控件类型(QSizePolicy::ControlType )、其大小策略(QSizePolicy::Policy )以及相关小部件的样式选项来计算间距。

样式选项

QStyleOption 的子类包含为各个元素设置样式所需的所有信息。QStyle 函数的调用者会实例化一个样式选项(通常在栈上),并填充其内容。根据要绘制的对象不同,样式期望的样式选项类也不同。例如,PE_FrameFocusRect 元素期望一个QStyleOptionFocusRect 参数。 您还可以创建自己的子类,供自定义样式使用。出于性能考虑,样式选项保留了公共变量。

控件可以处于多种不同的状态,这些状态由State 枚举定义。某些状态标志的含义因控件而异,但有些则是所有控件共有的,例如State_Enabled 。QStyleOption::initFrom() 用于设置这些通用状态;其余状态则由各个控件自行设置。

最值得注意的是,样式选项包含待绘制的控件的配色方案和边界矩形。大多数控件都有专门的样式选项。例如,QPushButton 和QCheckBox 控件使用QStyleOptionButton 选项,其中包含文本、图标以及图标的大小。控件样式参考详细描述了每个选项的具体内容。

当您重写那些接受QStyleOption 参数的QStyle 函数时,通常需要将该选项强制转换为子类,例如QStyleOptionFocusRect 。请使用qstyleoption_cast() 来确保指针类型正确。如果对象类型不正确,qstyleoption_cast() 将返回nullptr :

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

常见状态标志和成员

某些状态和变量对所有控件而言是通用的。控件通过QStyleOption::initFrom()来设置它们。并非所有元素都使用此函数。控件会自行创建样式选项,对于某些元素而言,initFrom()返回的信息并非必需。

状态设置条件
State_Enabled控件未被禁用时(参见QWidget::isEnabled())。
State_HasFocus控件处于焦点状态(参见QWidget::hasFocus())。
State_KeyboardFocusChange用户通过键盘更改了焦点(参见WA_KeyboardFocusChange )。
State_MouseOver鼠标光标悬停在小部件上。
State_Active该控件是活动窗口的子控件。

其他常见成员包括:

成员描述
rect要绘制的元素的边界矩形。initFrom() 将其设置为控件的边界矩形(QWidget::rect())。
direction布局方向,取值为Qt::LayoutDirection 枚举中的一个值。
palette绘制元素时使用的QPalette 。initFrom() 将其设置为控件的调色板(QWidget::palette())。
fontMetrics在控件上绘制文本时使用的QFontMetrics 。
styleObject该选项所描述的对象,通常是控件。样式会利用它来存储动画状态。

复杂的样式选项(继承自QStyleOptionComplex 的类)还共享另外两个变量:subControls 和activeSubControls 。它们都是QStyle::SubControl 值的或组合。它们指示复杂控件由哪些子控件组成,以及其中哪些当前处于活动状态。

QStyle 函数

QStyle 定义了三个用于绘制基元、控件和复合元素的函数:drawPrimitive()、drawControl() 和drawComplexControl()。它们接受“样式元素”部分列出的参数。

并非所有小部件都会传递指向自身的指针。如果传递给函数的样式选项中不包含所需信息,请检查该小部件的实现,以确定它是否会传递自身。

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

其他QStyle 函数为绘制函数进行计算。当小部件自行绘制多个样式元素时,也会使用这些函数来计算尺寸提示和边界矩形。这些函数通常与绘制函数采用相同的参数。

  • subElementRect() 接受一个 `SubElement ` 值,并计算子元素的边界矩形。样式通过此函数来确定元素各部分的绘制位置。若创建新样式,可复用基类的子元素位置。
  • subControlRect() 用于计算复杂控件中子控件的边界矩形。当你实现新样式时,需针对与基类不同的矩形重新实现该函数。
  • pixelMetric() 返回像素度量值,即以屏幕像素为单位的样式相关尺寸。其取值为 `PixelMetric ` 枚举中的一个值。像素度量值不必是静态测量值;您可以根据样式选项计算得出。
  • sizeFromContents() 根据给定的内容大小返回小部件的大小。小部件使用该值来计算其大小提示。
  • hitTestComplexControl() 返回在复杂控件中鼠标指针悬停的子控件。通常,这需要使用subControlRect() 获取子控件的边界矩形,并找出包含光标位置的那个矩形。

QStyle 还提供了函数polish() 和unpolish()。Qt 会在小部件首次显示时以及样式发生变化时对其进行“抛光”,并在样式发生变化或小部件被销毁时取消“抛光”。使用这些函数可以设置小部件的属性,或执行样式所需的其他操作。 例如,若需检测鼠标悬停在小部件上的时刻,可在 `polish()` 中设置 `WA_Hover ` 小部件属性。随后,该小部件会在其样式选项中设置 `State_MouseOver `。polish() 的重载方法还允许样式准备 `QApplication ` 并调整应用程序调色板;详见“调色板”章节。

最后,QStyle 提供了用于处理常见和复杂任务的静态辅助函数。sliderPositionFromValue() 和sliderValueFromPosition() 用于在滑块值与像素位置之间进行转换。visualRect()、visualPos() 和visualAlignment() 将逻辑坐标和对齐方式转换为从右到左布局中的镜像对应值,而alignedRect() 则根据当前方向对矩形进行对齐。有关详细信息,请参阅QStyle 类文档中的Right-to-Left Desktops 。

在重写QStyle 的虚函数时,请处理与基类不同的元素,其余部分则调用基类的实现。

调色板

每种样式都使用由QPalette 提供的画笔调色板进行绘制。每个控件状态都有一组颜色(即QPalette::ColorGroup ):具有键盘焦点的窗口中的控件为“活动”状态,其他窗口中的控件为“非活动”状态,被禁用的控件为“禁用”状态。State_Active 和State_Enabled 状态标志用于指示应使用哪个组。每个组都包含QPalette::ColorRole 中定义的颜色角色。 这些角色描述了颜色所适用的场景,例如绘制控件背景、文本或按钮。

每种样式都会决定如何使用这些颜色角色。例如,如果样式使用渐变,它可以使用调色板中的颜色,并通过 `QColor::darker()` 和 `QColor::lighter()` 将其调暗或调亮以生成渐变。通常,如果您需要调色板中未提供的画笔,可以从现有画笔派生出新的画笔。

当您为应用程序设置样式时,Qt 会根据该样式的standardPalette() 函数构建应用程序调色板,允许平台主题覆盖其提供的角色,并将结果传递给QStyle::polish() 函数。您可以重写该重载函数来调整样式所需的颜色。对于应用程序通过QApplication::setPalette() 显式设置的调色板,Qt 不会进行覆盖。

请勿硬编码颜色。应用程序和单个控件可以设置自己的配色方案,而从该配色方案中获取颜色的样式会遵循这些设置。它还会从两个配色方案中自动生成浅色和深色变体,无需任何额外代码。Qt 的 Fusion 样式就是这样工作的:

一个采用Fusion风格、使用浅色调的表单,其中包含文本框、下拉列表、旋转框、滑块、复选框、进度条和按钮

一个采用Fusion风格且使用深色配色的表单,其中包含文本框、下拉列表、旋转框、滑块、复选框、进度条和按钮

一种样式不必在所有可能的配色方案下都看起来美观,但应严格遵循所给定的配色方案。

项目视图

委托负责绘制项目视图中的项目。 Qt XML 的默认委托类 `QStyledItemDelegate` 通过 `CE_ItemViewItem ` 进行绘制,并使用 `CT_ItemViewItem` 计算项目大小,因此即使没有配套的委托类,样式也能控制项目的外观。该样式会直接绘制项目视图的标题、树枝指示符以及行背景。若要支持新的数据类型或项目数据角色,则需要自定义委托类;请参阅“模型/视图编程”。

实现建议

在实现样式时,请仔细阅读各控件及基类的代码。不同控件使用样式的方式各不相同,且基类的实现可能会影响绘制状态——例如,在未恢复状态的情况下修改QPainter 状态,或者在不使用适当的像素度量和子元素的情况下绘制某些元素。

除非必要,否则请勿在sizeFromContents()中更改控件的建议尺寸;应由QCommonStyle 的实现来处理。若需修改,请尽量保持微调。当不同样式间的控件布局差异较大时,应用程序的开发将变得困难。

请使用-reverse 命令行选项或QGuiApplication::setLayoutDirection() 测试您的样式,以确保不对称元素在从右到左的布局中也能显示正确。

另请参阅 QStyle 、QStyleOption 、QStylePainter 、“控件样式参考”以及《复选框样式设置:分步指南》。

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