警告
本节包含从C++自动翻译到Python的代码片段,可能包含错误。
样式表语法¶
Qt样式表的术语和语法规则几乎与HTML CSS相同。如果您已经了解CSS,您可能可以快速浏览本节。
样式规则¶
样式表由一系列样式规则组成。一个样式规则由选择器和声明组成。选择器指定哪些小部件受规则影响;声明指定应设置在小部件上的属性。例如:
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小部件提供的属性列表。
选择器类型¶
到目前为止,所有的例子都使用了最简单的选择器类型,即类型选择器。Qt样式表支持所有CSS2中定义的选择器。下表总结了最有用的选择器类型。
选择器
示例
解释
通用选择器
*匹配所有小部件。
类型选择器
QPushButton匹配
QPushButton及其子类的实例。属性选择器
QPushButton[flat="false"]匹配不是
flat的QPushButton实例。您可以使用此选择器来测试任何支持QVariant::toString()的Qt属性(有关详细信息,请参阅toString()函数文档)。此外,还支持特殊的class属性,用于类的名称。此选择器也可用于测试动态属性。有关使用动态属性进行定制的更多信息,请参阅使用动态属性进行定制。
除了
=,您还可以使用~=来测试QStringList类型的Qt属性是否包含给定的QString。警告
如果在设置样式表后Qt属性的值发生变化,可能需要强制重新计算样式表。实现此目的的一种方法是取消设置样式表并再次设置它。
类选择器
.QPushButton匹配
QPushButton的实例,但不匹配其子类的实例。这相当于
*[class~="QPushButton"]。ID .. _id-selector: 选择器
QPushButton#okButton匹配所有对象名称为
okButton的QPushButton实例。后代选择器
QDialog QPushButton匹配所有作为
QDialog后代(子代、孙代等)的QPushButton实例。子选择器
QDialog > QPushButton匹配所有作为
QDialog直接子元素的QPushButton实例。
子控件¶
为了样式化复杂的控件,需要访问控件的子控件,例如QComboBox的下拉按钮或QSpinBox的上下箭头。选择器可能包含子控件,这使得可以将规则的应用程序限制到特定的控件子控件。例如:
QComboBox::drop-down { image: url(dropdown.png) }
上述规则样式化了所有QComboBox的下拉按钮。尽管双冒号(::)语法让人联想到CSS3伪元素,但Qt子控件在概念上与这些不同,并且具有不同的级联语义。
子控件总是相对于另一个元素——参考元素进行定位。这个参考元素可以是小部件或另一个子控件。例如,::drop-down 的 QComboBox 默认放置在 QComboBox 的 Padding 矩形的右上角。默认情况下,::drop-down 放置在 ::drop-down 子控件的内容矩形的中心。请参阅下面的 可样式化小部件列表,了解用于样式化小部件的子控件及其默认位置。
可以使用subcontrol-origin属性来更改要使用的原始矩形。例如,如果我们希望将下拉菜单放置在QComboBox的边距矩形中,而不是默认的内边距矩形中,我们可以指定:
QComboBox { margin-right: 20px; } QComboBox::drop-down { subcontrol-origin: margin; }
下拉菜单在边距矩形内的对齐方式通过subcontrol-position属性进行更改。
width 和 height 属性可用于控制子控件的大小。请注意,设置 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的菜单指示器子控件以获取实际示例。
注意
对于复杂的部件,如QComboBox和QScrollBar,如果自定义了一个属性或子控件,所有其他属性或子控件也必须进行自定义。
伪状态¶
选择器可能包含伪状态,这些伪状态表示根据小部件的状态限制规则的适用。伪状态出现在选择器的末尾,中间用冒号(:)分隔。例如,当鼠标悬停在QPushButton上时,以下规则适用:
QPushButton:hover { color: white }
伪状态可以使用感叹号运算符进行否定。例如,当鼠标没有悬停在QRadioButton上时,以下规则适用:
QRadioButton:!hover { color: red }
伪状态可以链式使用,在这种情况下隐含了逻辑与的关系。例如,以下规则适用于当鼠标悬停在一个选中的QCheckBox上时:
QCheckBox:hover:checked { color: white }
否定伪状态可能出现在伪状态链中。例如,当鼠标悬停在一个未被按下的QPushButton上时,以下规则适用:
QPushButton:hover:!pressed { color: blue; }
如果需要,可以使用逗号运算符表示逻辑OR:
QCheckBox:hover, QCheckBox:checked { color: white }
伪状态可以与子控件组合出现。例如:
QComboBox::drop-down:hover { image: url(dropdown_bright.png) }
请参阅下面的伪状态列表部分,了解Qt小部件提供的伪状态列表。
冲突解决¶
当多个样式规则指定了相同属性但具有不同值时,就会发生冲突。考虑以下样式表:
QPushButton#okButton { color: gray } QPushButton { color: red }
两条规则都匹配名为 QPushButton 的实例 okButton,并且在 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),并且在color属性上存在冲突。由于QPushButton继承自QAbstractButton,可能会让人误以为QPushButton比QAbstractButton更具体。然而,对于样式表的计算,所有类型选择器具有相同的特异性,最后出现的规则优先。换句话说,color被设置为gray,适用于所有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样式表进行的字体和调色板更改将表现得好像用户手动调用了样式表所针对的所有QWidget上的相应setPalette()和setFont()方法。
样式表所做的更改会被传播。在更改发生时,它们会被一次性推送到所有匹配该样式表的小部件。
通过调用
setPalette()或setFont()所做的更改是继承的。它们被所有现有的和未来的子对象继承,其中相应的画笔或字体尚未被显式设置。
C++命名空间中的小部件¶
类型选择器可用于为特定类型的小部件设置样式。例如,
class MyPushButton(QPushButton): # ... # ... qApp.setStyleSheet("MyPushButton { background: yellow; }")
Qt样式表使用小部件的QObject::className()来确定何时应用类型选择器。当自定义小部件位于命名空间内时,QObject::className()返回<命名空间>::<类名>。这与子控件的语法冲突。为了解决这个问题,当对命名空间内的小部件使用类型选择器时,我们必须将::替换为--。例如,
ns = { class MyPushButton(QPushButton): # ... # ... qApp.setStyleSheet("ns--MyPushButton { background: yellow; }")
设置QObject属性¶
从4.3及以上版本开始,任何可设计的Q_PROPERTY都可以使用qproperty-<属性名称>语法进行设置。
例如,
MyLabel { qproperty-pixmap: url(pixmap.png); } MyGroupBox { qproperty-titleColor: rgb(100, 200, 100); } QPushButton { qproperty-iconSize: 20px 20px; }
如果属性引用了一个用Q_ENUM声明的枚举,你应该通过名称引用其常量,而不是它们的数值。
注意
请谨慎使用qproperty语法,因为它会修改正在绘制的部件。此外,qproperty语法仅在部件被样式修饰时评估一次。这意味着任何尝试在伪状态(如QPushButton :hover)中使用它们的操作都将无效。