Qt QSS

文章目录

  • 两个特殊的选择器
    • 子控件选择器
    • 伪类选择器
  • 没有选择器的情况
  • 选择器的匹配规则
  • 层叠性
  • 优先级
  • 优先级判断的三种方式
  • 优先级权重


文章逐渐转移至wx公众号:Qt历险记

两个特殊的选择器

子控件选择器

格式
类型选择器::子控件{ 属性: 值; }
类选择器::子控件{ 属性: 值; }
表示对类型选择器或类选择器指定的所有控件的子控件设置样式;
Qt 官方说明

为了样式化你的复杂 widget,很有必要使用 widget 的 subcontrol,比如 QComboBox 的 drop-down 部分或者是 QSpinBox 的上和下箭头。选择器也许会包含 subcontrols 用于限制 widget的 subcontrols, 举个例子:

QComboBox::down-arrow{
image: url(:/res/arrowdown.png); // 下拉图片
}

上述规则样式化所有 QComboBox 的 drop-down 部分,虽然双冒号(::)让人联想到 CSS3 的伪元素语法,但是 Qt 的 Sub-Controls 跟它是不一样的。

Sub-Controls 始终相对于另一个元素来定位–一个参考元素。这个参考元素可以是一个Widget 又或者是另一个 Sub-Control。举个例子,QComboBox 的::drop-down 默认被放置于QComboBox 的 Padding rectangle(盒子模型)的右上角。::drop-down 默认会被放置于另一个::drop-down Sub-Control 的中心。查看可样式化的 Widget 列表以了解更多使用 Sub-Control来样式化 Widget 和初始化其位置的内容。源 rectangle 可以使用 subcontrol-origin 来改变。举个例子,如果我们想要把 drop-down 放置于 QComboBox 的 margin rectangle 而不是默认的 Padding rectangle,我们可以像下面这样指定:


QComboBox {
margin-right: 20px;
}
QComboBox::drop-down {
subcontrol-origin: margin;
}

drop-down 在 Margin rectangle 内的排列方式可以由 subcontrol-position 来改变.
width 和 height 属性可以用来控制 Sub-control 的 size.需要注意的是,设置了 image 就隐式的设置了 Sub-control 的 size 了。

相对定位方案(position:relative),允许 Sub-Control 的位置从它的初始化位置作出偏移。
举个例子,当 QComboBox 的 drop-down 按钮被 pressed 时,我们也许想要那个箭头作出位移以显示一种“pressed”的效果,为了达到目标,我们可以像下面那样指定。

QComboBox::down-arrow {
image: url(down_arrow.png);
}
QComboBox::down-arrow:pressed {
position: relative;
top: 1px; left: 1px;
}

绝对定位方案(position : absolute),使得 Sub-control 的 position 和 size 基于其参考元素而改变。

一旦定位,它们将会与 widget 同等对待并且可以使用盒子模型来样式化。
查看 Sub-Control 列表以了解那些 sub-control 是被支持的,并且可以查看自定义QPushButton 的菜单指示器 Sub-Control 来了解一个实际的使用例子。

注意:像 QComboBox 和 QScrollBar 这样的复杂部件,如果 sub-control 的一项属性是自定义的,那么其他所有的属性跟 sub-control 也都应该自定义。

伪类选择器

格式
类型选择器:状态{ 属性: 值; }
类选择器:状态{ 属性: 值; }
表示对类型选择器或类选择器指定的所有控件设置它在指定状态时的样式.

Qt 官方说明
选择器也许会包含基于 widget 的 state 的程序限制规则的伪状态。伪状态以冒号(:)作为分隔紧跟着选择器。举个例子,下面的规则在鼠标悬浮在 QPushButton 的上方时生效:

QPushButton:hover { color: white }
伪状态可以使用感叹号进行取反,下面一条规则在鼠标没有悬浮在 QRadioButton 上方时生效:

QRadioButton:!hover { color: red }

伪状态可以链接,在这样的情况下,隐式地包含了逻辑与。举个例子,下面一条规则在鼠标悬浮到一个已 check 的 QCheckBox 上时生效:

QCheckBox:hover:checked { color: white }
伪状态的取反也可以出现在伪状态链中,举个例子,下面的规则在鼠标悬浮到一个没有被press 的 QPushButton 上时生效:

QPushButton:hover:!pressed { color: blue; }
如果有需要,可以使用逗号来表示逻辑或, 即并集选择器
QCheckBox:hover, QCheckBox:checked { color: white }
伪状态可以与 subcontrol 组合使用,举个例子:
QComboBox::drop-down:hover { image: url(dropdown_bright.png) }

没有选择器的情况

如果在 c++的代码中直接调用控件对象的 setStyleSheet 函数来设置样式,但样式中没有任何选择器, 例如下面这样

pBtn1->setStyleSheet(“color: green;”);
即使这种写法可以生效, 但它不符合语法规则, 因此不推荐使用.
经过测试, 这样的语句被忽略的选择器相当于通用选择器或下面例子中的选择器, 假如 pBtn1 是一个 QPushButton 对象的指针, 那么这条语句等价于

pBtn1->setStyleSheet(“QPushButton, QPushButton *{color: green;}”);

选择器的匹配规则

这里用户名输入框是一个 QComboBox 对象, 密码输入框是一个 QLineEdit 对象, 它们的父控件是一个 QDialog, 有这样一个需求:给这两个输入框设置相同的边框属性: 1 个像素宽的蓝色实线框, 为了方便更改风格, 我有一个 css 文件, 将所有样式都写在了这个文件里, 这时, 观察发现, 这两个控件都是QDialog的子控件,于是可以用后代选择器或者子元素选择器, 如下:

第一种:

QDialog QComboBox,QLineEdit{
border:1px solid blue;
}

第二种:

QDialog>QComboBox,QLineEdit{
border:1px solid blue;
}

当我写完并运行程序后, 发现无论采用哪种写法 QComboBox 是正常的, 但是我的程序界面中, 其他所有的 QLineEdit 的边框都变成了 1 个像素款的蓝色实线框, 而这并不是我想要的效果.

因此对于上面的现象, 我们很容易得出结论: 多个选择器组合使用时, 它们的结合方向是自右向左, 而不是我们认为的自左向右.也就是说, 这两个选择器分别被理解为(QDialogQComboBox), QLineEdit 和 (QDialog>QComboBox),QLineEdit.

正确的写法应该是

QDialog QComboBox, QDialog QLineEdit{
border:1px solid blue;
}

QDialog>QComboBox, QDialog>QLineEdit{
border:1px solid blue;
}

QSS 的特性

层叠性

qss 的语法来源于 css, 而 css 的全称是 Cascading StyleSheet, 翻译过来叫做层叠样式表,也叫级联样式表, 本文中一律使用层叠样式表.

层叠性是 css 处理冲突的一种能力.

只有在多个选择器匹配到同一个控件时才会发生层叠性, 如下面的例子:
pBtn1->setStyleSheet(“QPushButton{color: blue;}”);
pBtn1->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; }”);
注意 QGroupBox 和*之间的空格.
与此相反,使用 QWidget::setFont()可以设置字体包括孩子的字体,使用 QWidget::setPalette()可以设置调色板包括孩子的调色板。

如果想要字体和调 色板被孩子继承 , 可以给 QApplication 设 置
Qt::AA_UseStyleSheetPropagationInWidgetStyles(Qt5.7 加入)属性, 例如:
QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true);

优先级

为什么要有优先级?
当一个控件被多个选择器选中并且设置了相同的属性(值不同)时, 不能仅仅根据设置样式语句出现的先后顺序进行层叠, 那么控件的样式如何确定,于是引出了选择器的优先级问题。

一般通过下面两步进行选择器优先级的判定.

第一步:设置方式所产生的优先级问题

在 CSS 中, 有如下层叠优先级规则:
内联样式 > 内部样式 > 外部样式 > 浏览器缺省

而在 Qss 中, 这个规则表现为:
给控件直接设置的样式 > 给 QApplication 设置的样式

就是说, 调用控件的 setStylesheet 设置的样式的优先级永远高于给 QApplication 设置的样式, 即使 QApplication 中的选择器优先级更高

一般而言,选择器越特殊,它的优先级越高。也就是选择器指向的越准确,它的优先级就越高。

优先级判断的三种方式

  1. 间接选中

间接选中就是指继承, 也就是在 Qt5.7 及以上版本, 程序中给 QApplication 对象设置了Qt::AA_UseStyleSheetPropagationInWidgetStyles 属性时, 才会有间接选中.
如果是间接选中,那么最终的样式就是离目标最近的那个, 这里的近指的是两个控件的父子关系. 例如一个 QPushButton 对象被布局在 QGroupBox 中, 而 QGroupBox 又被布局在 QWidget 中, 此时如果给 QGroupBox 和 QWidget 都设置了 color 属性的颜色, 那么无论设置顺序如何, QPushButton 的前景色总是表现为 QGroupBox 设置的颜色, 因为QGroupBox 显然是离 QPushButton 最近的那一个.

  1. 相同选择器(直接选中)

如果都是直接选中, 并且都是同类型的选择器, 那么写在后面的样式会覆盖掉前面的样式, 例如

pBtn1->setStyleSheet(“QPushButton{color: green;}”);
pBtn1->setStyleSheet(“QPushButton{color: blue;}”);
显而易见, pBtn1 的前景色是蓝色.
3. 不同选择器(直接选中)

如果都是直接选中, 并且不是相同类型的选择器, 那么就会按照选择器的优先级来层叠.
具体的优先级如下:
Id > 类 > 类型 > 通配符 > 继承 > 默认

优先级权重

为什么会有优先级权重?
当多个选择器混合在一起使用时, 我们可以通过计算权重来判断谁的优先级最高,从而确定控件的样式.

注意点:

只有选择器是直接选中控件时才需要计算权重,否则直接选择器高于一切间接选中的选择器

优先级权重的计算方式:

  1. 计算选择器中的 id 选择器数量[=a]
  2. 计算选择器中类选择器的数量+属性选择器的数量[=b]
  3. 计算选择器中类型选择器的数量[=c]
  4. 忽略子控件选择器
    串联这三个数字 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: gray; }
两条规则都应用于 QPushButton 的实例(因为 QPushButton 继承于 QAbstractButton)并且冲突于 color 属性。因为 QPushButton 继承于 QAbstractButton,这让人不禁想到 QPushButton比 QAbstractButton 更特殊。然而,对于样式表的运算,所有的类型选择器都具有同等的特殊性,并且出现在更后面的规则优先级更高。换句话说,QAbstractButton 的 color 会被设置成灰色,包括 QPushButton。如果我们确实想要 QPushButton 字体颜色设置为红色,我们总是可以使用重新排列样式表规则顺序的方式实现。为确定规则的特殊性,Qt 样式表跟随 CSS2 规范

​一个选择器的特殊性由下面的方式计算:

  • 计算选择器中 ID 属性的数量[=a]
  • 计算选择器中其他属性和伪类的数量[=b]
  • 计算选择器中元素名字的数量[=c]
  • 忽略伪原素[如:subcontrol]

你可能感兴趣的:(Qt5,界面开发工程师课程,qt,开发语言,ui,qss,设计)