跳转到内容
新建笔记

QSS 全局样式与自定义 QWidget 背景

应用样式表适合统一默认字体和颜色;自定义 QWidget 还需要明确谁来绘制样式背景。红色背景或边框不出现时,应检查规则匹配、绘制路径和子控件几何关系。

本文完整程序已用 Qt 6.10.2 Widgets 编译,并在 Fusion 样式下检查背景、边框与子控件的实际合成像素。原生平台主题可能有不同外观,不能以这里的配色代替目标平台检查。

应用默认值与局部背景

跳转到“应用默认值与局部背景”

QApplication::setStyleSheet() 设置全局规则。原有白色背景、黑色文字、14px 字号和 Microsoft YaHei 字体可以保留;缺少这款字体的平台会回退到可用字体。把背景写在 QWidget 类型选择器上会匹配大量派生控件,适用于确实需要这个全局默认值的场景。

指定对象的局部规则可以覆盖全局默认值。为某一个面板设置背景时,使用 QWidget#BalanceSetupItem,不要仅为了缩小范围而写泛指所有 QWidget 的选择器。

自定义控件怎样参与样式绘制

跳转到“自定义控件怎样参与样式绘制”

普通 QWidget 支持样式背景,不能笼统说它必须手动开启一个属性才会显示 QSS。对于自己实现的 QWidget 子类,可以按 Qt 的 QWidget 样式说明 在 paintEvent() 中调用 QStyle::PE_Widget,随后再画自己的内容。使用类名作为选择器还需要正确的 Qt 元对象信息;下面保留 Q_OBJECT,并由 CMake 的 AUTOMOC 处理。

Qt::WA_StyledBackground 也是要求按样式绘制背景的属性,适用于有意使用 Qt 背景绘制路径的控件。下面选择显式 paintEvent() 路径,不再重复开启该属性。任意自绘函数随后覆盖整个控件时,仍可能遮住先前的背景。

完整的 BalanceSetupItem 示例

跳转到“完整的 BalanceSetupItem 示例”

保存为 main.cpp。窗口包含一个固定高 70px 的自定义条目,背景红色,上下各有 1px 灰色边框。原示例里未声明的 JP_SettingScreen 已替换为这里完整构建的普通窗口。

#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QPainter>
#include <QPushButton>
#include <QStyle>
#include <QStyleOption>
#include <QVBoxLayout>
class BalanceSetupItem final : public QWidget
{
Q_OBJECT
public:
explicit BalanceSetupItem(QWidget *parent = nullptr) : QWidget(parent)
{
setObjectName("BalanceSetupItem");
setFixedHeight(70);
setStyleSheet(R"QSS(
QWidget#BalanceSetupItem {
background-color: red;
border-top: 1px solid gray;
border-bottom: 1px solid gray;
}
QLabel#balanceTitle { background-color: transparent; color: black; }
)QSS");
auto *layout = new QHBoxLayout(this);
layout->setContentsMargins(1, 1, 1, 1);
auto *label = new QLabel("Balance setup", this);
label->setObjectName("balanceTitle");
auto *button = new QPushButton("Configure", this);
button->setObjectName("configureButton");
layout->addWidget(label);
layout->addStretch();
layout->addWidget(button);
}
protected:
void paintEvent(QPaintEvent *) override
{
QStyleOption option;
option.initFrom(this);
QPainter painter(this);
style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
}
};
QWidget *makeWindow()
{
qApp->setStyleSheet(R"QSS(
QWidget { background-color: white; color: black;
font-family: "Microsoft YaHei"; font-size: 14px; }
)QSS");
auto *window = new QWidget;
window->setWindowTitle("Global style and custom background");
auto *layout = new QVBoxLayout(window);
layout->addWidget(new QLabel("The application default is white.", window));
layout->addWidget(new BalanceSetupItem(window));
window->resize(480, 180);
return window;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *window = makeWindow();
window->show();
const int result = app.exec();
delete window;
return result;
}
#include "main.moc"

配套 CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)
project(QssExample LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
add_executable(qss-example main.cpp)
target_link_libraries(qss-example PRIVATE Qt6::Widgets)

用正确配置 Qt Kit 的 Qt Creator 打开工程后构建运行。两个教程的 main.cpp 各自是独立程序,应分别建工程。

边框为什么还可能被遮住

跳转到“边框为什么还可能被遮住”

QLayout::setContentsMargins(1, 1, 1, 1) 在布局区域四边留出空间;QSS 的 padding 属于控件的样式盒模型。它们作用于不同环节,不要假定给自定义父控件写 padding: 1px 就等于设置其子布局边距。示例直接给布局保留 1px,并检查标签与按钮都处于边框内侧。

标签另外设置透明背景,是为了显示条目的红色。应用的 QWidget { background-color: white; } 本来还会匹配标签;仅修改父对象的背景并不会撤销那条白色规则。

排查时依次确认对象名、有效样式的来源、绘制顺序和子对象是否覆盖边缘。不要加 !important,Qt 的 QSS 没有这项优先级机制。规则冲突详见 选择器与作用域。