警告

本节包含从C++自动翻译到Python的代码片段,可能存在错误。

样式表语法#

Qt样式表术语和语法规则几乎与HTML CSS完全相同。如果您已经了解CSS,您可能可以快速浏览本节内容。

样式规则#

样式表由一系列样式规则组成。一个 样式规则 由一个选择器和一条声明组成。选择器指定受该规则影响的哪些小部件;声明指定应在小部件上设置哪些属性。

QPushButton { color: red }

在上面的样式规则中,QPushButton 是选择器,{ color: red } 是声明。此规则指定了 QPushButton 及其子类(例如,MyPushButton)应使用红色作为前景色。

Qt样式表通常不区分大小写(即,colorColorCOLORcOloR 都指代相同的属性)。唯一例外的是类名、对象名和Qt属性名,它们是区分大小写的。

可以为同一条声明指定多个选择器,使用逗号(,)分隔选择器。例如,下面的规则

QPushButton, QLineEdit, QComboBox { color: red }

与下面的三个规则等价

QPushButton { color: red }
QLineEdit { color: red }
QComboBox { color: red }

样式规则的声明部分是由括号({})括起来的 property:value 对的列表,并用分号分隔。例如

QPushButton { color: red; background-color: white }

有关Qt小部件提供的属性列表,请参阅下面的属性列表部分。

选择器类型#

到目前为止的所有例子都使用了最简单的选择器类型,即类型选择器。Qt样式表支持所有CSS2中定义的选择器。下表总结了最有用的选择器类型。

选择器

例子

解析

通用选择器

*

匹配所有小部件。

类型选择器

QPushButton

匹配 QPushButton 及其子类的实例。

属性选择器

QPushButton[flat="false"]

匹配不包含flatQPushButton实例。您可以使用此选择器来测试支持QVariant::toString()的任何Qt属性(有关详细信息,请参阅toString()函数文档)。此外,支持特殊class属性,用于类的名称。

此选择器也可用于测试动态属性。有关使用动态属性进行定制的更多信息,请参阅使用动态属性进行定制

除了=外,您还可以使用~=来测试某个Qt属性是否包含指定的QString。

警告

如果样式表设置后Qt属性值发生变化,可能需要强制重新计算样式表。实现这一目标的一种方法是取消设置样式表,然后再设置一次。

类选择器

.QPushButton

匹配QPushButton的实例,但不包含其子类。

这等同于*[class~="QPushButton"]

ID .. _id-selector: 选择器

QPushButton#okButton

匹配对象名为okButton的所有QPushButton实例。

后代选择器

QDialog QPushButton

匹配所有派生自(子代、孙代等)QDialogQPushButton实例。

子元素选择器

QDialog > QPushButton

匹配所有直接为QDialog子元素的QPushButton实例。

子控件#

对于设计复杂的控件,需要访问小控件,如QComboBox的下拉按钮或在QSpinBox中的左右箭头。选择器可以包含子控件,这使得将规则应用于特定的控件子控件成为可能。例如:

QComboBox::drop-down { image: url(dropdown.png) }

上述规则样式化了所有QComboBox的下拉按钮。尽管双冒号(::)语法让人联想到CSS3伪元素,但Qt子控件在概念上与这些控件不同,并且有不同的一致性语义。

子控件始终相对于另一个元素——一个参考元素进行定位。这个参考元素可以是小部件或其他子控件。例如,::drop-down 的默认位置是 QComboBox 的填充矩形的右上角。默认情况下,::drop-down 子控件的定位是在其内容矩形的中心。下表列出了可用于样式化控件及其默认位置的子控件。

可以通过使用 subcontrol-origin 属性来更改要使用的原始矩形的起始位置。例如,如果我们想将下拉菜单放置在 QComboBox 的衬距矩形内,而不是默认的填充矩形,我们可以指定:

QComboBox {
    margin-right: 20px;
}
QComboBox::drop-down {
    subcontrol-origin: margin;
}

可以通过使用 subcontrol-position 属性更改下拉菜单在衬距矩形中的对齐方式。

可以使用 widthheight 属性来控制子控件的大小。请注意,显式设置 image 会隐式地设置子控件的大小。

相对定位方案(position : relative)允许子控件的定位相对于其初始位置进行偏移。例如,当按下 QComboBox 的下拉按钮时,我们可能希望内部的箭头进行偏移以产生“按下”效果。要实现这一点,我们可以指定:

QComboBox::down-arrow {
    image: url(down_arrow.png);
}
QComboBox::down-arrow:pressed {
    position: relative;
    top: 1px; left: 1px;
}

绝对定位方案(position : absolute)允许根据参考元素更改子控件的位置和大小。

定位完成后,它们将被视为与控件相同,并可以使用 box model 进行样式化。

下表列出了受支持的子控件,以及有关实际示例的 自定义 QPushButton 的菜单指示符子控件

注意

对于诸如 QComboBoxQScrollBar之类的复杂控件,如果自定义一个属性或子控件,必须相应地同时自定义其他所有属性或子控件。

伪状态#

选择器可以包含伪状态,以表示基于小部件的状态来限制规则的适用性。伪状态出现在选择器的末尾,状态之间用冒号(:)连接。例如,以下规则在鼠标悬停在QPushButton上时应用。

QPushButton:hover { color: white }

可以使用感叹号运算符对伪状态进行否定。例如,以下规则在鼠标没有悬停在QRadioButton上时应用。

QRadioButton:!hover { color: red }

伪状态可以链式应用,在这种情况下隐含逻辑与运算。例如,以下规则适用于鼠标悬停在选中的QCheckBox上时。

QCheckBox:hover:checked { color: white }

否定伪状态可以出现在伪状态链中。例如,以下规则适用于鼠标悬停在未按下的QPushButton上时。

QPushButton:hover:!pressed { color: blue; }

如果需要,可以使用逗号运算符表达逻辑或。

QCheckBox:hover, QCheckBox:checked { color: white }

伪状态可以与子控件组合出现。例如

QComboBox::drop-down:hover { image: url(dropdown_bright.png) }

请参阅下文的伪状态列表部分,以获取Qt控件提供的伪状态列表。

冲突解决#

当几个样式规则指定了具有不同值的相同属性时,会出现冲突。考虑以下样式表

QPushButton#okButton { color: gray }
QPushButton { color: red }

两条规则匹配名为okButtonQPushButton实例,且存在color属性的冲突。为了解决此冲突,我们必须考虑选择器的特定性。在上面的例子中,QPushButton#okButton被认为比QPushButton更加特定,因为它(通常)指的是单一对象,而不是类中所有实例。

类似地,具有伪状态的选择器比没有指定伪状态的选择器更为特定。因此,以下样式表指定当鼠标悬停在QPushButton上时,按钮应该有白色文本,否则为红色文本。

QPushButton:hover { color: white }
QPushButton { color: red }

这里有一个棘手的例子

QPushButton:hover { color: white }
QPushButton:enabled { color: red }

在这里,两个选择器的特定性相同,因此当按钮处于启用状态时,悬停鼠标,第二个规则将具有优先权。如果我们希望在那种情况下文本为白色,我们可以在样式表中修改规则顺序,如下所示

QPushButton:enabled { color: red }
QPushButton:hover { color: white }

或者,我們可以將第一個規則修飾得更為特定:

QPushButton:hover:enabled { color: white }
QPushButton:enabled { color: red }

同样的问题也会出现在类型选择器中。考虑以下示例

QPushButton { color: red }
QAbstractButton { color: gray }

这两条规则适用于 QPushButton 实例(因为 QPushButton 继承自 QAbstractButton)且存在 颜色 属性的冲突。由于 QPushButton 继承自 QAbstractButton,可能会让人误以为 QPushButton 比更具体的 QAbstractButton 更具体。然而,对于样式表的计算,所有类型选择器的特定性相同,最后出现的规则优先。换句话说,对于所有 QAbstractButton,包括 QPushButton颜色 设置为 灰色。如果真的想让 QPushButton 拥有红色文本,我们可以始终重新排序规则。

在确定规则特定性时,Qt样式表遵循 CSS2规范

选择器的特定性计算如下

  • 选择器中ID属性的数量(= a)

  • 选择器中其他属性和伪类的数量(= b)

  • 选择器中元素名的数量(= c)

  • 忽略伪元素 [{即 子控件}]。

将三个数字 a-b-c(在一个大基数数制中)连接起来,得到特定性。

一些例子

*             {}  /* a=0 b=0 c=0 -> specificity =   0 */
LI            {}  /* a=0 b=0 c=1 -> specificity =   1 */
UL LI         {}  /* a=0 b=0 c=2 -> specificity =   2 */
UL OL+LI      {}  /* a=0 b=0 c=3 -> specificity =   3 */
H1 + *[REL=up]{}  /* a=0 b=1 c=1 -> specificity =  11 */
UL OL LI.red  {}  /* a=0 b=1 c=3 -> specificity =  13 */
LI.red.level  {}  /* a=0 b=2 c=1 -> specificity =  21 */
#x34y         {}  /* a=1 b=0 c=0 -> specificity = 100 */

层叠#

样式表可以设置在 QApplication 上,在上层小部件上,以及在子小部件上。任意小部件的有效样式表是通过合并设置在小部件祖先(父级、祖父级等)上的样式表以及设置在 QApplication 上的样式表来获得的。

在出现冲突时,小部件自身的样式表始终优先于任何继承的样式表,无论冲突规则特定性如何。同样,父小部件的样式表优先于祖父的,等等。

此结果的后果之一是,将样式规则应用于小部件自动使它优先于祖先小部件样式表中或其他继承自QApplication 的一些建议中的规则。考虑以下示例。首先,我们在QApplication 上设置了一个样式表。

qApp.setStyleSheet("QPushButton { color: white }")

然后,我们在QPushButton 对象上设置了一个样式表。

myPushButton.setStyleSheet("* { color: blue }")

QPushButton 上的样式表将迫使QPushButton(以及其他子小部件)具有蓝色文本,尽管应用程序样式表中提供了一些更具体的规则。

结果将相同,如果我们这样写的话:

myPushButton.setStyleSheet("color: blue")

除了如果QPushButton 有子项(这是不太可能的),样式表对它们没有影响。

样式层叠是一个复杂的话题。有关详细信息,请参阅CSS2规范。请注意,Qt当前尚未实现!important

继承#

在经典的CSS中,如果某项的字体和颜色未明确设置,它将自动从父项继承。默认情况下,使用Qt样式表时,小部件不自动从其父小部件继承其字体和颜色设置。

例如,考虑一个位于QGroupBox 内的QPushButton

qApp.setStyleSheet("QGroupBox { color: red; } ")

QPushButton 没有明确设置颜色。因此,它不会继承其父 QGroupBox 的颜色,而具有系统颜色。如果我们想设置QGroupBox 及其子项的颜色,我们可以这样写:

qApp.setStyleSheet("QGroupBox, QGroupBox * { color: red; }")

相比之下,使用setFont()setPalette() 设置字体和调色板会使它们传播到子小部件。

如果您希望字体和调色板传播到子小部件,您可以为标志设置Qt::AA_UseStyleSheetPropagationInWidgetStyles。

用法

QCoreApplication.setAttribute(Qt.AA_UseStyleSheetPropagationInWidgetStyles, True)

当启用小部件样式字体和调色板传播时,通过 Qt 风格表进行的字体和调色板更改将表现得像是用户手动调用了所有由风格表针对的 QWidgets 的相应 setPalette()setFont() 方法。如果这会导致 C++ 中的传播,则将导致风格表中的传播,反之亦然。

C++ 命名空间内部的小部件

类型选择器可以用来设置特定类型小部件的样式。例如,

class MyPushButton(QPushButton):
    # ...

# ...
qApp.setStyleSheet("MyPushButton { background: yellow; }")

Qt 风格表使用小部件的 QObject::className() 来决定何时应用类型选择器。当自定义小部件在命名空间内时,QObject::className() 返回 <namespace>::<classname>。这与子控件的语法冲突。为了解决这个问题,当使用类型选择器为命名空间内的小部件设置样式时,我们必须将 :: 替换为 --。例如,

ns = {
    class MyPushButton(QPushButton):
        # ...


# ...
qApp.setStyleSheet("ns--MyPushButton { background: yellow; }")

设置 QObject 属性

从 4.3 版本以上,可以使用 qproperty-<property name> 语法设置任何可设计 Q_PROPERTY。

例如,

MyLabel { qproperty-pixmap: url(pixmap.png); }
MyGroupBox { qproperty-titleColor: rgb(100, 200, 100); }
QPushButton { qproperty-iconSize: 20px 20px; }

如果属性引用了使用 Q_ENUM 声明的枚举,你应该通过名称引用其常量,而不是它们的数值。

注意

使用 qproperty 语法时要小心,因为它会修改正在绘制的小部件。此外,qproperty 语法只评估一次,这是当小部件被风格润色时。这意味着尝试将它们用于伪状态,如 QPushButton :hover,将不会起作用。