跳转到内容
新建笔记

QSS 速查:选择器、子控件、状态与盒模型

Qt Widgets 的样式表由选择器和声明组成:QPushButton { color: white; } 先选择对象,再为匹配对象设置属性。它采用类似 CSS 的语法,但可用属性、子控件和状态由 Qt 控件实现决定,不能直接套用浏览器的全部 CSS 功能。

本页保留常用选择器、36 个子控件和44项状态的查阅表,并给出一套能加载资源文件的示例。排查具体对象没有命中规则的问题,可配合QSS 选择器与作用范围;组合按钮的背景处理见文件列表项悬停背景。

选择器:匹配哪个对象

跳转到“选择器:匹配哪个对象”
类型示例匹配范围
通用*当前样式作用范围内的所有控件。
类型QPushButton该类型及其子类的实例。QWidget 范围更广,QGroupBox 则限定分组框。
属性QPushButton[flat="false"]属性值匹配的按钮;动态属性也可参与,但修改后可能需要重新应用样式。
类.QPushButton精确类名对应的实例,不包括派生类;并非网页中任意 CSS class 的自动映射。
IDQPushButton#myButton类型满足且 objectName() 为 myButton 的对象;单独 #myButton 不限定类型。
后代QDialog QPushButton父链中有 QDialog 的按钮,中间可有其他容器。
直接子对象QDialog > QPushButtonparentWidget() 就是该对话框的按钮。

例如 QLineEdit, QComboBox 是两条选择器的并集;QDialog QWidget 可能覆盖对话框中大量后代控件,需留意它会不会把局部颜色一起改掉。C++ 变量名不会自动成为 objectName。自定义类型选择器还依赖元对象中的类名。

子控件:匹配控件内部的样式部位

跳转到“子控件:匹配控件内部的样式部位”

:: 后面是控件公开给样式系统的绘制部位,例如 QGroupBox::title。它不等于 C++ 子对象,因此不能用 QPushButton::item 选中按钮里的 QLabel。下表是查阅入口;使用前还要确认目标控件的支持说明。

子控件典型用途
::add-line滚动条向增加方向移动一行的按钮。
::add-page滚动条滑块与增加方向按钮之间的分页区域。
::branch树视图的展开分支指示。
::chunk进度条已完成的色块。
::close-button停靠窗口或页签的关闭按钮。
::corner抽象滚动区域中两条滚动条的交会角。
::down-arrow组合框、表头排序、滚动条或数值输入框的向下箭头。
::down-buttonQSpinBox 等数值输入框的减小按钮;滚动条用 ::add-line、::sub-line。
::drop-down组合框用于展开列表的按钮区域。
::float-button停靠窗口的浮动按钮。
::groove滑块控件的轨道。
::indicator复选框、单选框、可选菜单项、分组框或项目视图的勾选指示。
::handle滚动条、滑块或分隔器的可拖动部分。
::icon项目视图或菜单项中的图标。
::item项目视图、菜单栏、菜单或状态栏中的项。
::left-arrow滚动条等控件的向左箭头。
::left-corner页签容器的左侧角控件区域。
::menu-arrow带菜单的工具按钮中的菜单箭头。
::menu-button工具按钮采用独立菜单按钮模式时的菜单区域。
::menu-indicator按钮上的菜单指示;工具按钮也有相应支持。
::right-arrow子菜单或滚动条的向右箭头。
::pane页签容器内容区域的框架。
::right-corner页签容器的右侧角控件区域。
::scroller菜单或页签栏的滚动区域。
::section表头中的一个分区。
::separator菜单分隔线或主窗口停靠区域之间的分隔条。
::sub-line滚动条向减少方向移动一行的按钮。
::sub-page滚动条滑块与减少方向按钮之间的分页区域。
::tab页签栏或工具箱中的页签。
::tab-bar调整 QTabWidget 内页签栏的位置;单个页签外观仍用 ::tab。
::tear页签栏的截断提示部位。
::tearoff菜单的撕离提示部位。
::text项目视图中的文本部位。
::title分组框或停靠窗口的标题。
::up-arrow表头排序、滚动条或数值输入框的向上箭头;工具按钮也支持箭头部位。
::up-buttonQSpinBox 等数值输入框的增大按钮。

组合框、滚动条等复杂控件在部分部位被自定义后,其他部位也可能需要明确设置尺寸和外观。不要把上表理解为每个控件都支持每个名字。Qt 6.8 参考表对 ::down-button 的滚动条归属有误;上表按 Qt 6.10.2 的子控件映射和控件实际绘制分发修正。

伪状态:限定何时应用

跳转到“伪状态:限定何时应用”

单冒号 : 表示状态。连续状态表示同时满足,例如 QPushButton:hover:checked;! 表示否定,例如 QPushButton:hover:!pressed;逗号连接完整选择器表示“或”。

状态含义与适用提示
:active控件所属窗口当前处于活动状态。
:adjoins-item树分支的连接部位邻接一个项目。
:alternate项目视图开启交替行颜色后,被绘制为交替行的项目。
:bottom部位位于底侧,例如底部页签。
:checked可勾选对象处于勾选状态。
:closable停靠窗口等对象允许关闭。
:closed树节点等对象处于折叠或关闭状态。
:default默认按钮或菜单默认动作。
:disabled对象已禁用。
:editable组合框允许编辑。
:edit-focus历史 Qt Extended 的编辑焦点状态;普通桌面键盘焦点使用 :focus。
:enabled对象已启用。
:exclusive菜单项等属于互斥选择组。
:first有序集合中的首项。
:flat按钮采用平面外观。
:floatable停靠窗口等对象允许浮动。
:focus对象具有输入焦点。
:has-children树节点拥有子项目。
:has-siblings树分支对应 State_Sibling;实际 Qt 6 解析器采用这个复数拼写。原稿的 :has-sibling 不应作为有效规则使用。
:horizontal控件或部位按水平方向布局。
:hover支持该状态的控件或部位处于鼠标悬停状态。
:indeterminate不确定状态,例如三态复选框的部分选中或不定进度。
:last有序集合中的末项。
:left部位位于左侧。
:maximized子窗口等对象处于最大化状态。
:middle位于集合中间,既非首项也非末项。
:minimized子窗口等对象处于最小化状态。
:movable停靠窗口等对象允许移动。
:no-frame数值输入框或单行输入框等没有边框。
:non-exclusive菜单项等不属于互斥选择组。
:off可切换对象处于关闭状态。
:on可切换对象处于开启状态。
:only-one集合中仅有这一项。
:open树节点已展开,或带菜单的控件已展开菜单。
:next-selected当前项的下一个相邻项已选中;不是“将要被选中”。
:pressed对象处于按下状态。
:previous-selected当前项的上一个相邻项已选中;不是历史选择记录。
:read-only输入框只读,或组合框不可编辑。
:right部位位于右侧。
:selected页签、菜单项等被选中;注意与按钮的 :checked 区别。
:top部位位于顶侧。
:unchecked可勾选对象处于未勾选状态。
:vertical控件或部位按垂直方向布局。
:window控件本身是窗口。

状态名存在不代表每一种控件都会产生这个状态。比如不能用普通 QLabel:hover 推断按钮同样的交互绘制;部分选中常见于三态 QCheckBox,不要据此假设标准 QRadioButton 也提供三态 API。表中名称以Qt 6.10.2 实际解析器及官方状态参考核对。

属性设置内容
color文本前景色。
background-color背景填充色。
border边线宽度、线型和颜色。
padding边线内侧与内容之间的空间。
margin边线外侧的空间。
font字体及字号等;示例的 14px 是像素,不是 14 点。
selection-color所选文本的前景色,要求控件支持。
selection-background-color所选文本的背景色,要求控件支持。

属性值需要满足 Qt 的类型要求,例如颜色、长度或字体描述。支持盒模型的控件或子控件可按下面四层理解:外边距 margin,边框 border,内边距 padding,最后是内容区。默认边距、边框宽度和内边距都是零;实际样式可以改变它们。

Qt 盒模型:margin 包围 border,border 内是 padding 和 content

这张原图用于说明同心矩形的关系。QSS 边距和布局管理器的 setContentsMargins()、setSpacing() 属于不同设置,不能仅凭图示就认定它们会替代彼此。Qt 盒模型说明还说明了背景绘制和裁剪的边界。

原笔记保留的盒模型检查面板,展示边距、边框、填充和内容尺寸

第二张图展示一种盒模型检查面板,不能据此确定使用的是哪个工具,也不能把其中约 229.45 × 24 的内容尺寸直接当作 Qt 控件的实际几何测量。

先判断规则来自哪个层级,再比较同层级选择器。控件自己的样式优先于祖先或应用级样式;在同一层级,按 ID、属性及伪状态、类型名的具体性比较,同等具体性时后写的规则胜出。C++ 派生类不会因此获得更高的类型选择器优先级,QSS 也不支持 !important。

例如同一份样式中的 QPushButton#okButton { color: gray; } 比 QPushButton { color: red; } 更具体,按钮文字为灰色。QPushButton:hover:checked 比 QPushButton:hover 多一个状态条件,但不能因此覆盖具有更高 ID 具体性的规则。多个状态同时满足时,应明确期望的组合规则,避免只凭书写位置猜颜色。

下面示例让 stateButton 的普通文字为红色,悬停或勾选时为白色,同时满足时为蓝色;按钮背景展示红色、橙色和按下的绿色。为便于辨认红色文字,stateButton 的普通底色单独设为浅灰。okButton 保留 ID 规则的灰色文字。

可运行示例:从资源加载 QSS

跳转到“可运行示例:从资源加载 QSS”

创建同一目录中的三个文件。先保存 theme.qss:

QWidget {
background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);
font: 14px "Arial";
}
QPushButton {
background-color: rgb(255, 0, 0);
color: white;
border: 2px solid black;
border-radius: 10px;
padding: 5px;
}
QPushButton#okButton { color: gray; }
QPushButton:hover { background-color: rgb(255, 165, 0); }
QPushButton:pressed { background-color: rgb(0, 255, 0); }
QPushButton#stateButton {
color: red;
background-color: rgb(245, 245, 245);
}
QPushButton#stateButton:hover { background-color: rgb(255, 165, 0); }
QPushButton#stateButton:pressed { background-color: rgb(0, 255, 0); }
QPushButton#stateButton:hover, QPushButton#stateButton:checked {
color: white;
}
QPushButton#stateButton:hover:checked { color: blue; }
QWidget#filemanager_memeryArea {
background-color: rgb(228, 228, 228);
border-radius: 5px;
}
QGroupBox {
border: 1px solid gray;
margin-top: 14px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top center;
padding: 0 3px;
background-color: rgb(200, 200, 200);
}
QLineEdit, QComboBox {
background-color: white;
border: 1px solid black;
}
QLineEdit {
selection-color: white;
selection-background-color: rgb(40, 90, 160);
}

再保存 main.cpp。文件只检查“存在”并不等于能够读取,因此直接检查 open() 和流读取结果;读取失败时保留已有样式并返回错误。

#include <QApplication>
#include <QComboBox>
#include <QDebug>
#include <QFile>
#include <QFrame>
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QStringConverter>
#include <QTextStream>
#include <QVBoxLayout>
#include <cstdlib>
bool loadStyleSheet(QApplication &app, const QString &path,
QString *error = nullptr) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
if (error) *error = file.errorString();
return false;
}
QTextStream stream(&file);
stream.setEncoding(QStringConverter::Utf8);
const QString text = stream.readAll();
if (stream.status() != QTextStream::Ok || file.error() != QFile::NoError) {
if (error) *error = QStringLiteral("Failed to read stylesheet");
return false;
}
app.setStyleSheet(text);
if (error) error->clear();
return true;
}
QWidget *makeWindow() {
auto *window = new QWidget;
auto *column = new QVBoxLayout(window);
auto *area = new QFrame(window);
area->setObjectName("filemanager_memeryArea");
auto *areaLayout = new QVBoxLayout(area);
areaLayout->addWidget(new QLabel("File area", area));
auto *ok = new QPushButton("Object-name rule", area);
ok->setObjectName("okButton");
areaLayout->addWidget(ok);
auto *state = new QPushButton("Hover / check / press", area);
state->setObjectName("stateButton");
state->setCheckable(true);
areaLayout->addWidget(state);
column->addWidget(area);
auto *group = new QGroupBox("Inputs", window);
group->setObjectName("inputs");
auto *inputs = new QVBoxLayout(group);
inputs->setContentsMargins(10, 16, 10, 10);
auto *line = new QLineEdit("Select this text", group);
line->setObjectName("line");
auto *combo = new QComboBox(group);
combo->setObjectName("combo");
combo->addItems({"One", "Two"});
inputs->addWidget(line);
inputs->addWidget(combo);
column->addWidget(group);
window->setWindowTitle("QSS reference example");
window->resize(460, 300);
return window;
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
const QString path = QStringLiteral(":/styles/theme.qss");
QString error;
if (!loadStyleSheet(app, path, &error)) {
qWarning().noquote() << path << error;
return EXIT_FAILURE;
}
QWidget *window = makeWindow();
window->setAttribute(Qt::WA_DeleteOnClose);
window->show();
return app.exec();
}

最后保存 CMakeLists.txt。qt_add_resources() 把 theme.qss 编译到程序资源中,文件名和前缀共同形成 :/styles/theme.qss。

cmake_minimum_required(VERSION 3.21)
project(QssReferenceExample LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
add_executable(qss-reference-example main.cpp)
target_link_libraries(qss-reference-example PRIVATE Qt6::Widgets)
qt_add_resources(qss-reference-example styles
PREFIX "/styles"
FILES theme.qss
)

原笔记使用的 :/qdarkstyle/dark/darkstyle.qss 只有在相应资源确实被打包或注册时才可读取;这个路径本身不会安装第三方主题。改为磁盘路径时,同样经过读取失败处理。成功返回表示读取并提交了字符串,不表示已经校验所有 QSS 规则;语法和外观仍需通过 Qt 的诊断输出及实际控件检查。

这里的全局 QWidget 规则会直接匹配大量子控件,所以灰色区域内的标签仍可能拥有自己的白色背景。若期望标签透出父背景,应像列表项示例一样给目标标签设透明背景,避免把“全局匹配”误认为普通属性继承。

示例按 Qt 6.10.2、C++17 构建,检查资源成功加载、路径不存在时保留旧样式,以及示例控件的颜色、字号和组合状态。速查表是名称及适用范围核对,并不代表对每个控件的每项状态都进行了交互测试。自动绘制检查使用离屏平台和 Fusion 样式,部署平台的字体、原生菜单与窗口部件仍可能表现不同。