一、什么是QSS?
在 Qt 中,QSS(Qt Style Sheets) 是一种用于美化界面外观的技术,它的语法类似于 CSS(层叠样式表),但针对的是 Qt 的控件。QSS 可以让你在不改动控件代码的前提下,对 Qt 界面中的控件进行灵活、统一的样式定制。
QSS 可以控制 Qt 应用中的以下方面:
- 控件的背景颜色、文字颜色、边框、圆角、字体等;
- 控件的状态(如 hover、pressed、disabled)样式;
- 自定义控件的外观,打造现代化 UI;
- 控件的子控件样式(如 QScrollBar 的 handle、add-line、sub-line 等)。
二、QSS的基本语法
QSS 的语法基本与 CSS 相似,基本格式如下:
QPushButton {
background-color: #3498db;
color: white;
border: 1px solid #2980b9;
border-radius: 5px;
padding: 5px 10px;
}
QPushButton:hover {
background-color: #2980b9;
}
QPushButton:pressed {
background-color: #1abc9c;
}
上述 QSS 应用于 QPushButton,定义了其正常、鼠标悬浮、按下时的不同状态样式。
三、QSS 的使用方式
3.1 在代码中设置 QSS
QPushButton *btn = new QPushButton("Click me");
btn->setStyleSheet("background-color: red; color: white; border-radius: 5px;");
3.2 加载外部 QSS 文件
QFile file(":/styles/my_style.qss");
if (file.open(QFile::ReadOnly)) {
QString styleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(styleSheet); // 设置应用级样式
}
四、QSS中选择器的介绍和使用
4.1 Type Selector(类型选择器)
类型选择器是 QSS(Qt Style Sheets)中最基础也最常用的一类选择器,用于根据控件的类名(类型)来匹配并设置样式。它的语法非常简单,直接写控件的类型名即可。
语法格式:
QWidgetType {
/* 样式定义 */
}
其中 QWidgetType 是 Qt 控件的类名,例如:QPushButton, QLabel, QLineEdit 等。
示例一:设置所有按钮样式
QPushButton {
background-color: #3498db;
color: white;
border-radius: 5px;
padding: 6px 12px;
}
这个样式会应用到程序中所有的 QPushButton 控件上。
示例二:统一设置所有标签文字颜色
QLabel {
color: #2c3e50;
font-weight: bold;
}
无论标签显示在哪个位置,这条规则都会生效。
示例三:组合状态伪类
QPushButton:hover {
background-color: #2980b9;
}
QPushButton:pressed {
background-color: #1abc9c;
}
用于细化交互状态下的样式。
4.2 ID Selector(ID 选择器)
ID 选择器(又称对象名选择器)是 Qt Style Sheets(QSS)中用于精确匹配某个特定控件的选择器。通过设置控件的 objectName,可以为这个控件单独定义样式,而不会影响同类其他控件。
4.2.1 仅使用 ID(常见写法)
语法格式:
#objectName {
/* 样式定义 */
}
- # 表示按控件的 objectName 匹配;
- objectName 是通过 setObjectName(“name”) 设置的标识。
使用步骤:
在 QSS 中使用 ID Selector
#confirmBtn {
background-color: #27ae60;
color: white;
border-radius: 4px;
padding: 6px 12px;
}
设置控件的 objectName(C++ 中)
QPushButton *btn = new QPushButton("确定");
btn->setObjectName("confirmBtn");
这个样式只会应用到 objectName 为 confirmBtn 的按钮。
示例:多个按钮不同样式
#okButton {
background-color: #2ecc71;
}
#cancelButton {
background-color: #e74c3c;
}
QPushButton *okBtn = new QPushButton("OK");
okBtn->setObjectName("okButton");
QPushButton *cancelBtn = new QPushButton("Cancel");
cancelBtn->setObjectName("cancelButton");
结合状态伪类使用:
#okButton:hover {
background-color: #27ae60;
}
#cancelButton:pressed {
background-color: #c0392b;
}
4.2. 2 类型 + ID(更精确匹配)
语法格式:
QWidgetType#objectName {
/* 样式定义 */
}
表示只有 QWidgetType 类型,并且 objectName == “myButton” 才会应用此样式。
带类型限定的 ID 选择器:
/* 主窗口背景 */
QMainWindow {
background-color: #f0f2f5;
}
/* 标题标签 */
QLabel#titleLabel {
font-size: 20px;
font-weight: bold;
color: #2c3e50;
margin-bottom: 12px;
}
/* 输入框:搜索框 */
QLineEdit#searchInput {
background-color: white;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 6px 10px;
}
/* 确认按钮 */
QPushButton#confirmBtn {
background-color: #409EFF;
color: white;
border-radius: 4px;
padding: 6px 16px;
}
QPushButton#confirmBtn:hover {
background-color: #66b1ff;
}
QPushButton#confirmBtn:pressed {
background-color: #337ecc;
}
/* 取消按钮 */
QPushButton#cancelBtn {
background-color: #e6e6e6;
color: #606266;
border-radius: 4px;
padding: 6px 16px;
}
QPushButton#cancelBtn:hover {
background-color: #dcdcdc;
}
QPushButton#cancelBtn:pressed {
background-color: #c0c0c0;
}
对应控件设置代码(C++):
QPushButton* confirmBtn = new QPushButton("确认");
confirmBtn->setObjectName("confirmBtn");
QPushButton* cancelBtn = new QPushButton("取消");
cancelBtn->setObjectName("cancelBtn");
QLabel* titleLabel = new QLabel("欢迎使用系统");
titleLabel->setObjectName("titleLabel");
QLineEdit* searchInput = new QLineEdit;
searchInput->setObjectName("searchInput");
4.3 Class Selector(类选择器)
在标准的 CSS 中,“类选择器”是通过 . 来匹配 class 属性的,但在 Qt 的 QSS 中,“类选择器” 实际上是指“类型选择器”,用于匹配控件的类名(如 QPushButton、QLabel),而不是 CSS 那种 .className 的方式。
不过,为了方便理解和对比,有时也会口语上把下面这几种情况称作“类选择器”:
QSS 并不支持像 CSS 中的 .className 类似的“类名选择器”;Qt 的控件并没有 class 属性,也不能直接用 .className 匹配。
我们可以以继承自一个控件类型,例如 QPushButton,然后在 QSS 中写你自定义的类名。
// mybutton.h
class MyButton : public QPushButton {
Q_OBJECT
public:
explicit MyButton(QWidget* parent = nullptr);
};
MyButton {
background-color: #8e44ad;
color: white;
}
这样写就像 CSS 的 .my-button 一样作用于你所有的 MyButton 控件。
4.4 Universal Selector(通用选择器)
通用选择器(*) 是一种 匹配所有控件的选择器,用于为整个界面或某个控件的所有子控件设置统一的样式。
基本语法:
* {
font-family: "Microsoft YaHei";
font-size: 14px;
color: #2c3e50;
}
表示界面中所有控件都应用上述样式。
示例 1:全局统一字体和颜色
* {
font-family: "微软雅黑";
font-size: 13px;
color: #333333;
}
适合用于统一整个应用的控件风格,如设置字体、文字颜色等。
示例 2:限定某控件的所有子控件
QWidget * {
background-color: #f9f9f9;
}
这表示 QWidget 的所有子控件都使用背景色 #f9f9f9。
示例 3:配合更具体的规则使用
可以用 * 设置基础样式,再用其他选择器进行细化覆盖:
* {
font-size: 12px;
color: #555;
}
QLabel#titleLabel {
font-size: 18px;
font-weight: bold;
color: #2c3e50;
}
注意事项:
- * 的权重很低,更具体的选择器会覆盖它的效果;
- 不要用它设置所有控件的背景色,否则可能会导致控件嵌套层级混乱(特别是 QScrollArea、QGroupBox 等);
- 不推荐用 * 设置控件大小、边框、内边距等结构性样式,容易引发样式冲突;
4.5 属性选择器(Attribute Selector)
属性选择器允许你根据控件的某个Qt 属性值来选择性地应用样式。
基本语法:
控件类型[属性名="属性值"] {
/* 样式 */
}
自定义属性的使用建议:
- 使用 setProperty(“key”, “value”) 在代码中动态设置;
- 记得在修改后调用 style()->unpolish() 和 style()->polish() 使样式刷新(可选)。
unpolish() 和 polish() 的作用
在 Qt 中,style()->unpolish() 和 style()->polish() 主要用于强制刷新控件的样式,当我们修改了 setProperty() 之后,QSS 不能自动识别更改时,需要手动触发样式重新应用。
unpolish(QWidget *widget):
- 移除当前应用在控件上的 QSS 样式。
- 使控件恢复 Qt 默认样式(但不主动重绘)。
polish(QWidget *widget):
- 重新应用 QSS 样式。
- 如果控件的属性(如 setProperty())发生变化,必须调用 polish() 才能生效。
- 也会重新执行 paintEvent(),从而刷新 UI。
为什么需要 unpolish() + polish()?
- 直接使用 setProperty() 不会自动触发 QSS 更新,所以 QSS 需要手动刷新。
- setStyleSheet() 会刷新整个应用,性能消耗大。而 polish() 只刷新一个控件,性能更好。
例如:
myWidget->setProperty("level", "warning");
myWidget->style()->unpolish(myWidget);
myWidget->style()->polish(myWidget);
示例 1:控制是否启用的样式
QLineEdit {
background-color: white;
color: black;
}
QLineEdit:disabled {
background-color: #eee;
color: #aaa;
}
#include <QApplication>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
window.setWindowTitle("QLineEdit enabled 属性演示");
QLineEdit* edit = new QLineEdit;
edit->setPlaceholderText("请输入内容...");
QPushButton* toggleBtn = new QPushButton("启用/禁用");
QObject::connect(toggleBtn, &QPushButton::clicked, [=]() mutable {
edit->setEnabled(!edit->isEnabled());
});
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(edit);
layout->addWidget(toggleBtn);
window.setLayout(layout);
// 设置样式表
app.setStyleSheet(R"(
QLineEdit {
background-color: white;
color: black;
border: 1px solid #ccc;
padding: 4px;
}
QLineEdit:disabled {
background-color: #eee;
color: #aaa;
}
)");
window.show();
return app.exec();
}
当QLineEdit设置为disabled时,会自动匹配QSS 对应规则。即QLineEdit:disabled。
示例 2:QPushButton 设置自定义属性控制样式
设置一个属性:
QPushButton[level="danger"] {
background-color: #e74c3c;
color: white;
}
QPushButton[level="primary"] {
background-color: #409EFF;
color: white;
}
myButton->setProperty("level", "danger");
下面是一个 完整的 Qt 示例,展示如何使用 QSS 中的 属性选择器 来动态改变按钮的样式 :
QSS 样式文件:
QPushButton[level="primary"] {
color: white;
background-color: #3498db;
border: 2px solid #2980b9;
border-radius: 6px;
padding: 6px 12px;
font-size: 16px;
}
QPushButton[level="primary"]:hover {
background-color: #5dade2;
}
QPushButton[level="primary"]:pressed {
background-color: #2e86c1;
}
QPushButton[level="danger"] {
color: white;
background-color: #e74c3c;
border: 2px solid #c0392b;
border-radius: 6px;
padding: 6px 12px;
font-size: 16px;
}
QPushButton[level="danger"]:hover {
background-color: #ec7063;
}
QPushButton[level="danger"]:pressed {
background-color: #cb4335;
}
代码实现:
#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QFile>
#include <QStyle>
void updateButtonStyle(QPushButton* button, const QString& level) {
button->setProperty("level", level);
button->style()->unpolish(button);
button->style()->polish(button);
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
window.setWindowTitle("属性选择器示例");
QPushButton* btn = new QPushButton("点击切换风格");
btn->setFixedSize(200, 40);
// 初始设置属性
updateButtonStyle(btn, "primary");
// 切换属性值
QObject::connect(btn, &QPushButton::clicked, [=]() mutable {
QString current = btn->property("level").toString();
QString next = (current == "primary") ? "danger" : "primary";
updateButtonStyle(btn, next);
});
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(btn);
window.setLayout(layout);
// 加载样式表
QFile qss(":/res/qss_style.qss");
if (qss.open(QFile::ReadOnly)) {
app.setStyleSheet(qss.readAll());
}
window.show();
return app.exec();
}
输出结果:
下面是一个完整的 Qt 示例,演示如何通过按钮在深色模式和浅色模式之间切换 UI 主题:
要让主题设置全局生效,不仅影响 QWidget 主窗口,还影响所有子控件(如 QLabel, QLineEdit, QPushButton 等)
QSS 样式文件:
/* 浅色主题 */
*[theme="light"] {
background-color: #f9f9f9;
color: #000;
}
QLineEdit[theme="light"] {
background-color: white;
border: 1px solid #ccc;
color: #000;
}
QPushButton[theme="light"] {
background-color: #e0e0e0;
color: #000;
}
/* 深色主题 */
*[theme="dark"] {
background-color: #2c3e50;
color: #ecf0f1;
}
QLineEdit[theme="dark"] {
background-color: #34495e;
border: 1px solid #95a5a6;
color: #ecf0f1;
}
QPushButton[theme="dark"] {
background-color: #3b5998;
color: white;
}
优点:
- 无需对子控件逐一设置属性;
- 所有控件自动响应主题切换;
- 支持自定义属性驱动任意 QSS 控制逻辑。
代码实现:
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QFile>
#include <QStyle>
void applyTheme(QWidget* window, const QString& theme) {
window->setProperty("theme", theme);
window->style()->unpolish(window);
window->style()->polish(window);
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 主窗口
QWidget window;
window.setWindowTitle("主题切换示例");
// 设置初始主题
applyTheme(&window, "light");
// 控件
QLabel* label = new QLabel("请输入用户名:");
QLineEdit* edit = new QLineEdit;
QPushButton* switchBtn = new QPushButton("切换主题");
// 布局
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(label);
layout->addWidget(edit);
layout->addWidget(switchBtn);
window.setLayout(layout);
// 切换按钮事件
QObject::connect(switchBtn, &QPushButton::clicked, [&]() {
QString current = window.property("theme").toString();
QString next = (current == "light") ? "dark" : "light";
applyTheme(&window, next);
});
// 加载样式表
QFile file(":/res/qss_style.qss");
if (file.open(QFile::ReadOnly)) {
app.setStyleSheet(file.readAll());
}
window.show();
return app.exec();
}
输出结果:
4.6 Pseudo-class Selector(伪类选择器)
在 Qt 的 QSS(Qt Style Sheets)中,伪类选择器(Pseudo-class Selector) 是用于根据控件状态(如悬停、按下、选中、禁用等)来改变控件样式的强大工具。它类似于 CSS 中的伪类,例如 :hover、:checked 等。
常用的伪类选择器一览
示例:按钮的悬停与按下状态样式
QPushButton {
background-color: #3498db;
color: white;
border-radius: 5px;
padding: 6px 12px;
}
QPushButton:hover {
background-color: #2980b9; /* 鼠标悬停变深 */
}
QPushButton:pressed {
background-color: #1c5980; /* 鼠标按下再变深 */
}
以下是一个完整 Qt 示例程序,用于动态演示伪类(Pseudo-class)视觉效果:
QSS 样式文件:
QPushButton {
background-color: #3498db;
color: white;
border: none;
border-radius: 6px;
padding: 8px;
}
QPushButton:hover {
background-color: #2980b9;
}
QPushButton:pressed {
background-color: #1c5980;
}
QPushButton:disabled {
background-color: #cccccc;
color: #888888;
}
QLineEdit {
border: 2px solid #cccccc;
border-radius: 4px;
padding: 4px;
}
QLineEdit:focus {
border: 2px solid #0078d7;
background-color: #eef6ff;
}
QCheckBox::indicator {
width: 18px;
height: 18px;
}
QCheckBox::indicator:checked {
image: url(:/icons/checkbox_checked.png);
}
QCheckBox::indicator:unchecked {
image: url(:/icons/checkbox_unchecked.png);
}
代码实现:
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QCheckBox>
#include <QFile>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
window.setWindowTitle("伪类选择器演示");
QVBoxLayout* layout = new QVBoxLayout;
// 按钮 - hover & pressed
QPushButton* button = new QPushButton("悬停 / 按下效果");
// QLineEdit - focus
QLineEdit* edit = new QLineEdit;
edit->setPlaceholderText("点击这里以获得焦点");
// QCheckBox - checked
QCheckBox* checkbox = new QCheckBox("启用功能");
// Disabled 按钮
QPushButton* disabledBtn = new QPushButton("禁用按钮");
disabledBtn->setEnabled(false);
layout->addWidget(button);
layout->addWidget(edit);
layout->addWidget(checkbox);
layout->addWidget(disabledBtn);
window.setLayout(layout);
// 加载 QSS
QFile file(":/res/qss_style.qss");
if (file.open(QFile::ReadOnly)) {
app.setStyleSheet(file.readAll());
}
window.show();
return app.exec();
}
输出结果: