QSS语法概述
- 一、语法规则
- 二、选择器类型
- 三、子控件
- 四、伪态
- 五、冲突解决
- 六、样式层叠
- 七、样式继承
- 八、含命名空间样式设置
- 九、QObject 属性设置
概述:
- QSS也叫Qt样式表,Qt样式表术语和语法规则几乎与HTML CSS的术语和语法规则相同。如果已经了解CSS,那就可能可以快速浏览本节。
一、语法规则
QSS 语法非常简单。样式规则由选择器和声明组成。
- 选择器指定哪些控件受规则影响
- 声明指定应该在这个控件设置哪些属性。
就像下面:
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 }
样式规则的声明部分是一组属性:值对,用大括号({})括起来,用分号分隔。例如:
QPushButton { color: red; background-color: white }
控件的这些属性我到时候会在后面一篇博客里面记录一下。
二、选择器类型
选择器里面包含了子控件、伪态。
他们直接的关系是
选择器->子控件->伪态的,就行下面
QComboBox#oneCombox::drop-down:hover { image: url(dropdown_bright.png) }
Qt样式表支持 CSS2 中定义的所有选择器。
下表总结了最有用,也是最常用的选择器类型。
用得最多的就是:类型选择器、ID 选择器、通配符选择器
选择器名 | 例子 | 解释 |
---|---|---|
通配符选择器 | * | 匹配所有继承至QWidget的控件 |
类型选择器 | QPushButton | 匹配QPushButton及其子类的实例。 |
属性选择器 | QPushButton[flat=“false”] | 匹配非平的QPushButton实例。你可以使用这个选择器来测试任何支持QVariant::toString()的Qt属性(详细信息请参阅toString()函数文档)。此外,还支持特殊的class属性,用于表示类的名称。这个选择器也可以用来测试动态属性。有关使用动态属性进行自定义的更多信息,请参阅使用动态属性进行自定义。除了=,你还可以使用~=来测试QStringList类型的Qt属性是否包含给定的QString。警告:如果Qt属性的值在样式表设置之后发生了变化,可能需要强制重新计算样式表。实现这一点的一种方法是取消设置样式表并再次设置它。 |
类选择器 | .QPushButton | 匹配QPushButton的实例,但不匹配它的子类。和 类型选择器有区别。这相当于*[class~=“QPushButton”]。 |
ID 选择器 | QPushButton#okButton | 匹配所有对象名为okButton的QPushButton实例。也就是ObjectName 是 okButton,最常用 |
后代选择器 | QDialog QPushButton | 匹配QDialog的所有后代(子、孙辈等)QPushButton实例。匹配所有子代 |
后代选择器 | QDialog > QPushButton | 匹配QDialog的直接子QPushButton的所有实例。也即是只匹配子一代 |
这里的 属性选择器的属性 怎么看哈,其实可以在帮助文档里面查某一个类有哪些属性,属性是干嘛的,就行下面的QPushButton的属性,有flat 之类的,还跟页面相关。
三、子控件
因为某些复杂窗口组件的样式在父控件是设置不了的,就必须访问窗口组件的子控件,例如 QComboBox的 下拉按钮或QSpinBox的上下箭头。选择器可能包含子控件,使得可以将规则的应用直接设置限制到特定的控件 子控件。例如:
QComboBox::drop-down { image: url(dropdown.png) }
上面的规则为所有QCombobox的下拉按钮设置样式。虽然双冒号(::)的语法让人想起CSS3伪元素,但Qt子控件在概念上与它们有所不同,并且具有不同的级联语义。
子控件总是相对于另一个元素(引用元素)定位。这个引用元素可以是部件或另一个子控件。例如,默认情况下,QComboBox的::下拉框被放置在QComboBox的内边距矩形的右上角。默认情况下,::下拉位于::下拉子控件的内容矩形的中心。请参阅下面的可设置样式的窗口组件列表,以获取用于设置窗口组件样式及其默认位置的子控件。
可以使用subcontrol-origin属性更改要使用的原点矩形。例如,如果想把下拉框放在QComboBox的外边距矩形中,而不是默认的内边距矩形中,可以指定:
QComboBox {
margin-right: 20px;
}
QComboBox::drop-down {
subcontrol-origin: margin;
}
使用subcontrol-position属性更改下拉框在外边距矩形内的对齐方式。
width和height属性可以用来控制子控件的大小。请注意,设置图像会隐式地设置子控件的大小。这就需要手动设置那个子控件大小
相对定位方案(position: relative)允许子控件的位置偏离其初始位置。例如,当QComboBox的下拉按钮被按下时,我们可能希望其中的箭头被偏移,以产生“按下”的效果。为此,我们可以指定:
QComboBox::down-arrow {
image: url(down_arrow.png);
}
QComboBox::down-arrow:pressed {
position: relative;
top: 1px; left: 1px;
}
绝对定位方案(position: absolute),允许子控件的位置和大小相对于参考元素进行更改。
一旦定位,它们就会被视为与窗口组件相同的部件,并可以使用box模型设置样式。
请参阅下面的子控件列表以获得支持的子控件列表,并自定义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) }
五、冲突解决
当多个样式规则用不同的值指定相同的属性时,就会产生冲突。在冲突时,基本原则就是越具体的对象的设置规则会覆盖低级别的
考虑下面的样式表:
QPushButton#okButton { color: gray }
QPushButton { color: red }
这两个规则都匹配名为okButton的QPushButton实例,并且颜色属性存在冲突。为了解决这个冲突,我们必须考虑选择器的特异性。在上面的例子中,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更具体。
但是,对于样式表计算,所有类型选择器都具有相同的特异性,最后出现的规则优先。换句话说,所有QAbstractButtons(包括QPushButtons)的颜色都设置为灰色。如果我们真的想让QPushButtons有红色文本,我们总是可以重新排列规则。
为了确定规则的特异性,Qt样式表遵循CSS2规范:
选择器的特异性计算公式如下:
- 计算选择器中ID属性的数量(= a)
- 计算选择器中其他属性和伪类的数量(= b)
- 计算选择器中元素名称的数量(= c)
- 忽略伪元素[即子控件]。
将a-b-c三个数字连在一起(在一个大基数的数字系统中)就具有了专一性。
下面的是用的 Html 里面的样式举例
* {} /* 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有子元素(这是不太可能的),样式表不会对它们产生影响。
注意Qt目前没有实现 important 功能,Qt只采用了一部分的功能。
七、样式继承
在经典的CSS中,当一个项的字体和颜色没有显式设置时,它会自动从父项继承。默认情况下,当使用Qt样式表时,控件 件不会自动从其父控件 件继承其字体和颜色设置。
例如,考虑QGroupBox中的QPushButton:
qApp->setStyleSheet("QGroupBox { color: red; } ");
QPushButton没有显式的颜色集。因此,它不继承其父QGroupBox的颜色,而是具有系统颜色。如果我们想在QGroupBox和它的子节点上设置颜色,我们可以这样写:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
相反,使用QWidget::setFont()和QWidget::setPalette()设置字体和面板会传播到子部件。
如果你想让字体和调色板传播到子部件,你可以设置Qt::AA_UseStyleSheetPropagationInWidgetStyles标志,像这样:
用法:
QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);
当控件 样式的字体和调色板传播被启用时,通过Qt样式表所做的字体和调色板更改将表现为用户在样式表所针对的所有QWidget上手动调用了相应的QWidget::setPalette()和QWidget::setFont()方法。如果这将在c++中引起传播,同时也会在样式表中引起传播,反之亦然。
八、含命名空间样式设置
类型选择器可用于为特定类型的部件设置样式。例如,
class MyPushButton : public QPushButton {
// ...
}
// ...
qApp->setStyleSheet("MyPushButton { background: yellow; }");
Qt样式表使用小部件的QObject::className()来确定何时应用类型选择器。当自定义小部件位于名称空间中时,QObject::className()返回<名称空间>::<类名>。这与子控件的语法冲突。为了克服这个问题,在名称空间中的小部件使用Type Selector时,必须将“::”替换为“- -”。例如,
namespace ns {
class MyPushButton : public QPushButton {
// ...
}
}
// ...
qApp->setStyleSheet("ns--MyPushButton { background: yellow; }");
九、QObject 属性设置
这个用的少,了解了解就好
从Qt 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声明的枚举,则应该通过名称引用其常量,即,而不是它们的数值。
其实就是说,用Priority.High 而不是用 0
enum Priority { High, Low, VeryHigh, VeryLow };
Q_ENUM(Priority)
注意:请谨慎使用qproperty语法,因为它会修改正在绘制的部件。此外,qproperty语法只会被求值一次,这意味着任何尝试在伪状态(如QPushButton:hover)下使用它们都将不起作用。