第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一模一样。

注意:QSS不支持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;
}
核心要点:子控件选择器让你能深入到Qt控件的「骨骼」里做定制。但要注意,不是所有控件都暴露了子控件。常用的有: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选择器的体系梳理了一遍:

QSS选择器体系 QSS选择器 类型选择器 ID选择器 子控件选择器 QPushButton { } QLineEdit { } #loginButton { } #titleLabel { } ::drop-down ::handle ::indicator 选择器组合使用 QComboBox::drop-down:hover { background: #2980b9; } 类型 + 子控件 + 伪状态 = 精准定位

3.8 选择器最佳实践

  1. 优先用ID选择器定位关键控件,避免全局样式污染。
  2. 类型选择器用于统一风格,比如所有 QLineEdit 的边框。
  3. 子控件选择器只在需要定制复合控件内部时使用,别滥用。
  4. 伪状态一定要配合交互反馈,比如 :hover、:pressed。
  5. 样式文件统一放在 .qss 文件里,用 setStyleSheet 加载,别到处写内联。
小技巧:调试QSS时,可以在代码里临时加个 border: 1px solid red; 看看控件实际占了多少空间。我经常用这招排查布局问题。