样式表语法
Qt 样式表的术语和语法与HTML的CSS几乎一样. 如果你已经了解CSS, 你可以快速浏览这一节.
Style Rules
样式表的内容是由一系列样式规则组成. 一条 样式规则 是由选择器和声明组成. 选择器 指明哪些widget受所定义的规则影响; 声明 指明设置widget的哪些属性. 例如:
QPushButton { color: red }
在上述样式规则中, QPushButton
是选择器, { color: red }
是声明. 这条规则指明 QPushButton 和它的子类 (例如., MyPushButton
) 应该用红色作为它们的前景色.
Qt 样式表通常不区分大小写 (例如., color
, Color
, COLOR
和 cOloR
代表相同属性). 唯一的例外是类名, 对象名, 和 Qt的属性名, 它们区分大小写.
多个选择器能指定相同的声明, 多个选择器之间用逗号 (,
) 分隔. 例如:
QPushButton, QLineEdit, QComboBox { color: red }
这条规则相当于三条独立规则:
QPushButton { color: red } QLineEdit { color: red } QComboBox { color: red }
样式表声明部分由大括号({}
)括起来的一组键值对( property: value
), 键值对之间使用分号(;
)分隔. 例如:
QPushButton { color: red; background-color: white }
Qt的widget属性参见 属性列表 章节.
选择器类型
目前为止, 所有的示例使用最简单的选择器类型, 即类型选择器. Qt样式表支持 CSS2定义的选择器类型. 下表是最常用的选择器类型.
选择器 | 示例 | 描述 |
---|---|---|
通用选择器 | * | 匹配所有widget. |
类型选择器 | QPushButton | 匹配 QPushButton 及其子类实例. |
属性选择器 | QPushButton[flat="false"] | 匹配非扁平的 QPushButton 实例. 你可以用这个选择器测试Qt的 属性 是否支持QVariant::toString()函数 (详见 toString() 函数文档). 此外, 这个选择器还支持 class 属性(类名).这个选择器还支持动态属性测试. 有关动态属性自定义内容参见 Customizing Using Dynamic Properties. Instead of , 你也可以用 警告: 如果你设置样式表后, Qt的属性发生改变, 可能需要强制重新计算样式表. 一种有效的方式是取消样式表设置, 再重新设置. |
类选择器 | .QPushButton | 匹配 QPushButton实例, 但是不匹配它的子类实例. 等价于 |
ID 选择器 | QPushButton#okButton | 匹配所有对象名称是 okButton 的 QPushButton 实例. |
后代选择器 | QDialog QPushButton | 匹配QDialog的所有后代(包括子代)上的QPushButton实例. |
子代选择器 | QDialog > QPushButton | 匹配QDialog. | 的子代上的 QPushButton 实例
Sub-Controls
对于复杂的widget, 有时需要访问其子控件. 如QComboBox的上下按钮或 QSpinBox的上下箭头. 选择器可以包含 子控件, 通过这种方式, 应用程序能定义特定widget的子控件样式. 例如:
QComboBox::drop-down { image: url(dropdown.png) }
上述样式规则指定 QComboBox的上下按钮的显示样式. 虽然, 双冒号 (::
) 语法会让人联系 CSS3 的伪元素, 但是, Qt的子控件与它们在概念上是不同的, 具有不同的级联语义.
子控件是相对于另一个关联元素. 这个关联元素可能是一个widget或另一个子控件. 例如, 默认情况下, QComboBox 的 ::drop-down 被放置在 QComboBox 右侧. 有关widget的子控件及其默认位置参考 List of Stylable Widgets.
可以使用 subcontrol-origin 属性修改子控件的初始矩形. 例如, 如果我们采用下述方式将 QComboBox 的下拉框从内边距(Padding rectangle)改为外边距(margin rectangle):
QComboBox { margin-right: 20px; } QComboBox::drop-down { subcontrol-origin: margin; }
使用 subcontrol-position 属性改变下拉框的外边距的对齐方式.
使用 width and height 属性改变子控件的大小. 注意设置子控件的 image 也会改变子控件的大小.
设置相对位置 (position : relative), 会使子控件相对初始位置偏移. 例如, 当按下 QComboBox的下拉按钮时, 我们可以采用下述方式通过设置下拉按钮偏移产生一种 "pressed" 效果.
QComboBox::down-arrow { image: url(down_arrow.png); } QComboBox::down-arrow:pressed { position: relative; top: 1px; left: 1px; }
设置绝对位置 (position : absolute), 会使子控件参考关联元素改变自身的位置和大小.
设定位置后, 子控件会跟widget一样使用 box model 设置样式.
Qt支持的所有子控件列表参见下述章节 List of Sub-Controls, 此外, 示例可参考 Customizing the QPushButton's Menu Indicator Sub-Control .
注意: 对于复杂widget( QComboBox 和 QScrollBar), 如果你定义它们的一个子控件或属性, 那么所有 子控件和属性也必须定义.
伪状态
选择器可能包含 伪状态, that denote that restrict the application of the rule based on the widget's state. Pseudo-states appear at the end of the selector, with a colon (:
) in between. For example, the following rule applies when the mouse hovers over a QPushButton:
QPushButton:hover { color: white }
Pseudo-states can be negated using the exclamation operator. For example, the following rule applies when the mouse does not hover over a QRadioButton:
QRadioButton:!hover { color: red }
Pseudo-states can be chained, in which case a logical AND is implied. For example, the following rule applies to when the mouse hovers over a checked QCheckBox:
QCheckBox:hover:checked { color: white }
Negated Pseudo-states may appear in Pseudo-state chains. For example, the following rule applies when the mouse hovers over a QPushButton that is not pressed:
QPushButton:hover:!pressed { color: blue; }
If needed, logical OR can be expressed using the comma operator:
QCheckBox:hover, QCheckBox:checked { color: white }
Pseudo-states can appear in combination with subcontrols. For example:
QComboBox::drop-down:hover { image: url(dropdown_bright.png) }
See the List of Pseudo-States section below for the list of pseudo-states provided by Qt widgets.
Conflict Resolution
Conflicts arise when several style rules specify the same properties with different values. Consider the following style sheet:
QPushButton#okButton { color: gray } QPushButton { color: red }
Both rules match QPushButton instances called okButton
and there is a conflict for the color
property. To resolve this conflict, we must take into account the specificity of the selectors. In the above example, QPushButton#okButton
is considered more specific than QPushButton
, because it (usually) refers to a single object, not to all instances of a class.
Similarly, selectors with pseudo-states are more specific than ones that do not specify pseudo-states. Thus, the following style sheet specifies that a QPushButton should have white text when the mouse is hovering over it, otherwise red text:
QPushButton:hover { color: white } QPushButton { color: red }
Here's a tricky one:
QPushButton:hover { color: white } QPushButton:enabled { color: red }
Here, both selectors have the same specificity, so if the mouse hovers over the button while it is enabled, the second rule takes precedence. If we want the text to be white in that case, we can reorder the rules like this:
QPushButton:enabled { color: red } QPushButton:hover { color: white }
Alternatively, we can make the first rule more specific:
QPushButton:hover:enabled { color: white } QPushButton:enabled { color: red }
A similar issue arises in conjunction with Type Selectors. Consider the following example:
QPushButton { color: red } QAbstractButton { color: gray }
Both rules apply to QPushButton instances (since QPushButton inherits QAbstractButton) and there is a conflict for the color property. Because QPushButton inherits QAbstractButton, it might be tempting to assume that QPushButton
is more specific than QAbstractButton
. However, for style sheet computations, all Type Selectors have the same specificity, and the rule that appears last takes precedence. In other words, color is set to gray
for all QAbstractButtons, including QPushButtons. If we really want QPushButtons to have red text, we can always reorder the rules.
For determining the specificity of a rule, Qt Style Sheets follow the CSS2 Specification:
A selector's specificity is calculated as follows:
- count the number of ID attributes in the selector (= a)
- count the number of other attributes and pseudo-classes in the selector (= b)
- count the number of element names in the selector (= c)
- ignore pseudo-elements [i.e., subcontrols].
Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.
Some examples:
* {} /* 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 */
Cascading
Style sheets can be set on the QApplication, on parent widgets, and on child widgets. An arbitrary widget's effective style sheet is obtained by merging the style sheets set on the widget's ancestors (parent, grandparent, etc.), as well as any style sheet set on the QApplication.
When conflicts arise, the widget's own style sheet is always preferred to any inherited style sheet, irrespective of the specificity of the conflicting rules. Likewise, the parent widget's style sheet is preferred to the grandparent's, etc.
One consequence of this is that setting a style rule on a widget automatically gives it precedence over other rules specified in the ancestor widgets' style sheets or the QApplication style sheet. Consider the following example. First, we set a style sheet on the QApplication:
qApp->setStyleSheet("QPushButton { color: white }");
Then we set a style sheet on a QPushButton object:
myPushButton->setStyleSheet("* { color: blue }");
The style sheet on the QPushButton forces the QPushButton (and any child widget) to have blue text, in spite of the more specific rule set provided by the application-wide style sheet.
The result would have been the same if we had written
myPushButton->setStyleSheet("color: blue");
except that if the QPushButton had children (which is unlikely), the style sheet would have no impact on them.
Style sheet cascading is a complex topic. Refer to the CSS2 Specification for the gory details. Be aware that Qt currently doesn't implement !important
.
Inheritance
In classic CSS, when font and color of an item is not explicitly set, it gets automatically inherited from the parent. By default, when using Qt Style Sheets, a widget does not automatically inherit its font and color setting from its parent widget.
For example, consider a QPushButton inside a QGroupBox:
qApp->setStyleSheet("QGroupBox { color: red; } ");
The QPushButton does not have an explicit color set. Hence, instead of inheriting color of its parent QGroupBox, it has the system color. If we want to set the color on a QGroupBox and its children, we can write:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
In contrast, setting a font and palette using QWidget::setFont() and QWidget::setPalette() propagates to child widgets.
If you would prefer that the font and palette propagate to child widgets, you can set the Qt::AA_UseStyleSheetPropagationInWidgetStyles flag, like this:
Usage:
QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
When the widget-style font and palette propagation is enabled, font and palette changes made through Qt Style Sheets will behave as though the user had manually called the corresponding QWidget::setPalette() and QWidget::setFont() methods on all of the QWidgets targeted by the style sheet. If this would have caused propagation in C++, it will cause propagation in style sheets and visa versa.
Widgets Inside C++ Namespaces
The Type Selector can be used to style widgets of a particular type. For example,
class MyPushButton : public QPushButton { // ... } // ... qApp->setStyleSheet("MyPushButton { background: yellow; }");
Qt Style Sheet uses QObject::className() of the widget to determine when to apply the Type Selector. When custom widgets are inside namespaces, the QObject::className() returns <namespace>::<classname>. This conflicts with the syntax for Sub-Controls. To overcome this problem, when using the Type Selector for widgets inside namespaces, we must replace the "::" with "--". For example,
namespace ns { class MyPushButton : public QPushButton { // ... } } // ... qApp->setStyleSheet("ns--MyPushButton { background: yellow; }");
Setting QObject Properties
From 4.3 and above, any designable Q_PROPERTY can be set using the qproperty-<property name> syntax.
For example,
MyLabel { qproperty-pixmap: url(pixmap.png); } MyGroupBox { qproperty-titleColor: rgb(100, 200, 100); } QPushButton { qproperty-iconSize: 20px 20px; }
If the property references an enum declared with Q_ENUMS, you should reference its constants by name, i.e., not their numeric value.