样式表语法

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

样式规则

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

QPushButton { color: red }

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

Qt 样式表通常不区分大小写(即,colorColorCOLORcOloR 指的是同一个属性)。唯一的例外是类名、对象名 和 Qt 属性名,这些都是大小写敏感的。

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

与以下三个规则序列等效

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()(请参阅 toString() 函数文档以获取详细信息)的 Qt 属性。此外,支持特殊 class 属性,用于表示类名。

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

与其使用 =,您还可以使用 ~= 来测试一个类型为 QStringList 的 Qt 属性是否包含指定的 QString

警告:如果样式表设置之后 Qt 属性的值发生变化,可能需要强制重新计算样式表。一种实现方式是先取消设置样式表,然后再重新设置。

类选择器.QPushButton匹配 QPushButton 的实例,但不是其子类的实例。

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

ID 选择器 QPushButton#okButton匹配所有 QPushButton 实例,其中 对象名称okButton
后代选择器QDialog QPushButton匹配所有是 QDialog 后代(子、孙等)的 QPushButton 实例。
子元素选择器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)允许您改变子控件相对于参照元素的位置和尺寸。

一旦定位,它们将与小部件处理方式相同,可以使用盒模型进行样式化。

请参阅下面的子控件列表获取支持子控件列表,并查看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,可能会让人误以为QPushButtonQAbstractButton更具体。然而,对于样式表计算,所有类型选择器的特定性相同,最后出现的规则具有优先级。换句话说,对于所有包括QPushButton在内的QAbstractButton,都将颜色设置为灰色。如果我们真的希望QPushButton使用红色文本,我们总是可以重新排列规则。

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

选择器的特定性计算如下

  • 计算选择器中的ID属性数量(= a)
  • 计算选择器中的其他属性和伪类的数量(= b)
  • 计算选择器中的元素名称数量(= c)
  • 忽略伪元素[即subcontrols]

将三个数字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样式表时,小部件不自动从其父小部件继承其字体和颜色设置。

例如,考虑一个QPushButton在一个QGroupBox内部

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

QPushButton(请参阅qpushbutton.html)没有显式设置颜色。因此,它不继承其父QGroupBox(请参阅qgroupbox.html)的颜色,而是使用系统颜色。如果我们想在QGroupBox及其子对象上设置颜色,可以写出以下代码

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

相比之下,使用QWidget::setFont()和QWidget::setPalette()设置字体和外观,命令会传播到子控件。

如果您希望字体和外观传播到子控件,可以设置Qt::AA_UseStyleSheetPropagationInWidgetStyles标志,如下所示

使用方法

QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);

当启用部件样式字体和外观传播时,通过Qt样式表做出的字体和外观更改将表现得仿佛用户已手动调用相应QWidget::setPalette()和QWidget::setFont()方法对所有由样式表指定的QWidgets进行操作。

  • 样式表做出的更改会传播。它们在更改发生时, будут推送到一次匹配样式表的全部控件。
  • 调用QWidget::setPalette()或QWidget::setFont()做出的更改会被继承。它们被所有现有和未来的子控件继承,前提是相应的画笔或字体没有显式设置。

C++命名空间内的控件

类型选择器可用于为特定类型的控件设置样式。例如,

class MyPushButton : public QPushButton {
    // ...
}

// ...
qApp->setStyleSheet("MyPushButton { background: yellow; }");

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

namespace ns {
    class MyPushButton : public 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)中尝试使用它们的尝试都不会起作用。

© 2024 Qt公司有限公司。本文件中包含的文档贡献由各自的版权方拥有。本文件提供的文档是根据自由软件基金会发布的GNU自由文档许可版本1.3条款许可的。Qt和相关的标志是芬兰以及在世界其他地区的Qt公司有限公司的商标。所有其他商标均为其各自拥有者的财产。