一、层叠性
qss的语法来源于css,而css的全称是Cascading StyleSheet,翻译过来叫做层叠样式表,也叫级联样式表。层叠性是css处理冲突的一种能力,只有在多个选择器匹配到同一个控件时才会发生层叠性,例如:
btn1->setStyleSheet("QPushButton{ color: blue; }");
btn1->setStyleSheet(".QPushButton{ color: green; }");
这两个选择器匹配到了同一个按钮,结果是后面的样式覆盖了前面的,这就是层叠现象。
二、继承性(Qt-Version>=5.7)
在典型的CSS中,如果一个标签的字体和颜色没有显示设置,它们会自动从其父亲获得。当使用Qt样式表时,控件不会从其父亲继承字体和颜色的设置(请注意,父亲和父类、孩子和子类都是不同的概念,不要搞混),例如,考虑一个QGroupBox内有一个QPushButton:
qApp->setStyleSheet("QGroupBox{ color: red; }");
QPushButton没有任何显示的color设置。因此,它会获得系统的颜色而不是从父亲继承color的值。如果我们要设置QGroupBox及其所有孩子的color,我们可以这样写:
qApp->setStyleSheet("QGroupBox,QGroupBox *{ color: red; }");
注意QGrouBox和*之间的空格。
与此相反,使用QWidget::setFont()可以设置字体包括孩子的字体,使用QWidget::setPalette()可以设置调色板包括孩子的调色板。
如果想要字体和调色板被孩子继承,可以给QApplication设置Qt::AA_UseStyleSheetPropagationInWidgetStyles(Qt5.7 加入)属性
,例如:
QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles,true);
三、优先级
当一个控件被多个选择器选中并且设置了相同的属性(值不同)时,不能仅仅根据设置样式语句出现的先后顺序进行层叠,那么控件的样式如果确定,于是引出了选择器的优先级问题。一般通过下面两步进行选择器优先级的判定。
第一步:设置方式所产生的优先级问题
在CSS中,有如下层叠优先级规则:
内联样式 > 内部样式 > 外部样式 > 浏览器缺省
而在Qss中,这个规则表现为:
给控件直接设置的样式 > 给QApplication设置的样式
就是说,调用控件的setStyleSheet设置的样式的优先级永远高于给QApplication设置的样式,即使QApplication中的选择器优先级更高。
第二步:样式表本身的优先级问题
当设置方式相同,且几个样式规则为同一个控件的同一个属性指定不同的值时,就产生了冲突。此时,如何层叠就要由选择器的优先级来确定。
一般而言,选择器越特殊,它的优先级越高。也就是选择器指向的越准确,它的优先级就越高。
优先级判断的三种方式
- 间接选中
间接选中就是指继承,也就是在Qt5.7及以上版本,程序中给QApplication对象设置了Qt::AA_UseStyleSheetPropagationInWidgetStyles
属性时,才会有间接选中。
如果是间接选中,那么最终的样式就是离目标最近的那个,这里的近指的是两个控件的父子关系。例如一个QPushButton对象被布局在QGroupBox中,而QGroupBox又被布局在QWidget中,此时如果给QGroupBox和QWidget都设置了color属性的颜色,那么无论设置顺序如何,QPushButton的前景色总是表现为QGroupBox设置的颜色,因为QGroupBox显然是离QPushButton最近的那一个。 - 相同选择器(直接选中)
如果都是直接选中,并且都是同类型的选择器,那么写在后面的样式会覆盖掉前面的样式,例如:
显而易见,btn1的前景色是蓝色。btn1->setStyleSheet("QPushButton{ color: green; }"); btn1->setStyleSheet("QPushButton{ color: blue; }");
- 不同选择器(直接选中)
如果都是直接选中,并且不是相同类型的选择器,那么就会按照选择器的优先级来层叠。
具体的优先级如下:ID > 类 > 类型 > 通配符 > 继承 > 默认
优先级权重
当多个选择器混合在一起使用时,我们可以通过计算权重来判断谁的优先级最高,从而确定控件的样式。
注意点:只有选择器时直接选中控件是才需要计算权重,否则直接选择器高于一切间接选中的选择器。
优先级权重的计算方式:
- 计算选择器中的id选择器数量[=a]
- 计算选择器中类选择器的数量+属性选择器的数量[=b]
- 计算选择器中类型选择器的数量[=c]
- 忽略子控件选择器
串联这三个数字a-b-c就得到优先级权重,数字越大优先级越高。
Qt官方关于冲突解决的说明
当几个样式规则为同一个属性指定不同的值时,就产生了冲突。请考虑下面的样式表:
QPushButton#okButton { color: gray; }
QPushButton { color: red; }
两条规则都匹配名为okButton的QPushButton实例并且冲突于颜色属性。为了解决冲突,我们必须考虑到选择器的特殊性。在上面的例子中,QPushButton#okButton被视为比QPushButton更特殊,因为它(通常)指向一个单一的对象,而不是QPushButton的所有实例。
相似的,指定了伪状态的选择器比没有指定伪状态的更特殊。从而,下面的样式表指明了当鼠标悬浮到QPushButton上方时其字体颜色应该为白色,而其余情况为红色:
QPushButton:hover { color: white; }
QPushButton { color:red; }
接下来看一个很有意思的:
QPushButton:hover { color: white; }
QPushButton:enabled { color: red; }
两个选择器都有相同的特殊性,所以当鼠标悬浮在一个enabled的按钮上时,第二条规则优先。如果在这种情况下我们想要文字变为白色,我们可以像下面那样重新排布一下样式规则:
QPushButton:enabled { color: red; }
QPushButton:hover { color: white; }
另外,我们可以使第一条规则更特殊一些:
QPushButton:hover:enabled { color: white; }
QPushButton:enabled { color: red; }
相似的问题出现在相互配合的类型选择器上。考虑以下情况:
QPushButton { color: red; }
QAbstractButton { color: gary; }
两条规则都应用于QPushButton的实例(因为QPushButton继承于QAbstractButton)并且冲突于color属性。因为QPushButton继承于QAbstractButton,这让人不禁想到QPushButton比QAbstractButton更特殊。然而,对于样式表的运算,所有的类型选择器都具有同等的特殊性,并且出现在更后面的规则优先级更高。换句话说,QAbstractButton的color会被设置成灰色,包括QPushButton。如果我们确实想要QPushButton字体颜色设置为红色,我们总是可以使用重新排列样式表规则顺序的方式实现。
为确定规则的特殊性,Qt样式表跟随CSS2规范
一个选择器的特殊性由下面的方式计算:
- 计算选择器中ID属性的数量[=a]
- 计算选择器中其它属性和伪类的数量[=b]
- 计算选择器中元素名字的数量[=c]
- 忽略伪元素[如:subcontrol]
串联这三个数字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*/
四、盒模型
4.1、什么是盒模型?
盒模型仅仅是一个形象的比喻,所有的widget都被看做是一个“盒子”,一个盒子包括:外边距,边框,内边距,和实际内容。它们可以看做是有包含关系的矩形,并且这种包含关系是固定不变的。如图所示:
- Margin(外边距):与其他盒子之间的距离;
- Border(边框):外边距与内边距之间的区域。边框有自己的颜色不会受到盒子的背景颜色影响;
- Padding(内边距):内容和边框之间的区域。会受到背景颜色的影响;
- Content(内容):盒子的内容,显示文本,图像或其他控件。
除了内容外,其他三个部分均不能单独设置颜色,智能设置其宽度,并且使以像素为单位。
4.2、盒模型中的宽度与高度
在属性中将要学到width,height两个属性,设置的均是盒子的内容的宽高,而我们在C++代码中的窗口的width与height指的是整个盒子的宽度与高度,这一点非常重要。
整个盒子的宽度应该等于:
左外边距+左边框+左内边距+内容宽度+右内边距+右边框+右外边距
同理,整个盒子的高度也是上下外边距,内边距和内容高度的和。