注意:
- 本文为个人学习总结,不对的地方还请各位指出;
- 谢绝任何形式的转载!
World Wide Web Consortium (W3C) 将网页主要分为三个组成部分:
- 结构(Structure) → HTML
- 表现(Presentation) → CSS
- 行为(Behavior) → JavaScript
一个设计良好的前端页面中,这三个部分应该相互分离又各司其职,这不管对于项目的开发还是后期维护都会更加友好。
在Qt中,结构、表现和行为可分别对应于.ui、.qss、c++(或其他)。Qt中的样式表有两种存在形式:
1).qss文件,通过程序将其设置到程序中(类似于web中的.css文件,外联样式);
2)直接写在.ui文件中(可在QDesigner中编写,类似于web中的行内样式)
在前端设计中,盒模型一个非常重要的概念。所有的部件都被视为有4个同心矩形的盒子(box),如图:
- content rectangle:绘制窗口部件的内容的区域,如文字,图片;
- padding rectangle:包围content rectangle,由padding属性指定填充操作,主要是窗口部件内容与边缘线(border)之间的空隙,由top,right,bottom和left设置他的大小;
- border rectangle:包围padding rectangle,为边界预留空间,可认为是窗口的外框线;
- margin rectangle:最外面的矩形,主要是负责与其他窗口部件间的距离。
Qt样式表的语法规则与HTML、CSS基本相同。
在qss中,一个样式由选择器(selector)和声明(declaration)组成。其中,选择器确定了样式的作用对象,声明确定了作用于该对象的确切样式。如:
QPushButton
{
color: red
}
在这段代码中,QPushButton为选择器(selector),{ color: red }为样式声明(declaration)。这段代码的作用是将所有QPushButton及其子类的前景色(文本颜色)设置为红色。
可以同时对多个对象设置样式,用逗号(,)来分隔选择器(selector)。如:
QPushButton, QLineEdit, QComboBox
{
color: red
}
这与下面这段代码的作用是相同的:
QPushButton
{
color: red
}
QLineEdit
{
color: red
}
QComboBox
{
color: red
}
声明(declaration)部分是由一个由多个<属性: 值>(键值对)组成的列表,由大括号({})包围并由分号(;)分隔开。如:
QPushButton
{
color: red;
background-color: white
}
Qss中支持CSS2中定义的所有选择器(selector),下表为部分最常用的选择器(selector)。
选择器(selector) | 举例 | 说明 |
---|---|---|
通配选择器 | * | 匹配所有部件 |
类型选择器 | QPushButton | 匹配所有QPushButton及其子类的实例 |
属性选择器 | QPushButton[flat=“false”] | 匹配所有flat属性为false的QPushButton实例 |
类选择器 | .QPushButton | 匹配所有QPushButton的实例,但不包含其子类的实例 |
ID选择器 | QPushButton#okButton(或直接写#okButton) | 匹配所有object name(id)为okButton的QPushButton实例 |
后代/包含选择器 | QDialog QPushButton | 匹配所有QDialog中包含的所有QPushButton部件的实例,包括子、孙、子子孙孙部件 |
子选择器 | QDialog > QPushButton | 匹配QDialog的直接子控件QPushButton的实例 |
Qss中为复杂的部件定义了很多子控件,方便对于这些复杂部件的样式进行控制。如QComboBox的下拉按钮,以及QSpinBox的上下箭头。如:
QComboBox::drop-down
{
image: url(dropdown.png)
}
这段代码设置了所有QComboBox的下拉按钮的样式。双冒号(::)语法让人想起CSS3中的伪元素,但这在Qt中的概念不同,并且具有不同的级联语义。
可以使用subcontrol-origin属性来更改部件在其父部件中绘制的参考矩形,默认在padding的矩形中绘制。subcontrol-origin有margin、border、padding、content这4个值可供选择(对应盒模型中的4个矩形)。例如,我们想将drop-down子控件设置为参考margin矩形而不是默认的padding矩形来绘制,我们可以定义:
QComboBox
{
margin-right: 20px;
}
QComboBox::drop-down
{
subcontrol-origin: margin;
}
更近一步的例如,对一个QComboBox设置如下基本样式:
QComboBox
{
margin: 20px;
padding:20px;
border:20px solid blue;
}
然后分别设置其子控件::drop-down的属性subcontrol-origin为margin、border、padding、content:
QComboBox::drop-down
{
subcontrol-origin: margin/border/padding/content;
}
通过上一节内容我们知道,可以使用subcontrol-origin属性设置子控件的绘制矩形区域,如margin rectangle。但是子控件一般不会与该矩形区域一样大,那么这时候子控件究竟应该绘制在哪个区域呢?答案是我们可以通过subcontrol-position属性来指定子控件的绘制位置,但不同子控件的默认绘制位置也是不同的。subcontrol-position属性在水平和垂直方向分别有left、center、right和top、center、bottom这几个值可以选择。
如,我们在上一节样式表的基础上为子控件添加subcontrol-position属性并分别设置水平方向为left、center、right这3个值(注意下拉按钮在水平方向位置的变化):
类似于CSS,在QSS中也有相对位置(position: relative)和绝对位置(position: absolute)的概念。
可以用于改变子控件相对于其原始位置的偏移量。如,可用于实现点击按钮时的按下效果
QComboBox::down-arrow
{
image: url(down_arrow.png);
}
QComboBox::down-arrow:pressed
{
position: relative;
top: 1px;
left: 1px;
}
可以实现子控件的位置和大小随着参考部件的改变而改变。
选择器可以包含伪状态,用于根据部件的状态变换相应的样式。伪状态位于选择器的末尾,中间用冒号(:)隔开。
常用的伪状态有鼠标悬停(hover)、选中(checked)、按下(pressed)等。如下代码设置QPushButton在鼠标滑过时前景(文本)变为白色:
QPushButton:hover
{
color: white
}
伪状态可以用感叹号(!)来表示否定。如:
QPushButton:!hover
{
color: white
}
伪状态可以连接使用,这种情况下相当于隐含了一个逻辑“与”操作。如:
QCheckBox:hover:checked
{
color: white
}
否定的伪状态也可以连接使用。如:
QPushButton:hover:!pressed
{
color: blue;
}
如果需要,也可以使用逗号操作来表示逻辑“或”。如:
QCheckBox:hover, QCheckBox:checked
{
color: white
}
伪状态也可以与子控件组合。如:
QComboBox::drop-down:hover
{
image: url(dropdown_bright.png)
}
当样式中用不同的值指定了同一个属性时,就会出现冲突。如:
QPushButton#okButton
{
color: gray
}
QPushButton
{
color: red
}
这两个规则都匹配了名为名为okButton的QPushButton实例,且color属性存在冲突。要解决这一冲突,我们必须考虑选择器(selector)的特殊性。在上面的示例中,QPushButton#okButton被认为比QPushButton更具体,因为他具体到某一对象,而不是所有类的实例。
同样的,具有伪状态的选择器比不指定伪状态的选择器更具体。如,以下样式表指定了当鼠标悬停在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更具体。但是,对于样式表的计算来说,所有的类型选择器都具有相同的特异性,并且最后出现的规则优先。换句话说,以上样式表会将所有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 */
QSS可以在QApplication、父部件和子控件上设置。任意部件上的有效样式表是通过组合设置在其祖先(父部件、祖父部件等)及QApplication中的样式得到的。
当冲突出现时,部件自身的样式表的优先级要高于任何由祖先部件集成过来的样式表。同时,父部件样式表的优先级要高于祖父部件的样式表。
比如,首先,我们在QApplication上设置样式表:
qApp->setStyleSheet("QPushButton { color: white }");
然后我们对QPushButton的对象设置样式表:
myPushButton->setStyleSheet("* { color: blue }");
这样写的结果是:QPushButton样式表强制QPushButton(及其任何子控件)显示蓝色文字。
在经典的CSS中,当一个部件的字体和颜色等没有被明确设置时,它会从父级部件继承。但在QSS中,部件不会自动丛父级部件继承字体或颜色这些属性。
如,QGroupBox中包含QPushButton:
qApp->setStyleSheet("QGroupBox { color: red; } ");
子控件QPushButton没有一个明确的颜色设置,因此子控件QPushButton不会继承其父部件QGroupBox的颜色,而是显示系统的默认颜色。如果要同时设置QGroupBox及其子控件的颜色,可以这样写:
qApp->setStyleSheet("QGroupBox, QGroupBox * { color: red; }");
与之相比,也可以通过QWidget::setFont()和QWidget::setPalette()来为子控件设置字体和颜色。
类型选择器可用来设置某一特定类型部件的样式。如:
class MyPushButton : public QPushButton
{
// ...
}
// ...
qApp->setStyleSheet("MyPushButton { background: yellow; }");
QSS使用部件的QObject::className()来确定何时应用类型选择器。当自定义的部件在命名空间中时,QObject::className()会返回::。这与子控件的语法冲突。为了解决这个问题,当命名空间内的部件使用类型选择器时,必须更换”::” 为 “–”。 例如:
namespace ns
{
class MyPushButton : public QPushButton
{
// ...
}
}
// ...
qApp->setStyleSheet("ns--MyPushButton { background: yellow; }");
自从Qt4.3以后,任何可设计的Q_PROPERTY都可以通过qproperty-语法来设置。如:
MyLabel
{
qproperty-pixmap: url(pixmap.png);
}
MyGroupBox
{
qproperty-titleColor: rgb(100, 200, 100);
}
QPushButton
{
qproperty-iconSize: 20px 20px;
}
参考资料
- https://doc.qt.io/qt-5/stylesheet-syntax.html