第3章 QSS基础与选择器:从语法到实战
QSS,说白了就是Qt版的CSS。我第一次接触时心想:这不就是换皮嘛?但真用起来才发现,坑比想象的多。这一章咱们就把QSS的语法和选择器彻底捋清楚。
3.1 QSS语法结构
QSS的语法极其简单,就两样东西:选择器和声明块。
QPushButton {
background-color: #3498db;
color: white;
border-radius: 6px;
padding: 8px 16px;
}
选择器告诉Qt「你要改谁」,声明块告诉Qt「改成什么样」。每条声明用分号结尾,属性名和值之间用冒号。嗯,跟CSS一模一样。
display、position 这些就别想了。Qt有自己的一套布局系统,样式表只管「画皮」,不管「骨架」。
我在项目中遇到过有人把 margin 写成 magin,结果查了半天。QSS解析器不会报错,写错了就静默忽略。所以,属性名拼写必须一字不差。
3.2 类选择器(Class Selector)
类选择器用 . 开头,匹配所有设置了特定 objectName 的控件。注意,这里的「类」不是C++类,而是Qt的 objectName。
/* 匹配所有 objectName 为 "myButton" 的控件 */
.myButton {
font-size: 14px;
font-weight: bold;
}
/* 匹配所有 QPushButton 实例 */
QPushButton {
border: 1px solid #ccc;
}
类选择器有两种写法:
| 写法 | 含义 | 示例 |
|---|---|---|
.objectName |
匹配所有 objectName 为该值的控件 |
.myBtn { color: red; } |
QPushButton.objectName |
匹配特定类型且 objectName 匹配的控件 |
QPushButton#myBtn { }(ID选择器写法) |
objectName。比如 ui->btnSubmit->setObjectName("submitButton");。这样样式表里直接用 #submitButton 定位,清晰又好维护。
3.3 ID选择器(ID Selector)
ID选择器用 # 开头,匹配 objectName 完全匹配的控件。它比类选择器更精确——一个ID在同一个窗口里应该唯一。
#submitButton {
background-color: #27ae60;
color: white;
border-radius: 8px;
padding: 10px 24px;
}
#cancelButton {
background-color: #e74c3c;
color: white;
border-radius: 8px;
padding: 10px 24px;
}
ID选择器和类选择器可以组合使用:
QPushButton#submitButton {
/* 只匹配 QPushButton 类型且 objectName 为 submitButton 的控件 */
font-size: 16px;
}
我曾经在项目里看到有人给十几个按钮都用同一个 objectName,然后用类选择器去匹配。结果样式全乱了。记住:ID选择器要求唯一性,类选择器可以复用。这是两个不同的设计思路。
3.4 子控件选择器(Sub-Control Selector)
这是QSS最强大的特性之一。有些控件是复合控件,比如 QComboBox 有下拉箭头,QSpinBox 有上下按钮,QScrollBar 有滑块和轨道。子控件选择器用 :: 来访问这些内部零件。
/* 修改下拉框的箭头按钮 */
QComboBox::drop-down {
width: 30px;
border-left: 1px solid #ccc;
}
/* 修改滚动条的滑块 */
QScrollBar::handle {
background-color: #3498db;
border-radius: 4px;
min-height: 20px;
}
/* 修改复选框的勾选标记 */
QCheckBox::indicator {
width: 18px;
height: 18px;
border: 2px solid #555;
border-radius: 3px;
}
QCheckBox::indicator:checked {
background-color: #2ecc71;
border-color: #27ae60;
}
子控件选择器还可以跟伪状态(pseudo-states)组合:
QScrollBar::handle:hover {
background-color: #2980b9;
}
QScrollBar::handle:pressed {
background-color: #1c6ea4;
}
QComboBox::drop-down、QSpinBox::up-button、QScrollBar::handle、QCheckBox::indicator、QHeaderView::section 等。
3.5 选择器优先级与层叠规则
当多个选择器匹配同一个控件时,谁说了算?QSS有一套优先级规则:
| 优先级 | 选择器类型 | 示例 |
|---|---|---|
| 最高 | 内联样式(通过 setStyleSheet 直接设置) |
button->setStyleSheet("color: red;"); |
| 高 | ID选择器 | #myButton { } |
| 中 | 类选择器 + 类型选择器 | QPushButton.myClass { } |
| 低 | 类型选择器 | QPushButton { } |
| 最低 | 通配选择器 | * { } |
优先级相同时,后定义的覆盖先定义的。嗯,跟CSS一样。
setStyleSheet 给单个控件设了内联样式,结果整个应用的样式表都不生效了。因为内联样式的优先级最高,它会覆盖所有外部样式表。所以,尽量少用内联样式,把样式统一放在一个 qss 文件里管理。
3.6 实战:用选择器重构一个登录界面
光说不练假把式。咱们用选择器来重构一个登录界面:
/* 全局样式 */
* {
font-family: "Microsoft YaHei", "Segoe UI", sans-serif;
font-size: 14px;
}
/* 主窗口背景 */
#loginWindow {
background-color: #f0f4f8;
}
/* 标题标签 */
#titleLabel {
font-size: 24px;
font-weight: bold;
color: #2c3e50;
margin-bottom: 20px;
}
/* 输入框 */
QLineEdit {
border: 2px solid #bdc3c7;
border-radius: 6px;
padding: 10px 14px;
background-color: white;
color: #2c3e50;
}
QLineEdit:focus {
border-color: #3498db;
}
/* 登录按钮 */
#loginButton {
background-color: #3498db;
color: white;
border: none;
border-radius: 8px;
padding: 12px 32px;
font-size: 16px;
font-weight: bold;
}
#loginButton:hover {
background-color: #2980b9;
}
#loginButton:pressed {
background-color: #1c6ea4;
}
/* 复选框 */
QCheckBox {
spacing: 8px;
}
QCheckBox::indicator {
width: 18px;
height: 18px;
border: 2px solid #bdc3c7;
border-radius: 3px;
}
QCheckBox::indicator:checked {
background-color: #2ecc71;
border-color: #27ae60;
}
你看,用ID选择器定位具体控件,用类型选择器统一风格,用伪状态处理交互反馈。这样写出来的样式表,结构清晰,维护起来也方便。
3.7 知识结构图
下面这张图把QSS选择器的体系梳理了一遍:
3.8 选择器最佳实践
- 优先用ID选择器定位关键控件,避免全局样式污染。
- 类型选择器用于统一风格,比如所有
QLineEdit的边框。 - 子控件选择器只在需要定制复合控件内部时使用,别滥用。
- 伪状态一定要配合交互反馈,比如
:hover、:pressed。 - 样式文件统一放在
.qss文件里,用setStyleSheet加载,别到处写内联。
border: 1px solid red; 看看控件实际占了多少空间。我经常用这招排查布局问题。