Qt样式系统详解(上)

目录

  1. Qt样式系统基础
  2. QSS(Qt样式表)详解
  3. QStyle样式引擎
  4. QPalette调色板系统
  5. QStyleOption样式配置
  6. 自定义样式的实现
  7. 样式系统的高级应用
  8. 主题系统与皮肤切换
  9. 样式性能优化
  10. 实际应用场景与最佳实践

1. Qt样式系统基础

1.1 什么是Qt样式系统

样式系统的基本概念

什么是样式系统?

样式系统是Qt框架中负责控制应用程序视觉外观的核心机制。它决定了控件如何被绘制、如何显示颜色、字体、边框、背景等视觉元素。简单来说,样式系统就是Qt应用程序的"化妆师",它让界面变得美观、统一、符合设计规范。

样式系统的核心作用:

  1. 视觉呈现:控制控件的颜色、字体、边框、背景等视觉属性
  2. 平台适配:在不同操作系统上提供原生或统一的视觉体验
  3. 主题切换:支持亮色/暗色主题、自定义主题等
  4. 品牌定制:让应用程序具有独特的视觉风格

样式系统的工作流程:

复制代码
用户界面需求 → 样式系统 → 绘制引擎 → 屏幕显示

当Qt需要绘制一个控件时(比如按钮),样式系统会:

  1. 收集控件的状态信息(是否按下、是否悬停、是否禁用等)
  2. 查找适用的样式规则(QSS样式表、QStyle样式引擎、QPalette调色板)
  3. 计算最终的视觉属性(颜色、尺寸、位置等)
  4. 调用绘制函数将控件绘制到屏幕上
Qt样式系统的组成部分

Qt样式系统由三个核心组件组成,它们协同工作,共同完成界面的绘制:

1. QSS(Qt Style Sheets)- 样式表

QSS是Qt的样式表系统,类似于Web开发中的CSS。它允许开发者通过文本方式定义控件的样式,具有声明式、易维护、易修改的特点。

特点:

  • 基于CSS语法,学习成本低
  • 支持选择器、伪状态、子控件等高级特性
  • 可以动态修改,支持运行时切换主题
  • 样式与代码分离,便于维护

示例:

cpp 复制代码
// 通过QSS设置按钮样式
QPushButton *button = new QPushButton("点击我");
button->setStyleSheet(
    "QPushButton {"
    "    background-color: #4CAF50;"
    "    color: white;"
    "    border: none;"
    "    padding: 10px;"
    "    border-radius: 5px;"
    "}"
    "QPushButton:hover {"
    "    background-color: #45a049;"
    "}"
);

2. QStyle - 样式引擎

QStyle是Qt的样式引擎,负责实际绘制控件。它是一个抽象基类,定义了绘制控件的接口。Qt为不同平台提供了不同的QStyle实现(如Windows样式、macOS样式、Fusion样式等)。

特点:

  • 提供统一的绘制接口
  • 支持平台原生样式
  • 可以完全自定义绘制逻辑
  • 控制控件的尺寸、布局、绘制细节

QStyle的核心方法:

cpp 复制代码
class QStyle {
public:
    // 绘制基本元素(如按钮、复选框等)
    virtual void drawPrimitive(PrimitiveElement pe, 
                               const QStyleOption *opt,
                               QPainter *p, 
                               const QWidget *w = nullptr) const = 0;
    
    // 绘制控件元素
    virtual void drawControl(ControlElement element,
                             const QStyleOption *opt,
                             QPainter *p,
                             const QWidget *w = nullptr) const = 0;
    
    // 绘制复杂控件(如组合框、滚动条等)
    virtual void drawComplexControl(ComplexControl cc,
                                    const QStyleOptionComplex *opt,
                                    QPainter *p,
                                    const QWidget *w = nullptr) const = 0;
    
    // 计算控件尺寸
    virtual QSize sizeFromContents(ContentsType ct,
                                    const QStyleOption *opt,
                                    const QSize &contentsSize,
                                    const QWidget *w = nullptr) const = 0;
};

3. QPalette - 调色板

QPalette是Qt的调色板系统,定义了应用程序使用的颜色方案。它通过颜色角色(Color Roles)和颜色组(Color Groups)来组织颜色,确保界面颜色的一致性和可访问性。

特点:

  • 定义统一的颜色方案
  • 支持不同状态的颜色(活动、非活动、禁用)
  • 与QSS和QStyle协同工作
  • 支持主题切换

示例:

cpp 复制代码
// 设置调色板
QPalette palette;
palette.setColor(QPalette::Window, Qt::white);        // 窗口背景色
palette.setColor(QPalette::WindowText, Qt::black);   // 窗口文本色
palette.setColor(QPalette::Button, Qt::lightGray);   // 按钮背景色
palette.setColor(QPalette::ButtonText, Qt::black);   // 按钮文本色
palette.setColor(QPalette::Highlight, Qt::blue);     // 高亮色
palette.setColor(QPalette::HighlightedText, Qt::white); // 高亮文本色

qApp->setPalette(palette);

三者的关系:

复制代码
QSS样式表(声明式,易用)
    ↓
QStyle样式引擎(执行绘制)
    ↓
QPalette调色板(提供颜色)
    ↓
最终视觉效果
样式系统与其他UI框架的对比

与Web CSS的对比:

特性 Qt QSS Web CSS
语法 类似CSS,但有一些差异 标准CSS
选择器 支持类型、类、ID、伪状态等 支持更多选择器类型
盒模型 支持margin、border、padding 标准盒模型
动画 支持简单的过渡效果 支持完整的动画系统
布局 不直接控制布局(由布局管理器控制) 可以控制布局(Flexbox、Grid)

与WPF样式的对比:

特性 Qt样式系统 WPF样式系统
样式定义 QSS文本或QStyle代码 XAML或代码
数据绑定 不支持 支持强大的数据绑定
模板 不支持控件模板 支持控件模板
动画 有限支持 完整的动画系统
主题 通过QSS和QPalette 通过ResourceDictionary

与Android样式的对比:

特性 Qt样式系统 Android样式系统
样式定义 QSS文本 XML资源文件
主题 运行时切换 编译时或运行时切换
适配 手动适配不同屏幕 自动适配(dp、sp等)
Material Design 需要手动实现 原生支持

Qt样式系统的优势:

  1. 跨平台统一:一套代码可以在多个平台运行,样式可以统一或适配平台
  2. 易于使用:QSS语法简单,类似CSS,学习成本低
  3. 灵活强大:支持声明式(QSS)和命令式(QStyle)两种方式
  4. 性能优秀:样式系统经过优化,性能表现良好
  5. 可扩展性强:可以完全自定义样式引擎
为什么需要样式系统

1. 视觉一致性

样式系统确保应用程序的所有控件具有一致的视觉风格。通过统一的样式定义,可以避免每个控件单独设置样式,减少代码重复,提高维护性。

示例:

cpp 复制代码
// ❌ 没有样式系统:每个控件都要单独设置
QPushButton *btn1 = new QPushButton("按钮1");
btn1->setStyleSheet("background-color: blue; color: white;");

QPushButton *btn2 = new QPushButton("按钮2");
btn2->setStyleSheet("background-color: blue; color: white;");

QPushButton *btn3 = new QPushButton("按钮3");
btn3->setStyleSheet("background-color: blue; color: white;");

// ✅ 有样式系统:统一设置
qApp->setStyleSheet("QPushButton { background-color: blue; color: white; }");
QPushButton *btn1 = new QPushButton("按钮1");
QPushButton *btn2 = new QPushButton("按钮2");
QPushButton *btn3 = new QPushButton("按钮3");

2. 主题切换支持

现代应用程序通常需要支持亮色/暗色主题切换。样式系统通过QSS和QPalette可以轻松实现主题切换,无需修改代码逻辑。

示例:

cpp 复制代码
void MainWindow::switchTheme(bool dark) {
    if (dark) {
        // 暗色主题
        qApp->setStyleSheet(
            "QWidget { background-color: #2b2b2b; color: #dddddd; }"
            "QPushButton { background-color: #3c3f41; }"
        );
    } else {
        // 亮色主题
        qApp->setStyleSheet(
            "QWidget { background-color: #ffffff; color: #000000; }"
            "QPushButton { background-color: #e0e0e0; }"
        );
    }
}

3. 平台原生体验

Qt样式系统支持平台原生样式,让应用程序在不同平台上看起来像原生应用,提供更好的用户体验。

示例:

cpp 复制代码
// Windows平台使用Windows样式
qApp->setStyle("windows");

// macOS平台使用macOS样式
qApp->setStyle("macos");

// 跨平台统一样式
qApp->setStyle("fusion");

4. 品牌定制

企业应用程序通常需要符合品牌视觉规范。样式系统可以轻松实现品牌定制,包括颜色、字体、图标等。

5. 可维护性

样式系统将样式定义与代码逻辑分离,使得样式修改不需要重新编译代码,提高了开发效率和可维护性。


1.2 Qt样式系统的层次结构

样式层级关系

Qt样式系统采用分层架构,样式规则按照优先级从高到低依次应用。理解这个层级关系对于正确使用样式系统至关重要。

样式层级(从高到低):

复制代码
1. 控件自身样式(最高优先级)
   ↓
2. 父控件样式
   ↓
3. 应用级样式(QApplication::setStyleSheet)
   ↓
4. QStyle样式引擎
   ↓
5. QPalette调色板
   ↓
6. 系统默认样式(最低优先级)

实际示例:

cpp 复制代码
// 1. 应用级样式(优先级3)
qApp->setStyleSheet("QPushButton { background-color: red; }");

// 2. 父控件样式(优先级2)
QWidget *parent = new QWidget();
parent->setStyleSheet("QPushButton { background-color: blue; }");

// 3. 控件自身样式(优先级1,最高)
QPushButton *button = new QPushButton("按钮", parent);
button->setStyleSheet("background-color: green;");

// 结果:按钮是绿色的(控件自身样式优先级最高)

源码分析:

在Qt源码中,样式查找的顺序在QWidget::styleSheet()和QApplication::styleSheet()中实现。当控件需要获取样式时,Qt会:

  1. 首先检查控件自身是否有styleSheet属性
  2. 如果没有,向上查找父控件的styleSheet
  3. 如果父控件也没有,查找QApplication的styleSheet
  4. 如果都没有,使用QStyle和QPalette
样式继承机制

Qt样式系统支持样式继承,子控件可以继承父控件的样式规则。这个机制类似于CSS的继承,但有一些Qt特有的规则。

继承规则:

  1. 样式表继承:子控件会继承父控件的样式表规则,除非子控件有更具体的规则覆盖
  2. 字体继承:子控件自动继承父控件的字体设置
  3. 调色板继承:子控件自动继承父控件的调色板设置
  4. 样式引擎继承:子控件使用与父控件相同的QStyle实例

样式表继承示例:

cpp 复制代码
// 父控件设置样式
QWidget *parent = new QWidget();
parent->setStyleSheet(
    "QWidget {"
    "    background-color: #f0f0f0;"
    "    color: #333333;"
    "    font-size: 12px;"
    "}"
    "QPushButton {"
    "    background-color: #4CAF50;"
    "    color: white;"
    "}"
);

// 子控件自动继承
QPushButton *button = new QPushButton("按钮", parent);
// button会继承:
// - background-color: #4CAF50(来自QPushButton规则)
// - color: white(来自QPushButton规则)
// - font-size: 12px(继承自QWidget规则)

QLabel *label = new QLabel("标签", parent);
// label会继承:
// - background-color: #f0f0f0(来自QWidget规则)
// - color: #333333(来自QWidget规则)
// - font-size: 12px(来自QWidget规则)

字体继承示例:

cpp 复制代码
// 应用级字体
qApp->setFont(QFont("Microsoft YaHei", 10));

// 父控件字体
QWidget *parent = new QWidget();
parent->setFont(QFont("SimHei", 12));

// 子控件继承父控件字体
QLabel *label = new QLabel("文本", parent);
// label的字体是 SimHei 12(继承自parent)

// 子控件可以覆盖继承的字体
QLabel *label2 = new QLabel("文本2", parent);
label2->setFont(QFont("Courier New", 9));
// label2的字体是 Courier New 9(覆盖了继承的字体)

调色板继承示例:

cpp 复制代码
// 父控件设置调色板
QWidget *parent = new QWidget();
QPalette pal = parent->palette();
pal.setColor(QPalette::WindowText, Qt::red);
parent->setPalette(pal);

// 子控件继承调色板
QLabel *label = new QLabel("红色文本", parent);
// label的文本颜色是红色(继承自parent的调色板)
样式优先级规则

Qt样式系统使用特定的优先级规则来决定哪个样式规则生效。理解这些规则对于正确使用样式系统非常重要。

优先级规则(从高到低):

  1. 内联样式 (控件自身的setStyleSheet)
  2. ID选择器 (#objectName)
  3. 类选择器 + 伪状态 (QPushButton:hover)
  4. 类选择器 (QPushButton)
  5. 类型选择器 + 伪状态 (QWidget:hover)
  6. 类型选择器 (QWidget)
  7. 通用选择器 (*)

选择器特异性:

Qt使用类似CSS的特异性(Specificity)规则:

  • ID选择器:特异性最高
  • 类选择器:中等特异性
  • 类型选择器:较低特异性
  • 通用选择器:最低特异性

示例:

cpp 复制代码
qApp->setStyleSheet(
    // 规则1:通用选择器(优先级最低)
    "* { color: black; }"
    
    // 规则2:类型选择器(优先级中等)
    "QPushButton { color: blue; }"
    
    // 规则3:类选择器(优先级较高)
    ".myButton { color: green; }"
    
    // 规则4:ID选择器(优先级最高)
    "#okButton { color: red; }"
);

QPushButton *btn1 = new QPushButton("按钮1");
// btn1的颜色是blue(匹配规则2)

QPushButton *btn2 = new QPushButton("按钮2");
btn2->setProperty("class", "myButton");
// btn2的颜色是green(匹配规则3,优先级高于规则2)

QPushButton *btn3 = new QPushButton("按钮3");
btn3->setObjectName("okButton");
// btn3的颜色是red(匹配规则4,优先级最高)

伪状态的影响:

伪状态会增加选择器的特异性:

cpp 复制代码
qApp->setStyleSheet(
    "QPushButton { background-color: gray; }"           // 普通状态
    "QPushButton:hover { background-color: lightgray; }" // 悬停状态(优先级更高)
    "QPushButton:pressed { background-color: darkgray; }" // 按下状态(优先级更高)
);

样式覆盖规则:

当多个样式规则同时匹配时,后定义的规则会覆盖先定义的规则(如果优先级相同):

cpp 复制代码
qApp->setStyleSheet(
    "QPushButton { background-color: red; }"   // 先定义
    "QPushButton { background-color: blue; }"   // 后定义,覆盖前面的
);
// 最终QPushButton的背景色是blue
样式作用域

样式作用域定义了样式规则的有效范围。Qt支持全局作用域和局部作用域。

全局作用域:

通过QApplication::setStyleSheet()设置的样式具有全局作用域,影响应用程序中的所有控件。

cpp 复制代码
// 全局样式
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"
    "QLabel { color: black; }"
);

// 所有QPushButton和QLabel都会应用这些样式
QPushButton *btn1 = new QPushButton("按钮1");
QPushButton *btn2 = new QPushButton("按钮2");
QLabel *label = new QLabel("标签");

局部作用域:

通过QWidget::setStyleSheet()设置的样式具有局部作用域,只影响该控件及其子控件。

cpp 复制代码
// 全局样式
qApp->setStyleSheet("QPushButton { background-color: blue; }");

// 局部样式(只影响sidebar及其子控件)
QWidget *sidebar = new QWidget();
sidebar->setStyleSheet("QPushButton { background-color: green; }");

QPushButton *btn1 = new QPushButton("按钮1");  // 蓝色(全局样式)
QPushButton *btn2 = new QPushButton("按钮2", sidebar);  // 绿色(局部样式)

样式作用域的查找顺序:

当控件需要获取样式时,Qt按以下顺序查找:

  1. 控件自身的styleSheet
  2. 父控件的styleSheet(向上查找)
  3. QApplication的styleSheet

实际应用:

cpp 复制代码
// 全局主题
qApp->setStyleSheet(
    "QWidget { background-color: #ffffff; }"
    "QPushButton { background-color: #4CAF50; color: white; }"
);

// 侧边栏局部主题
QWidget *sidebar = new QWidget();
sidebar->setStyleSheet(
    "QWidget { background-color: #2b2b2b; }"  // 覆盖全局的QWidget样式
    "QPushButton { background-color: #3c3f41; }" // 覆盖全局的QPushButton样式
);

// 主内容区使用全局主题
QWidget *mainContent = new QWidget();
// mainContent使用全局样式(白色背景)

// 侧边栏使用局部主题
QPushButton *sidebarBtn = new QPushButton("侧边栏按钮", sidebar);
// sidebarBtn使用局部样式(深色背景)

1.3 Qt样式系统的三大支柱

QSS(Qt Style Sheets)- 样式表

QSS是什么?

QSS(Qt Style Sheets)是Qt的样式表系统,它允许开发者使用类似CSS的语法来定义控件的视觉样式。QSS是Qt样式系统中最易用、最灵活的部分。

QSS的核心特点:

  1. 声明式语法:类似CSS,易于学习和使用
  2. 选择器支持:支持类型、类、ID、属性、伪状态等选择器
  3. 动态修改:可以在运行时修改样式,支持主题切换
  4. 样式分离:样式与代码逻辑分离,便于维护

QSS的基本语法:

cpp 复制代码
// 基本语法:选择器 { 属性: 值; }
QPushButton {
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 10px;
    border-radius: 5px;
}

QSS的应用方式:

cpp 复制代码
// 方式1:通过代码设置
QPushButton *button = new QPushButton("按钮");
button->setStyleSheet("background-color: blue; color: white;");

// 方式2:从文件加载
QFile file("style.qss");
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(styleSheet);

// 方式3:从资源文件加载
QFile file(":/styles/style.qss");
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(styleSheet);

QSS的优势:

  • 易用性:语法简单,类似CSS,学习成本低
  • 灵活性:支持复杂的选择器和伪状态
  • 可维护性:样式与代码分离,便于修改和维护
  • 动态性:支持运行时修改,实现主题切换

QSS的局限性:

  • 性能:解析和应用QSS需要一定的性能开销
  • 功能限制:不支持CSS的所有特性(如动画、变换等)
  • 布局控制:不能直接控制布局(由布局管理器控制)
QStyle - 样式引擎

QStyle是什么?

QStyle是Qt的样式引擎,它是一个抽象基类,定义了绘制控件的接口。QStyle负责实际绘制控件的外观,包括颜色、形状、尺寸等。

QStyle的核心职责:

  1. 绘制控件:绘制按钮、复选框、滚动条等控件
  2. 计算尺寸:计算控件的最佳尺寸
  3. 提供度量:提供样式相关的度量值(如边框宽度、间距等)
  4. 平台适配:在不同平台上提供原生或统一的视觉体验

QStyle的继承体系:

cpp 复制代码
QStyle (抽象基类)
├── QCommonStyle (通用样式基类)
│   ├── QWindowsStyle (Windows样式)
│   ├── QMacStyle (macOS样式)
│   ├── QFusionStyle (Fusion样式,跨平台)
│   └── QProxyStyle (代理样式,用于扩展)
└── 自定义样式

QStyle的核心方法:

cpp 复制代码
class QStyle {
public:
    // 绘制基本元素(如按钮、复选框等)
    virtual void drawPrimitive(PrimitiveElement pe,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w = nullptr) const = 0;
    
    // 绘制控件元素
    virtual void drawControl(ControlElement element,
                             const QStyleOption *opt,
                             QPainter *p,
                             const QWidget *w = nullptr) const = 0;
    
    // 绘制复杂控件(如组合框、滚动条等)
    virtual void drawComplexControl(ComplexControl cc,
                                    const QStyleOptionComplex *opt,
                                    QPainter *p,
                                    const QWidget *w = nullptr) const = 0;
    
    // 计算控件尺寸
    virtual QSize sizeFromContents(ContentsType ct,
                                    const QStyleOption *opt,
                                    const QSize &contentsSize,
                                    const QWidget *w = nullptr) const = 0;
    
    // 获取像素度量(如边框宽度、间距等)
    virtual int pixelMetric(PixelMetric metric,
                             const QStyleOption *option = nullptr,
                             const QWidget *widget = nullptr) const = 0;
    
    // 获取样式提示
    virtual int styleHint(StyleHint hint,
                          const QStyleOption *option = nullptr,
                          const QWidget *widget = nullptr,
                          QStyleHintReturn *returnData = nullptr) const = 0;
};

QStyle的使用:

cpp 复制代码
// 设置应用程序的样式
qApp->setStyle("fusion");  // 使用Fusion样式

// 或者创建样式实例
QStyle *style = new QFusionStyle();
qApp->setStyle(style);

// 获取控件的样式
QStyle *widgetStyle = widget->style();

QStyle的优势:

  • 完全控制:可以完全控制控件的绘制逻辑
  • 平台原生:支持平台原生样式,提供原生体验
  • 性能优秀:直接绘制,性能优于QSS
  • 可扩展性:可以继承QStyle创建自定义样式

QStyle的局限性:

  • 复杂度高:需要理解Qt的绘制系统
  • 代码量大:实现完整样式需要大量代码
  • 维护困难:样式逻辑与代码耦合,不易维护
QPalette - 调色板

QPalette是什么?

QPalette是Qt的调色板系统,它定义了应用程序使用的颜色方案。QPalette通过颜色角色(Color Roles)和颜色组(Color Groups)来组织颜色,确保界面颜色的一致性和可访问性。

QPalette的核心概念:

  1. 颜色角色(Color Roles):定义颜色的用途(如窗口背景、文本颜色、按钮颜色等)
  2. 颜色组(Color Groups):定义颜色的状态(如活动、非活动、禁用等)

颜色角色(Color Roles):

cpp 复制代码
enum ColorRole {
    WindowText,        // 窗口文本色
    Button,            // 按钮背景色
    Light,             // 浅色(用于3D效果)
    Dark,              // 深色(用于3D效果)
    Mid,               // 中间色(用于3D效果)
    Text,              // 文本色
    BrightText,        // 亮文本色
    ButtonText,        // 按钮文本色
    Base,              // 基础背景色(如输入框背景)
    Window,            // 窗口背景色
    Shadow,            // 阴影色
    Highlight,         // 高亮色
    HighlightedText,   // 高亮文本色
    Link,              // 链接色
    LinkVisited,       // 已访问链接色
    AlternateBase,     // 交替背景色(如表格行)
    ToolTipBase,       // 工具提示背景色
    ToolTipText,       // 工具提示文本色
    // ... 更多角色
};

颜色组(Color Groups):

cpp 复制代码
enum ColorGroup {
    Active,    // 活动状态(窗口有焦点)
    Inactive,  // 非活动状态(窗口无焦点)
    Disabled   // 禁用状态(控件被禁用)
};

QPalette的使用:

cpp 复制代码
// 创建调色板
QPalette palette;

// 设置活动状态的颜色
palette.setColor(QPalette::Active, QPalette::Window, Qt::white);
palette.setColor(QPalette::Active, QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Active, QPalette::Button, Qt::lightGray);
palette.setColor(QPalette::Active, QPalette::ButtonText, Qt::black);
palette.setColor(QPalette::Active, QPalette::Highlight, Qt::blue);
palette.setColor(QPalette::Active, QPalette::HighlightedText, Qt::white);

// 设置禁用状态的颜色
palette.setColor(QPalette::Disabled, QPalette::WindowText, Qt::gray);
palette.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::gray);

// 应用调色板
qApp->setPalette(palette);

QPalette的优势:

  • 一致性:确保应用程序使用统一的颜色方案
  • 可访问性:支持不同状态的颜色,提高可访问性
  • 主题支持:可以轻松切换亮色/暗色主题
  • 平台适配:可以适配不同平台的颜色方案

QPalette的局限性:

  • 功能有限:只能定义颜色,不能定义其他样式属性
  • 需要配合:通常需要与QSS或QStyle配合使用
三者的关系与协同工作

三者的关系:

QSS、QStyle和QPalette是Qt样式系统的三大支柱,它们协同工作,共同完成界面的绘制:

复制代码
QSS样式表(声明式,易用)
    ↓ 提供样式规则
QStyle样式引擎(执行绘制)
    ↓ 使用颜色
QPalette调色板(提供颜色)
    ↓
最终视觉效果

协同工作流程:

  1. QSS提供样式规则:开发者通过QSS定义控件的样式规则(颜色、边框、背景等)
  2. QStyle执行绘制:QStyle根据样式规则和控件状态,调用绘制函数绘制控件
  3. QPalette提供颜色:QPalette提供统一的颜色方案,QSS和QStyle都可以使用

实际工作示例:

cpp 复制代码
// 1. 设置QPalette(提供颜色方案)
QPalette palette;
palette.setColor(QPalette::Window, Qt::white);
palette.setColor(QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Highlight, Qt::blue);
qApp->setPalette(palette);

// 2. 设置QStyle(选择样式引擎)
qApp->setStyle("fusion");

// 3. 设置QSS(定义样式规则)
qApp->setStyleSheet(
    "QPushButton {"
    "    background-color: palette(button);"  // 使用QPalette的颜色
    "    color: palette(button-text);"
    "    border: 1px solid palette(dark);"
    "    padding: 5px;"
    "}"
    "QPushButton:hover {"
    "    background-color: palette(highlight);"  // 使用QPalette的高亮色
    "}"
);

// 当绘制QPushButton时:
// 1. QSS提供样式规则(背景色、边框、内边距等)
// 2. QStyle根据规则和控件状态绘制控件
// 3. QPalette提供具体的颜色值

优先级关系:

当三者同时设置时,优先级从高到低:

  1. QSS样式表(最高优先级)
  2. QStyle样式引擎
  3. QPalette调色板(最低优先级)

示例:

cpp 复制代码
// 设置QPalette
QPalette palette;
palette.setColor(QPalette::Button, Qt::red);
qApp->setPalette(palette);

// 设置QSS(会覆盖QPalette的颜色)
qApp->setStyleSheet("QPushButton { background-color: blue; }");

// 结果:QPushButton的背景色是blue(QSS优先级更高)

最佳实践:

  1. 使用QPalette定义颜色方案:确保颜色的一致性和可访问性
  2. 使用QSS定义样式细节:利用QSS的灵活性定义边框、圆角、阴影等
  3. 使用QStyle控制绘制逻辑:对于复杂控件,可以自定义QStyle
  4. 三者配合使用:充分利用三者的优势,实现最佳的视觉效果

总结:

Qt样式系统的三大支柱各司其职,协同工作:

  • QSS:提供声明式的样式定义,易于使用和维护
  • QStyle:提供强大的绘制能力,支持平台原生和自定义样式
  • QPalette:提供统一的颜色方案,确保颜色的一致性和可访问性

理解三者的关系和协同工作机制,对于掌握Qt样式系统至关重要。


2. QSS(Qt样式表)详解

2.1 QSS基础语法

CSS与QSS的关系

QSS的起源:

QSS(Qt Style Sheets)是Qt框架借鉴Web开发中的CSS(Cascading Style Sheets)语法而设计的样式表系统。Qt开发团队在设计QSS时,充分考虑了CSS的成熟语法和广泛使用,使得熟悉Web开发的开发者能够快速上手。

相似之处:

  1. 语法结构:QSS和CSS都使用相同的语法结构

    css 复制代码
    /* CSS */
    selector {
        property: value;
    }
    
    /* QSS */
    QPushButton {
        background-color: blue;
    }
  2. 选择器:都支持类型选择器、类选择器、ID选择器等

  3. 属性命名 :大部分属性名称相同(如background-color、color、border等)

  4. 盒模型:都使用margin、border、padding的概念

  5. 继承机制:都支持样式继承

主要差异:

特性 CSS QSS
目标对象 HTML元素 Qt控件(QWidget及其子类)
选择器 支持更多选择器类型 支持Qt特有的选择器(如伪状态)
布局控制 可以控制布局(Flexbox、Grid) 不能直接控制布局(由布局管理器控制)
动画 完整的动画系统 支持简单的过渡效果
变换 支持transform(旋转、缩放等) 不支持transform
伪元素 支持::before、::after 不支持,但支持子控件(Sub-Controls)
变量 支持CSS变量 不支持变量(但可以使用QPalette)

实际对比示例:

css 复制代码
/* CSS - Web开发 */
.button {
    background-color: #4CAF50;
    color: white;
    padding: 10px;
    border-radius: 5px;
}

.button:hover {
    background-color: #45a049;
}
cpp 复制代码
// QSS - Qt开发
QPushButton {
    background-color: #4CAF50;
    color: white;
    padding: 10px;
    border-radius: 5px;
}

QPushButton:hover {
    background-color: #45a049;
}

为什么Qt选择类似CSS的语法?

  1. 降低学习成本:熟悉CSS的开发者可以快速上手
  2. 成熟的设计:CSS经过多年发展,语法设计成熟
  3. 易于维护:声明式语法,样式与代码分离
  4. 灵活性:支持复杂的选择器和样式规则
QSS基本语法规则

基本语法结构:

QSS的基本语法遵循以下结构:

复制代码
选择器 {
    属性1: 值1;
    属性2: 值2;
    ...
}

语法规则:

  1. 选择器:指定要应用样式的控件类型或标识
  2. 大括号:包含样式规则,必须成对出现
  3. 属性 :要设置的样式属性(如background-color、color等)
  4. 冒号:属性名和值之间用冒号分隔
  5. 值:属性的具体值
  6. 分号:每个属性声明以分号结尾

示例:

cpp 复制代码
// 基本语法示例
QPushButton {
    background-color: blue;
    color: white;
    border: 2px solid black;
    padding: 10px;
    border-radius: 5px;
}

注释:

QSS支持C++风格的注释:

cpp 复制代码
/* 这是单行注释 */
QPushButton {
    background-color: blue; /* 行内注释 */
    color: white;
}

/*
 * 这是多行注释
 * 可以写多行
 */

多个选择器:

可以为多个选择器设置相同的样式:

cpp 复制代码
QPushButton, QLabel, QLineEdit {
    color: blue;
    font-size: 14px;
}

嵌套选择器:

QSS支持嵌套选择器(后代选择器):

cpp 复制代码
QWidget {
    background-color: white;
}

QWidget QPushButton {
    background-color: blue;
}

语法注意事项:

  1. 大小写敏感:QSS是大小写敏感的

    cpp 复制代码
    // ✅ 正确
    QPushButton { background-color: blue; }
    
    // ❌ 错误
    qpushbutton { background-color: blue; }
  2. 引号使用:字符串值通常不需要引号,但包含空格的值需要引号

    cpp 复制代码
    // ✅ 正确
    font-family: "Microsoft YaHei";
    font-family: Arial;
    
    // ❌ 错误(如果字体名包含空格)
    font-family: Microsoft YaHei;  // 会被解析为两个值
  3. 单位:数值通常需要单位(px、pt、em等)

    cpp 复制代码
    // ✅ 正确
    padding: 10px;
    font-size: 12pt;
    
    // ❌ 错误(缺少单位)
    padding: 10;  // 可能不会生效
选择器(Selectors)

什么是选择器?

选择器用于指定哪些控件应该应用样式规则。QSS支持多种选择器类型,每种选择器有不同的优先级和用途。

选择器类型概览:

  1. 通用选择器 (*):匹配所有控件
  2. 类型选择器 (QPushButton):匹配特定类型的控件
  3. 类选择器 (.className):匹配具有特定类的控件
  4. ID选择器 (#objectName):匹配具有特定对象名的控件
  5. 属性选择器 ([property="value"]):匹配具有特定属性的控件
  6. 子选择器 (parent > child):匹配直接子控件
  7. 后代选择器 (ancestor descendant):匹配所有后代控件
  8. 伪状态选择器 (:hover、:pressed等):匹配特定状态的控件

选择器优先级:

选择器的优先级从高到低:

  1. ID选择器(#objectName)
  2. 类选择器(.className)
  3. 类型选择器(QPushButton)
  4. 通用选择器(*)

示例:

cpp 复制代码
qApp->setStyleSheet(
    // 通用选择器(优先级最低)
    "* { color: black; }"
    
    // 类型选择器(优先级中等)
    "QPushButton { color: blue; }"
    
    // 类选择器(优先级较高)
    ".myButton { color: green; }"
    
    // ID选择器(优先级最高)
    "#okButton { color: red; }"
);
属性(Properties)

什么是属性?

属性定义了控件的视觉样式,如颜色、字体、边框、间距等。QSS支持大量属性,可以控制控件的各个方面。

属性分类:

  1. 颜色属性 :color、background-color、border-color等
  2. 字体属性 :font-family、font-size、font-weight等
  3. 边框属性 :border、border-width、border-style、border-radius等
  4. 间距属性 :margin、padding等
  5. 尺寸属性 :width、height、min-width、max-width等
  6. 背景属性 :background、background-image、background-repeat等
  7. 文本属性 :text-align、text-decoration等

常用属性示例:

cpp 复制代码
QPushButton {
    /* 颜色属性 */
    color: white;
    background-color: #4CAF50;
    border-color: #45a049;
    
    /* 字体属性 */
    font-family: "Microsoft YaHei";
    font-size: 14px;
    font-weight: bold;
    
    /* 边框属性 */
    border: 2px solid #45a049;
    border-radius: 5px;
    
    /* 间距属性 */
    padding: 10px 15px;
    margin: 5px;
    
    /* 尺寸属性 */
    min-width: 100px;
    min-height: 30px;
}

属性继承:

某些属性会被子控件继承:

  • color:文本颜色
  • font-*:字体相关属性
  • text-align:文本对齐

属性不继承:

某些属性不会被继承:

  • background-color:背景颜色
  • border:边框
  • padding:内边距
  • margin:外边距
值(Values)

值的类型:

QSS支持多种类型的值:

  1. 颜色值:

    • 命名颜色:red、blue、white等
    • 十六进制:#FF0000、#4CAF50等
    • RGB:rgb(255, 0, 0)
    • RGBA:rgba(255, 0, 0, 0.5)
    • QPalette引用:palette(button)、palette(button-text)等
  2. 长度值:

    • 像素:10px、20px
    • 点:12pt、14pt
    • 相对单位:1em、2em
    • 百分比:50%、100%
  3. 字符串值:

    • 字体名:"Microsoft YaHei"、Arial
    • URL:url(:/images/button.png)
  4. 关键字值:

    • none、auto、inherit
    • solid、dashed、dotted(边框样式)
    • left、center、right(对齐方式)

颜色值示例:

cpp 复制代码
QPushButton {
    /* 命名颜色 */
    background-color: red;
    
    /* 十六进制 */
    background-color: #FF0000;
    background-color: #F00;  /* 简写形式 */
    
    /* RGB */
    background-color: rgb(255, 0, 0);
    
    /* RGBA(带透明度) */
    background-color: rgba(255, 0, 0, 0.5);
    
    /* QPalette引用 */
    background-color: palette(button);
    color: palette(button-text);
}

长度值示例:

cpp 复制代码
QPushButton {
    /* 像素 */
    padding: 10px;
    font-size: 14px;
    
    /* 点 */
    font-size: 12pt;
    
    /* 相对单位 */
    font-size: 1.2em;
    
    /* 百分比 */
    width: 50%;
    height: 100%;
}

字符串值示例:

cpp 复制代码
QPushButton {
    /* 字体名(包含空格需要引号) */
    font-family: "Microsoft YaHei";
    font-family: Arial;  /* 不包含空格可以不用引号 */
    
    /* 图片URL */
    background-image: url(:/images/button.png);
    border-image: url(:/images/border.png);
}

关键字值示例:

cpp 复制代码
QPushButton {
    /* 边框样式 */
    border: 1px solid black;
    border: 1px dashed black;
    border: 1px dotted black;
    border: none;  /* 无边框 */
    
    /* 文本对齐 */
    text-align: left;
    text-align: center;
    text-align: right;
    
    /* 自动尺寸 */
    width: auto;
    height: auto;
}

值的组合:

某些属性可以接受多个值:

cpp 复制代码
QPushButton {
    /* 边框:宽度 样式 颜色 */
    border: 2px solid #4CAF50;
    
    /* 内边距:上 右 下 左 */
    padding: 10px 15px 10px 15px;
    
    /* 内边距:上下 左右 */
    padding: 10px 15px;
    
    /* 内边距:所有方向相同 */
    padding: 10px;
    
    /* 外边距:上 右 下 左 */
    margin: 5px 10px 5px 10px;
}

特殊值:

  1. palette()函数:引用QPalette中的颜色

    cpp 复制代码
    color: palette(button-text);
    background-color: palette(button);
  2. url()函数:引用资源文件

    cpp 复制代码
    background-image: url(:/images/background.png);
  3. qlineargradient()函数:线性渐变

    cpp 复制代码
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 white, stop:1 gray);

2.2 QSS选择器详解

选择器是QSS的核心,它决定了哪些控件会应用样式规则。理解各种选择器的用法和优先级对于掌握QSS至关重要。

通用选择器(Universal Selector)

语法 :*

作用:匹配所有控件,优先级最低。

使用场景:

  • 设置全局默认样式
  • 重置所有控件的某些属性

示例:

cpp 复制代码
// 设置所有控件的默认字体
qApp->setStyleSheet("* { font-family: 'Microsoft YaHei'; font-size: 12px; }");

// 重置所有控件的内边距和外边距
qApp->setStyleSheet("* { margin: 0; padding: 0; }");

注意事项:

  • 通用选择器优先级最低,容易被其他选择器覆盖
  • 使用通用选择器可能影响性能(需要检查所有控件)
  • 谨慎使用,避免过度影响
类型选择器(Type Selector)

语法 :控件类型名(如QPushButton、QLabel等)

作用:匹配指定类型的所有控件。

使用场景:

  • 为特定类型的控件设置统一样式
  • 最常用的选择器类型

示例:

cpp 复制代码
// 设置所有按钮的样式
qApp->setStyleSheet(
    "QPushButton {"
    "    background-color: #4CAF50;"
    "    color: white;"
    "    border: none;"
    "    padding: 10px;"
    "    border-radius: 5px;"
    "}"
);

// 设置所有标签的样式
qApp->setStyleSheet(
    "QLabel {"
    "    color: #333333;"
    "    font-size: 14px;"
    "}"
);

// 设置所有输入框的样式
qApp->setStyleSheet(
    "QLineEdit {"
    "    border: 1px solid #cccccc;"
    "    padding: 5px;"
    "    border-radius: 3px;"
    "}"
);

控件类型名称:

QSS使用Qt控件的类名作为类型选择器:

  • QWidget:所有控件的基类
  • QPushButton:按钮
  • QLabel:标签
  • QLineEdit:单行输入框
  • QTextEdit:多行文本编辑器
  • QComboBox:组合框
  • QCheckBox:复选框
  • QRadioButton:单选按钮
  • QSlider:滑块
  • QProgressBar:进度条
  • QScrollBar:滚动条
  • QTabWidget:标签页控件
  • QMenu:菜单
  • QMenuBar:菜单栏
  • 等等...

注意事项:

  • 类型选择器匹配所有该类型的控件,包括子类
  • 如果控件是QPushButton的子类,也会匹配QPushButton选择器
类选择器(Class Selector)

语法 :.类名

作用 :匹配具有特定类的控件。控件通过setProperty("class", "类名")设置类。

使用场景:

  • 为特定功能的控件设置样式
  • 创建可重用的样式类

示例:

cpp 复制代码
// 定义样式类
qApp->setStyleSheet(
    ".primary-button {"
    "    background-color: #4CAF50;"
    "    color: white;"
    "}"
    
    ".secondary-button {"
    "    background-color: #2196F3;"
    "    color: white;"
    "}"
    
    ".danger-button {"
    "    background-color: #f44336;"
    "    color: white;"
    "}"
);

// 应用样式类
QPushButton *okBtn = new QPushButton("确定");
okBtn->setProperty("class", "primary-button");  // 应用primary-button样式

QPushButton *cancelBtn = new QPushButton("取消");
cancelBtn->setProperty("class", "secondary-button");  // 应用secondary-button样式

QPushButton *deleteBtn = new QPushButton("删除");
deleteBtn->setProperty("class", "danger-button");  // 应用danger-button样式

多个类:

一个控件可以有多个类:

cpp 复制代码
QPushButton *btn = new QPushButton("按钮");
btn->setProperty("class", "primary-button large-button");
// 按钮同时具有primary-button和large-button两个类

// 样式定义
qApp->setStyleSheet(
    ".primary-button { background-color: #4CAF50; }"
    ".large-button { padding: 15px; font-size: 16px; }"
);

注意事项:

  • 类名不能包含空格(多个类用空格分隔)
  • 类选择器优先级高于类型选择器
  • 类名区分大小写
ID选择器(ID Selector)

语法 :#对象名

作用:匹配具有特定对象名(objectName)的控件。优先级最高。

使用场景:

  • 为特定控件设置唯一样式
  • 覆盖其他选择器的样式

示例:

cpp 复制代码
// 定义ID样式
qApp->setStyleSheet(
    "#okButton {"
    "    background-color: #4CAF50;"
    "    color: white;"
    "}"
    
    "#cancelButton {"
    "    background-color: #f44336;"
    "    color: white;"
    "}"
);

// 设置对象名
QPushButton *okBtn = new QPushButton("确定");
okBtn->setObjectName("okButton");  // 应用#okButton样式

QPushButton *cancelBtn = new QPushButton("取消");
cancelBtn->setObjectName("cancelButton");  // 应用#cancelButton样式

ID选择器的优先级:

ID选择器优先级最高,可以覆盖其他选择器:

cpp 复制代码
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"  // 类型选择器
    "#okButton { background-color: green; }"    // ID选择器(优先级更高)
);

QPushButton *btn = new QPushButton("按钮");
btn->setObjectName("okButton");
// btn的背景色是green(ID选择器优先级更高)

注意事项:

  • 对象名必须唯一(在同一父控件下)
  • ID选择器优先级最高,谨慎使用
  • 对象名区分大小写
属性选择器(Attribute Selector)

语法:

  • [属性名]:匹配具有该属性的控件
  • [属性名="值"]:匹配属性值等于指定值的控件
  • [属性名~="值"]:匹配属性值包含指定值的控件(用空格分隔)

作用:根据控件的属性(property)来匹配控件。

使用场景:

  • 根据自定义属性设置样式
  • 根据控件状态设置样式

示例:

cpp 复制代码
// 匹配具有enabled属性的控件
qApp->setStyleSheet(
    "QPushButton[enabled=\"true\"] {"
    "    background-color: #4CAF50;"
    "}"
    
    "QPushButton[enabled=\"false\"] {"
    "    background-color: #cccccc;"
    "    color: #999999;"
    "}"
);

// 使用自定义属性
QPushButton *btn = new QPushButton("按钮");
btn->setProperty("buttonType", "primary");

qApp->setStyleSheet(
    "QPushButton[buttonType=\"primary\"] {"
    "    background-color: #4CAF50;"
    "}"
    
    "QPushButton[buttonType=\"secondary\"] {"
    "    background-color: #2196F3;"
    "}"
);

属性值匹配:

cpp 复制代码
// 精确匹配
QPushButton[objectName="okButton"] { ... }

// 包含匹配(值用空格分隔)
QPushButton[class~="primary"] { ... }  // 匹配class包含"primary"的按钮

注意事项:

  • 属性名区分大小写
  • 属性值需要用引号括起来
  • 属性选择器优先级高于类型选择器
子选择器(Child Selector)

语法 :父选择器 > 子选择器

作用:匹配直接子控件(只匹配直接子级,不匹配孙子级)。

使用场景:

  • 为特定父控件的直接子控件设置样式
  • 精确控制样式作用范围

示例:

cpp 复制代码
// 只匹配QWidget的直接子QPushButton
qApp->setStyleSheet(
    "QWidget > QPushButton {"
    "    background-color: blue;"
    "}"
);

// 结构示例
QWidget *parent = new QWidget();
QPushButton *btn1 = new QPushButton("按钮1", parent);  // ✅ 匹配(直接子)
QWidget *child = new QWidget(parent);
QPushButton *btn2 = new QPushButton("按钮2", child);   // ❌ 不匹配(不是直接子)

与后代选择器的区别:

cpp 复制代码
// 子选择器:只匹配直接子控件
QWidget > QPushButton { ... }

// 后代选择器:匹配所有后代控件
QWidget QPushButton { ... }
后代选择器(Descendant Selector)

语法 :祖先选择器 后代选择器(用空格分隔)

作用:匹配所有后代控件(包括子级、孙子级等所有层级)。

使用场景:

  • 为特定容器内的所有控件设置样式
  • 创建局部主题

示例:

cpp 复制代码
// 匹配QWidget内的所有QPushButton(任何层级)
qApp->setStyleSheet(
    "QWidget QPushButton {"
    "    background-color: blue;"
    "}"
);

// 结构示例
QWidget *parent = new QWidget();
QPushButton *btn1 = new QPushButton("按钮1", parent);  // ✅ 匹配
QWidget *child = new QWidget(parent);
QPushButton *btn2 = new QPushButton("按钮2", child);   // ✅ 匹配(后代)

实际应用:

cpp 复制代码
// 为侧边栏内的所有按钮设置样式
QWidget *sidebar = new QWidget();
sidebar->setObjectName("sidebar");

qApp->setStyleSheet(
    "#sidebar QPushButton {"
    "    background-color: #3c3f41;"
    "    color: white;"
    "    text-align: left;"
    "    padding: 10px;"
    "}"
);

多级后代选择器:

cpp 复制代码
// 匹配QMainWindow内的QWidget内的QPushButton
QMainWindow QWidget QPushButton { ... }
伪类选择器(Pseudo-Class Selector)

语法 :选择器:伪类

注意:在QSS中,伪类选择器的概念与CSS不同。QSS主要使用伪状态选择器(Pseudo-State Selector),而不是伪类选择器。

QSS中的伪类:

QSS实际上没有传统CSS意义上的伪类选择器(如:first-child、:last-child等),而是使用伪状态选择器。

与CSS的区别:

  • CSS伪类 ::hover、:active、:focus、:first-child等
  • QSS伪状态 ::hover、:pressed、:checked、:disabled等

在QSS中,我们通常说的"伪类"实际上是指"伪状态"。

伪状态选择器(Pseudo-State Selector)

语法 :选择器:伪状态

作用:根据控件的状态(如悬停、按下、选中等)来匹配控件。

常用伪状态:

  1. :hover:鼠标悬停
  2. :pressed:按下状态
  3. :checked:选中状态(用于复选框、单选按钮等)
  4. :unchecked:未选中状态
  5. :enabled:启用状态
  6. :disabled:禁用状态
  7. :focus:获得焦点
  8. :flat:扁平样式
  9. :default:默认按钮
  10. :exclusive:独占按钮组

示例:

cpp 复制代码
qApp->setStyleSheet(
    // 普通状态
    "QPushButton {"
    "    background-color: #4CAF50;"
    "    color: white;"
    "}"
    
    // 悬停状态
    "QPushButton:hover {"
    "    background-color: #45a049;"
    "}"
    
    // 按下状态
    "QPushButton:pressed {"
    "    background-color: #3d8b40;"
    "}"
    
    // 禁用状态
    "QPushButton:disabled {"
    "    background-color: #cccccc;"
    "    color: #999999;"
    "}"
    
    // 获得焦点
    "QPushButton:focus {"
    "    border: 2px solid #2196F3;"
    "}"
);

复选框和单选按钮的伪状态:

cpp 复制代码
qApp->setStyleSheet(
    // 未选中
    "QCheckBox {"
    "    color: black;"
    "}"
    
    // 选中
    "QCheckBox:checked {"
    "    color: blue;"
    "}"
    
    // 禁用
    "QCheckBox:disabled {"
    "    color: gray;"
    "}"
);

伪状态的组合:

可以组合多个伪状态:

cpp 复制代码
qApp->setStyleSheet(
    // 悬停且未禁用
    "QPushButton:hover:!disabled {"
    "    background-color: #45a049;"
    "}"
    
    // 选中且悬停
    "QCheckBox:checked:hover {"
    "    color: darkblue;"
    "}"
);

否定伪状态:

使用!来否定伪状态:

cpp 复制代码
qApp->setStyleSheet(
    // 未禁用
    "QPushButton:!disabled {"
    "    background-color: #4CAF50;"
    "}"
    
    // 未选中
    "QCheckBox:!checked {"
    "    color: gray;"
    "}"
);

伪状态的优先级:

伪状态会增加选择器的特异性,优先级高于普通选择器:

cpp 复制代码
qApp->setStyleSheet(
    "QPushButton { background-color: gray; }"           // 普通状态
    "QPushButton:hover { background-color: lightgray; }" // 悬停状态(优先级更高)
    "QPushButton:pressed { background-color: darkgray; }" // 按下状态(优先级更高)
);

注意事项:

  • 伪状态区分大小写
  • 可以组合多个伪状态
  • 伪状态会增加选择器的优先级
  • 某些控件可能不支持某些伪状态

2.3 QSS常用属性

QSS提供了丰富的属性来控制控件的视觉样式。本节介绍最常用的属性及其用法。

颜色和背景属性

颜色属性:

  1. color:文本颜色

    cpp 复制代码
    QPushButton { color: white; }
    QLabel { color: #333333; }
  2. background-color:背景颜色

    cpp 复制代码
    QPushButton { background-color: #4CAF50; }
    QWidget { background-color: white; }
  3. border-color:边框颜色

    cpp 复制代码
    QPushButton { border-color: #45a049; }

背景属性:

  1. background:综合背景属性(可以设置颜色、图片等)

    cpp 复制代码
    QPushButton {
        background: #4CAF50;  // 背景颜色
    }
  2. background-image:背景图片

    cpp 复制代码
    QPushButton {
        background-image: url(:/images/button.png);
    }
  3. background-repeat:背景图片重复方式

    cpp 复制代码
    QPushButton {
        background-image: url(:/images/pattern.png);
        background-repeat: repeat;  // repeat, repeat-x, repeat-y, no-repeat
    }
  4. background-position:背景图片位置

    cpp 复制代码
    QPushButton {
        background-image: url(:/images/icon.png);
        background-position: left center;
    }

颜色值格式:

cpp 复制代码
QPushButton {
    /* 命名颜色 */
    background-color: red;
    
    /* 十六进制 */
    background-color: #FF0000;
    background-color: #F00;  /* 简写 */
    
    /* RGB */
    background-color: rgb(255, 0, 0);
    
    /* RGBA(带透明度) */
    background-color: rgba(255, 0, 0, 0.5);
    
    /* QPalette引用 */
    background-color: palette(button);
    color: palette(button-text);
}
边框和圆角属性

边框属性:

  1. border:综合边框属性

    cpp 复制代码
    QPushButton {
        border: 2px solid #4CAF50;  /* 宽度 样式 颜色 */
    }
  2. border-width:边框宽度

    cpp 复制代码
    QPushButton {
        border-width: 2px;
        /* 或分别设置 */
        border-top-width: 2px;
        border-right-width: 1px;
        border-bottom-width: 2px;
        border-left-width: 1px;
    }
  3. border-style:边框样式

    cpp 复制代码
    QPushButton {
        border-style: solid;    /* 实线 */
        border-style: dashed;   /* 虚线 */
        border-style: dotted;   /* 点线 */
        border-style: none;     /* 无边框 */
    }
  4. border-color:边框颜色

    cpp 复制代码
    QPushButton {
        border-color: #4CAF50;
        /* 或分别设置 */
        border-top-color: red;
        border-right-color: blue;
        border-bottom-color: green;
        border-left-color: yellow;
    }

圆角属性:

  1. border-radius :圆角半径

    cpp 复制代码
    QPushButton {
        border-radius: 5px;  /* 所有角 */
        /* 或分别设置 */
        border-top-left-radius: 10px;
        border-top-right-radius: 10px;
        border-bottom-left-radius: 5px;
        border-bottom-right-radius: 5px;
    }

边框示例:

cpp 复制代码
// 圆角按钮
QPushButton {
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    padding: 10px;
}

// 带边框的输入框
QLineEdit {
    border: 1px solid #cccccc;
    border-radius: 3px;
    padding: 5px;
}

// 虚线边框
QWidget {
    border: 2px dashed #999999;
}
字体和文本属性

字体属性:

  1. font-family:字体族

    cpp 复制代码
    QPushButton {
        font-family: "Microsoft YaHei";
        font-family: Arial, "Microsoft YaHei", sans-serif;  /* 多个字体,逗号分隔 */
    }
  2. font-size:字体大小

    cpp 复制代码
    QPushButton {
        font-size: 14px;
        font-size: 12pt;
        font-size: 1.2em;
    }
  3. font-weight:字体粗细

    cpp 复制代码
    QPushButton {
        font-weight: normal;  /* 正常 */
        font-weight: bold;     /* 粗体 */
        font-weight: 100;      /* 数值:100-900 */
    }
  4. font-style:字体样式

    cpp 复制代码
    QPushButton {
        font-style: normal;   /* 正常 */
        font-style: italic;    /* 斜体 */
        font-style: oblique;  /* 倾斜 */
    }
  5. font:综合字体属性

    cpp 复制代码
    QPushButton {
        font: bold 14px "Microsoft YaHei";  /* 粗细 大小 字体族 */
    }

文本属性:

  1. text-align:文本对齐

    cpp 复制代码
    QPushButton {
        text-align: left;    /* 左对齐 */
        text-align: center;  /* 居中 */
        text-align: right;  /* 右对齐 */
    }
  2. text-decoration:文本装饰

    cpp 复制代码
    QLabel {
        text-decoration: none;        /* 无装饰 */
        text-decoration: underline;   /* 下划线 */
        text-decoration: line-through; /* 删除线 */
    }
  3. text-transform:文本转换

    cpp 复制代码
    QLabel {
        text-transform: none;      /* 不转换 */
        text-transform: uppercase; /* 大写 */
        text-transform: lowercase; /* 小写 */
        text-transform: capitalize; /* 首字母大写 */
    }

字体示例:

cpp 复制代码
// 标题样式
QLabel {
    font-family: "Microsoft YaHei";
    font-size: 18px;
    font-weight: bold;
    color: #333333;
}

// 正文样式
QLabel {
    font-family: "Microsoft YaHei";
    font-size: 14px;
    font-weight: normal;
    color: #666666;
}
间距和边距属性

内边距(Padding):

内边距是控件内容与边框之间的距离。

cpp 复制代码
QPushButton {
    padding: 10px;              /* 所有方向相同 */
    padding: 10px 15px;           /* 上下 左右 */
    padding: 5px 10px 15px 20px; /* 上 右 下 左 */
    
    /* 或分别设置 */
    padding-top: 10px;
    padding-right: 15px;
    padding-bottom: 10px;
    padding-left: 15px;
}

外边距(Margin):

外边距是控件与外部元素之间的距离。

cpp 复制代码
QPushButton {
    margin: 5px;                  /* 所有方向相同 */
    margin: 5px 10px;             /* 上下 左右 */
    margin: 2px 5px 8px 10px;    /* 上 右 下 左 */
    
    /* 或分别设置 */
    margin-top: 5px;
    margin-right: 10px;
    margin-bottom: 5px;
    margin-left: 10px;
}

间距示例:

cpp 复制代码
// 按钮间距
QPushButton {
    padding: 10px 20px;  /* 内边距:上下10px,左右20px */
    margin: 5px;         /* 外边距:所有方向5px */
}

// 输入框间距
QLineEdit {
    padding: 5px;        /* 内边距:所有方向5px */
    margin: 0;           /* 外边距:无 */
}
尺寸属性

宽度和高度:

cpp 复制代码
QPushButton {
    width: 100px;   /* 固定宽度 */
    height: 30px;   /* 固定高度 */
}

最小和最大尺寸:

cpp 复制代码
QPushButton {
    min-width: 100px;   /* 最小宽度 */
    min-height: 30px;   /* 最小高度 */
    max-width: 300px;  /* 最大宽度 */
    max-height: 100px;  /* 最大高度 */
}

尺寸单位:

cpp 复制代码
QPushButton {
    width: 100px;      /* 像素 */
    width: 50%;        /* 百分比(相对于父控件) */
    width: auto;       /* 自动(根据内容) */
}

尺寸示例:

cpp 复制代码
// 固定尺寸按钮
QPushButton {
    width: 120px;
    height: 40px;
}

// 最小尺寸按钮
QPushButton {
    min-width: 100px;
    min-height: 30px;
}

// 全宽按钮
QPushButton {
    width: 100%;
}
定位属性

注意 :QSS不能像CSS那样直接控制控件的定位(如position、top、left等)。控件的定位由Qt的布局管理器(如QHBoxLayout、QVBoxLayout等)控制。

QSS可以影响的"定位"相关属性:

  1. alignment:对齐方式(在某些控件中有效)

    cpp 复制代码
    QLabel {
        /* 注意:text-align用于文本对齐,不是控件对齐 */
    }
  2. subcontrol-position:子控件位置(用于子控件)

    cpp 复制代码
    QComboBox::drop-down {
        subcontrol-position: top right;
    }

实际定位控制:

在Qt中,控件的定位应该通过布局管理器来实现:

cpp 复制代码
// ❌ QSS不能这样控制定位
QPushButton {
    position: absolute;
    top: 10px;
    left: 20px;
}

// ✅ 应该使用布局管理器
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button);

总结:

QSS主要用于控制控件的视觉样式(颜色、字体、边框等),而不是布局和定位。布局应该使用Qt的布局管理器来实现。


2.4 QSS盒模型

盒模型是QSS中最重要的概念之一,它定义了控件内容、内边距、边框和外边距之间的关系。

盒模型的组成

盒模型的四个部分(从内到外):

复制代码
┌─────────────────────────────────┐
│         Margin (外边距)          │
│  ┌───────────────────────────┐  │
│  │      Border (边框)         │  │
│  │  ┌─────────────────────┐  │  │
│  │  │  Padding (内边距)   │  │  │
│  │  │  ┌───────────────┐  │  │  │
│  │  │  │   Content    │  │  │  │
│  │  │  │   (内容)     │  │  │  │
│  │  │  └───────────────┘  │  │  │
│  │  └─────────────────────┘  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘
  1. Content(内容):控件的实际内容(文本、图标等)
  2. Padding(内边距):内容与边框之间的距离
  3. Border(边框):围绕内边距和内容的边框
  4. Margin(外边距):控件与外部元素之间的距离

盒模型示例:

cpp 复制代码
QPushButton {
    /* 内容区域 */
    width: 100px;
    height: 30px;
    
    /* 内边距 */
    padding: 10px;
    
    /* 边框 */
    border: 2px solid #4CAF50;
    
    /* 外边距 */
    margin: 5px;
}
margin、border、padding的使用

Margin(外边距):

外边距控制控件与外部元素的距离,不会影响控件本身的尺寸。

cpp 复制代码
QPushButton {
    margin: 10px;              /* 所有方向相同 */
    margin: 5px 10px;          /* 上下 左右 */
    margin: 2px 5px 8px 10px;  /* 上 右 下 左 */
    
    /* 或分别设置 */
    margin-top: 10px;
    margin-right: 10px;
    margin-bottom: 10px;
    margin-left: 10px;
}

Border(边框):

边框围绕内边距和内容,会影响控件的总尺寸。

cpp 复制代码
QPushButton {
    border: 2px solid #4CAF50;  /* 宽度 样式 颜色 */
    
    /* 或分别设置 */
    border-width: 2px;
    border-style: solid;
    border-color: #4CAF50;
    
    /* 或分别设置各边 */
    border-top: 2px solid red;
    border-right: 1px solid blue;
    border-bottom: 2px solid green;
    border-left: 1px solid yellow;
}

Padding(内边距):

内边距控制内容与边框的距离,会影响控件的可用内容区域。

cpp 复制代码
QPushButton {
    padding: 10px;             /* 所有方向相同 */
    padding: 5px 10px;          /* 上下 左右 */
    padding: 2px 5px 8px 10px;  /* 上 右 下 左 */
    
    /* 或分别设置 */
    padding-top: 10px;
    padding-right: 15px;
    padding-bottom: 10px;
    padding-left: 15px;
}

实际示例:

cpp 复制代码
// 按钮盒模型
QPushButton {
    /* 内容区域 */
    min-width: 80px;
    min-height: 30px;
    
    /* 内边距:内容与边框的距离 */
    padding: 10px 20px;
    
    /* 边框 */
    border: 2px solid #4CAF50;
    border-radius: 5px;
    
    /* 外边距:按钮与其他元素的距离 */
    margin: 5px;
}
盒模型计算规则

总尺寸计算:

控件的总尺寸 = 内容尺寸 + 内边距 + 边框 + 外边距

复制代码
总宽度 = width + padding-left + padding-right + border-left-width + border-right-width + margin-left + margin-right
总高度 = height + padding-top + padding-bottom + border-top-width + border-bottom-width + margin-top + margin-bottom

示例计算:

cpp 复制代码
QPushButton {
    width: 100px;        /* 内容宽度 */
    height: 30px;        /* 内容高度 */
    padding: 10px;        /* 内边距:上下左右各10px */
    border: 2px solid;    /* 边框:上下左右各2px */
    margin: 5px;          /* 外边距:上下左右各5px */
}

/* 总宽度 = 100 + 10*2 + 2*2 + 5*2 = 134px */
/* 总高度 = 30 + 10*2 + 2*2 + 5*2 = 64px */

注意事项:

  1. 外边距不计算在控件尺寸内:外边距是控件外部的空间
  2. 边框和内边距会增加控件尺寸 :如果设置了width: 100px和padding: 10px,实际内容区域会小于100px
  3. 盒模型会影响布局:布局管理器计算控件尺寸时会考虑内边距和边框
盒模型的实际应用

场景1:按钮样式设计:

cpp 复制代码
QPushButton {
    /* 内容区域 */
    min-width: 100px;
    min-height: 35px;
    
    /* 内边距:让文本不贴边 */
    padding: 8px 16px;
    
    /* 边框:定义按钮边界 */
    border: 1px solid #4CAF50;
    border-radius: 4px;
    
    /* 外边距:按钮之间的间距 */
    margin: 4px;
}

场景2:输入框样式设计:

cpp 复制代码
QLineEdit {
    /* 内容区域 */
    min-height: 30px;
    
    /* 内边距:让输入文本不贴边 */
    padding: 5px 10px;
    
    /* 边框:定义输入框边界 */
    border: 1px solid #cccccc;
    border-radius: 3px;
    
    /* 外边距:输入框之间的间距 */
    margin: 2px;
}

场景3:卡片样式设计:

cpp 复制代码
QWidget {
    /* 内容区域 */
    min-width: 200px;
    min-height: 150px;
    
    /* 内边距:内容与卡片边缘的距离 */
    padding: 20px;
    
    /* 边框:卡片边界 */
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    
    /* 外边距:卡片之间的间距 */
    margin: 10px;
    
    /* 背景 */
    background-color: white;
}

盒模型调试技巧:

可以通过设置不同的边框颜色来可视化盒模型:

cpp 复制代码
QPushButton {
    border: 2px solid red;      /* 边框:红色 */
    padding: 10px;               /* 内边距:10px */
    background-color: blue;     /* 背景:蓝色(包括内边距区域) */
}

2.5 QSS子控件(Sub-Controls)

什么是子控件

子控件概念:

子控件(Sub-Controls)是复杂控件内部的组成部分。例如,QComboBox(组合框)包含下拉箭头、文本区域等子控件;QScrollBar(滚动条)包含滑块、上下箭头等子控件。

子控件语法:

cpp 复制代码
主控件::子控件 {
    属性: 值;
}

示例:

cpp 复制代码
// 设置QComboBox的下拉箭头样式
QComboBox::drop-down {
    border: 1px solid #cccccc;
    background-color: #f0f0f0;
}
常用控件的子控件列表

QComboBox(组合框):

  • ::drop-down:下拉箭头按钮
  • ::down-arrow:下拉箭头图标
cpp 复制代码
QComboBox {
    border: 1px solid #cccccc;
    padding: 5px;
}

QComboBox::drop-down {
    border: none;
    background-color: #4CAF50;
    width: 30px;
}

QComboBox::down-arrow {
    image: url(:/images/arrow_down.png);
    width: 12px;
    height: 12px;
}

QScrollBar(滚动条):

  • ::handle:滑块
  • ::add-line:增加按钮(向下/向右)
  • ::sub-line:减少按钮(向上/向左)
  • ::add-page:增加页面区域
  • ::sub-page:减少页面区域
cpp 复制代码
QScrollBar:vertical {
    width: 12px;
    background-color: #f0f0f0;
}

QScrollBar::handle:vertical {
    background-color: #cccccc;
    min-height: 20px;
    border-radius: 6px;
}

QScrollBar::handle:vertical:hover {
    background-color: #999999;
}

QScrollBar::add-line:vertical,
QScrollBar::sub-line:vertical {
    height: 0px;  /* 隐藏箭头按钮 */
}

QSpinBox/QDoubleSpinBox(数字输入框):

  • ::up-button:增加按钮
  • ::down-button:减少按钮
  • ::up-arrow:向上箭头
  • ::down-arrow:向下箭头
cpp 复制代码
QSpinBox {
    border: 1px solid #cccccc;
    padding: 5px;
}

QSpinBox::up-button {
    background-color: #4CAF50;
    border: none;
    width: 20px;
}

QSpinBox::down-button {
    background-color: #f44336;
    border: none;
    width: 20px;
}

QSlider(滑块):

  • ::handle:滑块手柄
  • ::groove:滑槽
  • ::sub-page:滑块左侧区域
  • ::add-page:滑块右侧区域
cpp 复制代码
QSlider::groove:horizontal {
    height: 4px;
    background-color: #e0e0e0;
}

QSlider::handle:horizontal {
    width: 16px;
    height: 16px;
    background-color: #4CAF50;
    border-radius: 8px;
    margin: -6px 0;
}

QSlider::handle:horizontal:hover {
    background-color: #45a049;
}

QProgressBar(进度条):

  • ::chunk:进度块
cpp 复制代码
QProgressBar {
    border: 1px solid #cccccc;
    border-radius: 3px;
    text-align: center;
}

QProgressBar::chunk {
    background-color: #4CAF50;
    border-radius: 2px;
}

QTabWidget(标签页):

  • ::tab-bar:标签栏
  • ::tab:标签页
cpp 复制代码
QTabWidget::pane {
    border: 1px solid #cccccc;
}

QTabBar::tab {
    background-color: #f0f0f0;
    padding: 8px 16px;
    border: 1px solid #cccccc;
}

QTabBar::tab:selected {
    background-color: #4CAF50;
    color: white;
}
子控件的样式设置

基本用法:

cpp 复制代码
// 设置主控件样式
QComboBox {
    border: 1px solid #cccccc;
    padding: 5px;
    border-radius: 3px;
}

// 设置子控件样式
QComboBox::drop-down {
    border: none;
    background-color: #4CAF50;
    width: 30px;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
}

子控件与伪状态组合:

cpp 复制代码
QScrollBar::handle:vertical:hover {
    background-color: #999999;
}

QScrollBar::handle:vertical:pressed {
    background-color: #666666;
}

子控件的尺寸控制:

cpp 复制代码
QComboBox::drop-down {
    width: 30px;  /* 固定宽度 */
}

QScrollBar::handle:vertical {
    min-height: 20px;  /* 最小高度 */
}
子控件的布局控制

子控件位置:

使用subcontrol-position和subcontrol-origin控制子控件的位置:

cpp 复制代码
QComboBox::drop-down {
    subcontrol-origin: padding;
    subcontrol-position: top right;
    width: 30px;
}

子控件对齐:

cpp 复制代码
QComboBox::down-arrow {
    image: url(:/images/arrow.png);
    width: 12px;
    height: 12px;
    subcontrol-origin: padding;
    subcontrol-position: center;
}

实际应用示例:

cpp 复制代码
// 自定义组合框样式
QComboBox {
    border: 1px solid #cccccc;
    border-radius: 3px;
    padding: 5px 10px;
    min-width: 120px;
}

QComboBox::drop-down {
    subcontrol-origin: padding;
    subcontrol-position: top right;
    width: 30px;
    border-left: 1px solid #cccccc;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
    background-color: #f0f0f0;
}

QComboBox::down-arrow {
    image: url(:/images/arrow_down.png);
    width: 12px;
    height: 12px;
}

QComboBox::drop-down:hover {
    background-color: #e0e0e0;
}

2.6 QSS伪状态(Pseudo-States)

伪状态是QSS中非常重要的特性,它允许根据控件的状态(如悬停、按下、选中等)来应用不同的样式。

常用伪状态列表

交互状态:

  1. :hover:鼠标悬停

    cpp 复制代码
    QPushButton:hover {
        background-color: #45a049;
    }
  2. :pressed:按下状态

    cpp 复制代码
    QPushButton:pressed {
        background-color: #3d8b40;
    }
  3. :focus:获得焦点

    cpp 复制代码
    QLineEdit:focus {
        border: 2px solid #2196F3;
    }

选中状态:

  1. :checked:选中(用于复选框、单选按钮等)

    cpp 复制代码
    QCheckBox:checked {
        color: blue;
    }
  2. :unchecked:未选中

    cpp 复制代码
    QCheckBox:unchecked {
        color: gray;
    }

启用/禁用状态:

  1. :enabled:启用状态

    cpp 复制代码
    QPushButton:enabled {
        background-color: #4CAF50;
    }
  2. :disabled:禁用状态

    cpp 复制代码
    QPushButton:disabled {
        background-color: #cccccc;
        color: #999999;
    }

其他状态:

  1. :flat:扁平样式

    cpp 复制代码
    QPushButton:flat {
        border: none;
    }
  2. :default:默认按钮

    cpp 复制代码
    QPushButton:default {
        border: 2px solid #2196F3;
    }
  3. :exclusive:独占按钮组

    cpp 复制代码
    QPushButton:exclusive {
        background-color: #4CAF50;
    }
伪状态的组合使用

多个伪状态组合:

cpp 复制代码
// 悬停且未禁用
QPushButton:hover:!disabled {
    background-color: #45a049;
}

// 选中且悬停
QCheckBox:checked:hover {
    color: darkblue;
}

// 按下且获得焦点
QPushButton:pressed:focus {
    background-color: #3d8b40;
    border: 2px solid #2196F3;
}

否定伪状态:

使用!来否定伪状态:

cpp 复制代码
// 未禁用
QPushButton:!disabled {
    background-color: #4CAF50;
}

// 未选中
QCheckBox:!checked {
    color: gray;
}

完整的状态链示例:

cpp 复制代码
QPushButton {
    /* 默认状态 */
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 10px;
    border-radius: 5px;
}

QPushButton:hover {
    /* 悬停状态 */
    background-color: #45a049;
}

QPushButton:pressed {
    /* 按下状态 */
    background-color: #3d8b40;
}

QPushButton:focus {
    /* 获得焦点 */
    border: 2px solid #2196F3;
}

QPushButton:disabled {
    /* 禁用状态 */
    background-color: #cccccc;
    color: #999999;
}
自定义伪状态

注意:QSS本身不支持自定义伪状态,但可以通过控件的属性(property)来模拟:

cpp 复制代码
// 设置自定义属性
QPushButton *btn = new QPushButton("按钮");
btn->setProperty("state", "loading");

// 使用属性选择器
qApp->setStyleSheet(
    "QPushButton[state=\"loading\"] {"
    "    background-color: #ff9800;"
    "}"
    
    "QPushButton[state=\"success\"] {"
    "    background-color: #4CAF50;"
    "}"
);

动态切换属性:

cpp 复制代码
// 切换状态
void setButtonState(QPushButton *btn, const QString &state) {
    btn->setProperty("state", state);
    btn->style()->unpolish(btn);  // 刷新样式
    btn->style()->polish(btn);
    btn->update();
}
动态伪状态切换

代码控制伪状态:

虽然伪状态(如:hover、:pressed)是自动的,但可以通过代码来触发某些状态:

cpp 复制代码
// 程序化触发焦点
QPushButton *btn = new QPushButton("按钮");
btn->setFocus();  // 触发:focus状态

// 程序化触发选中
QCheckBox *checkbox = new QCheckBox("选项");
checkbox->setChecked(true);  // 触发:checked状态

// 程序化触发禁用
QPushButton *btn = new QPushButton("按钮");
btn->setEnabled(false);  // 触发:disabled状态

状态切换示例:

cpp 复制代码
class CustomButton : public QPushButton {
    Q_OBJECT
public:
    CustomButton(const QString &text, QWidget *parent = nullptr)
        : QPushButton(text, parent)
    {
        setProperty("customState", "normal");
    }
    
    void setCustomState(const QString &state) {
        setProperty("customState", state);
        style()->unpolish(this);
        style()->polish(this);
        update();
    }
};

// 样式定义
qApp->setStyleSheet(
    "CustomButton[customState=\"normal\"] {"
    "    background-color: #4CAF50;"
    "}"
    
    "CustomButton[customState=\"loading\"] {"
    "    background-color: #ff9800;"
    "}"
    
    "CustomButton[customState=\"success\"] {"
    "    background-color: #4CAF50;"
    "}"
);

2.7 QSS高级特性

渐变(Gradients)

线性渐变:

使用qlineargradient()函数创建线性渐变:

cpp 复制代码
QPushButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 white, stop:1 gray);
}

参数说明:

  • x1, y1:起始点坐标(0-1)
  • x2, y2:结束点坐标(0-1)
  • stop:位置 颜色:渐变停止点

渐变示例:

cpp 复制代码
// 垂直渐变(从上到下)
QPushButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 #4CAF50, stop:1 #45a049);
}

// 水平渐变(从左到右)
QPushButton {
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
                                stop:0 #4CAF50, stop:1 #45a049);
}

// 多色渐变
QPushButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 white,
                                stop:0.5 #4CAF50,
                                stop:1 #45a049);
}

径向渐变:

使用qradialgradient()函数创建径向渐变:

cpp 复制代码
QPushButton {
    background: qradialgradient(cx:0.5, cy:0.5, radius:0.5,
                                 fx:0.5, fy:0.5,
                                 stop:0 white, stop:1 gray);
}
图片和图标

背景图片:

cpp 复制代码
QPushButton {
    background-image: url(:/images/button_bg.png);
    background-repeat: no-repeat;
    background-position: center;
}

边框图片:

cpp 复制代码
QPushButton {
    border-image: url(:/images/button_border.png) 10 10 10 10 stretch stretch;
}

图标:

cpp 复制代码
// 在按钮上设置图标(通过代码)
QPushButton *btn = new QPushButton("按钮");
btn->setIcon(QIcon(":/images/icon.png"));

// 通过QSS设置子控件的图标
QComboBox::down-arrow {
    image: url(:/images/arrow_down.png);
    width: 12px;
    height: 12px;
}
透明度和阴影

透明度:

cpp 复制代码
QPushButton {
    background-color: rgba(76, 175, 80, 0.8);  /* RGBA格式 */
}

QWidget {
    background-color: transparent;  /* 透明背景 */
}

阴影效果(通过边框模拟):

cpp 复制代码
QPushButton {
    background-color: #4CAF50;
    border: none;
    border-radius: 5px;
    /* 注意:QSS不直接支持box-shadow,可以通过多层边框模拟 */
}

注意 :QSS不直接支持box-shadow属性,如果需要阴影效果,可以考虑:

  1. 使用图片
  2. 通过代码绘制
  3. 使用多层控件模拟
动画和过渡效果

QSS的动画支持:

QSS本身不支持CSS那样的动画(@keyframes、animation等),但可以通过以下方式实现类似效果:

1. 使用伪状态切换:

cpp 复制代码
QPushButton {
    background-color: #4CAF50;
    transition: background-color 0.3s;  /* 注意:QSS不支持transition */
}

QPushButton:hover {
    background-color: #45a049;
}

注意 :QSS不支持transition属性,状态切换是瞬时的。

2. 通过代码实现动画:

cpp 复制代码
// 使用QPropertyAnimation实现颜色过渡
QPropertyAnimation *animation = new QPropertyAnimation(button, "styleSheet");
animation->setDuration(300);
animation->setStartValue("background-color: #4CAF50;");
animation->setEndValue("background-color: #45a049;");
animation->start();

3. 使用Qt动画框架:

cpp 复制代码
// 使用QGraphicsOpacityEffect实现淡入淡出
QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(button);
button->setGraphicsEffect(effect);

QPropertyAnimation *animation = new QPropertyAnimation(effect, "opacity");
animation->setDuration(300);
animation->setStartValue(0.0);
animation->setEndValue(1.0);
animation->start();

总结:

QSS虽然不支持完整的动画系统,但可以通过:

  • 伪状态切换实现简单的交互效果
  • Qt动画框架实现复杂的动画
  • 代码控制实现动态样式切换


3. QStyle样式引擎

3.1 QStyle基础

QStyle是Qt样式系统的核心引擎,它负责实际绘制控件的外观。理解QStyle的工作原理对于深入掌握Qt样式系统至关重要。

QStyle的作用和职责

QStyle是什么?

QStyle是一个抽象基类,定义了绘制Qt控件的接口。它是Qt样式系统的执行引擎,负责将样式规则转换为实际的视觉效果。

QStyle的核心职责:

  1. 绘制控件:绘制按钮、复选框、滚动条等控件的视觉外观
  2. 计算尺寸:计算控件的最佳尺寸,考虑内容、内边距、边框等
  3. 提供度量:提供样式相关的度量值(如边框宽度、间距、图标大小等)
  4. 平台适配:在不同平台上提供原生或统一的视觉体验
  5. 样式提示:提供样式相关的提示信息(如控件行为、布局规则等)

QStyle的工作流程:

复制代码
控件需要绘制
    ↓
QStyle::drawControl() / drawComplexControl()
    ↓
根据QStyleOption获取控件状态和参数
    ↓
使用QPainter绘制控件外观
    ↓
完成绘制

QStyle与QSS的关系:

  • QSS:提供声明式的样式定义(易用、灵活)
  • QStyle:执行实际的绘制工作(强大、可定制)

QSS样式表最终会被QStyle解析和应用。当控件需要绘制时,QStyle会:

  1. 检查是否有QSS样式表
  2. 如果有,解析QSS规则
  3. 结合QStyle的绘制逻辑
  4. 使用QPainter绘制控件

QStyle的优势:

  1. 完全控制:可以完全控制控件的绘制逻辑
  2. 平台原生:支持平台原生样式,提供原生体验
  3. 性能优秀:直接绘制,性能优于QSS解析
  4. 可扩展性:可以继承QStyle创建自定义样式

QStyle的局限性:

  1. 复杂度高:需要理解Qt的绘制系统
  2. 代码量大:实现完整样式需要大量代码
  3. 维护困难:样式逻辑与代码耦合,不易维护
Qt内置样式列表

Qt提供了多个内置样式,可以在不同平台上使用:

1. Windows样式 (QWindowsStyle / "windows")

  • 平台:Windows
  • 特点:使用Windows原生样式,提供原生Windows外观
  • 使用 :qApp->setStyle("windows");

2. macOS样式 (QMacStyle / "macos")

  • 平台:macOS
  • 特点:使用macOS原生样式,提供原生macOS外观
  • 使用 :qApp->setStyle("macos");

3. Fusion样式 (QFusionStyle / "fusion")

  • 平台:所有平台
  • 特点:跨平台统一样式,现代扁平化设计
  • 使用 :qApp->setStyle("fusion");

4. GTK+样式 (QGtkStyle / "gtk+")

  • 平台:Linux(需要GTK+)
  • 特点:使用GTK+主题系统
  • 使用 :qApp->setStyle("gtk+");

5. Android样式 (QAndroidStyle / "android")

  • 平台:Android
  • 特点:Android Material Design风格
  • 使用 :qApp->setStyle("android");

6. iOS样式 (QIOSStyle / "ios")

  • 平台:iOS
  • 特点:iOS原生风格
  • 使用 :qApp->setStyle("ios");

获取可用样式列表:

cpp 复制代码
QStringList availableStyles = QStyleFactory::keys();
qDebug() << "可用样式:" << availableStyles;
// 输出:可用样式:("windows", "fusion", ...)

设置样式:

cpp 复制代码
// 方式1:使用样式名称
qApp->setStyle("fusion");

// 方式2:创建样式实例
QStyle *style = QStyleFactory::create("fusion");
qApp->setStyle(style);

// 方式3:使用平台特定样式
#ifdef Q_OS_WIN
    qApp->setStyle("windows");
#elif defined(Q_OS_MAC)
    qApp->setStyle("macos");
#else
    qApp->setStyle("fusion");
#endif
QStyle的继承体系

继承层次结构:

复制代码
QStyle (抽象基类)
    ↓
QCommonStyle (通用样式基类)
    ├── QWindowsStyle (Windows样式)
    ├── QMacStyle (macOS样式)
    ├── QFusionStyle (Fusion样式)
    ├── QGtkStyle (GTK+样式)
    ├── QAndroidStyle (Android样式)
    └── QIOSStyle (iOS样式)
    ↓
QProxyStyle (代理样式,用于扩展)

QStyle(抽象基类):

QStyle定义了所有样式必须实现的接口:

cpp 复制代码
class QStyle {
public:
    // 绘制基本元素
    virtual void drawPrimitive(PrimitiveElement pe,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w = nullptr) const = 0;
    
    // 绘制控件元素
    virtual void drawControl(ControlElement element,
                             const QStyleOption *opt,
                             QPainter *p,
                             const QWidget *w = nullptr) const = 0;
    
    // 绘制复杂控件
    virtual void drawComplexControl(ComplexControl cc,
                                    const QStyleOptionComplex *opt,
                                    QPainter *p,
                                    const QWidget *w = nullptr) const = 0;
    
    // 计算控件尺寸
    virtual QSize sizeFromContents(ContentsType ct,
                                    const QStyleOption *opt,
                                    const QSize &contentsSize,
                                    const QWidget *w = nullptr) const = 0;
    
    // 获取像素度量
    virtual int pixelMetric(PixelMetric metric,
                             const QStyleOption *option = nullptr,
                             const QWidget *widget = nullptr) const = 0;
    
    // 获取样式提示
    virtual int styleHint(StyleHint hint,
                          const QStyleOption *option = nullptr,
                          const QWidget *widget = nullptr,
                          QStyleHintReturn *returnData = nullptr) const = 0;
    
    // ... 更多方法
};

QCommonStyle(通用样式基类):

QCommonStyle提供了QStyle接口的默认实现,大多数内置样式都继承自它:

cpp 复制代码
class QCommonStyle : public QStyle {
    // 提供默认实现
    // 子类可以重写特定方法
};

QProxyStyle(代理样式):

QProxyStyle允许在不修改原始样式的情况下扩展样式功能:

cpp 复制代码
class CustomStyle : public QProxyStyle {
public:
    CustomStyle(QStyle *baseStyle = nullptr) : QProxyStyle(baseStyle) {}
    
    void drawPrimitive(PrimitiveElement pe,
                       const QStyleOption *opt,
                       QPainter *p,
                       const QWidget *w = nullptr) const override {
        // 自定义绘制逻辑
        if (pe == PE_PanelButtonCommand) {
            // 自定义按钮绘制
            // ...
        } else {
            // 使用基础样式
            QProxyStyle::drawPrimitive(pe, opt, p, w);
        }
    }
};

// 使用
QStyle *baseStyle = QStyleFactory::create("fusion");
QStyle *customStyle = new CustomStyle(baseStyle);
qApp->setStyle(customStyle);
样式引擎的工作原理

样式引擎的工作流程:

  1. 控件请求绘制 :当控件需要绘制时(如paintEvent()),调用QStyle::drawControl()或drawComplexControl()

  2. 创建样式选项 :控件创建QStyleOption对象,包含绘制所需的所有信息(位置、大小、状态等)

  3. 样式处理:QStyle根据样式选项和控件状态,决定如何绘制

  4. 应用QSS:如果有QSS样式表,QStyle会解析并应用QSS规则

  5. 执行绘制 :使用QPainter实际绘制控件

源码示例(简化版):

cpp 复制代码
// 控件绘制流程(简化)
void QPushButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QStyleOptionButton option;
    initStyleOption(&option);  // 初始化样式选项
    
    // 调用样式引擎绘制
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
}

// QStyle的绘制实现(简化)
void QCommonStyle::drawControl(ControlElement element,
                                const QStyleOption *opt,
                                QPainter *p,
                                const QWidget *widget) const {
    switch (element) {
        case CE_PushButton:
            // 绘制按钮
            drawPushButton(opt, p, widget);
            break;
        case CE_PushButtonLabel:
            // 绘制按钮标签
            drawPushButtonLabel(opt, p, widget);
            break;
        // ... 其他控件元素
    }
}

样式选项(QStyleOption):

QStyleOption包含绘制所需的所有信息:

cpp 复制代码
class QStyleOption {
public:
    QRect rect;              // 绘制区域
    QPalette palette;         // 调色板
    QFont font;              // 字体
    QStyle::State state;     // 状态(启用、禁用、悬停等)
    // ... 更多属性
};

// 控件特定的样式选项
class QStyleOptionButton : public QStyleOption {
public:
    QString text;            // 按钮文本
    QIcon icon;              // 按钮图标
    QSize iconSize;          // 图标大小
    bool flat;               // 是否扁平
    // ... 更多属性
};

样式状态(State):

QStyle::State枚举定义了控件的状态:

cpp 复制代码
enum State {
    State_None = 0x00000000,
    State_Enabled = 0x00000001,      // 启用
    State_Raised = 0x00000002,       // 凸起
    State_Sunken = 0x00000004,       // 凹陷
    State_Off = 0x00000008,          // 关闭
    State_NoChange = 0x00000010,     // 无变化
    State_On = 0x00000020,            // 开启
    State_Horizontal = 0x00000040,   // 水平
    State_HasFocus = 0x00000080,     // 有焦点
    State_Top = 0x00000100,           // 顶部
    State_Bottom = 0x00000200,        // 底部
    State_FocusAtBorder = 0x00000400, // 焦点在边框
    State_AutoRaise = 0x00000800,     // 自动凸起
    State_MouseOver = 0x00001000,     // 鼠标悬停
    State_UpArrow = 0x00002000,       // 上箭头
    State_Selected = 0x00004000,      // 选中
    State_Active = 0x00008000,         // 活动
    State_Window = 0x00010000,        // 窗口
    State_Open = 0x00020000,           // 打开
    State_Children = 0x00040000,       // 有子项
    State_Item = 0x00080000,           // 项
    State_Sibling = 0x00100000,        // 兄弟
    State_Editing = 0x00200000,        // 编辑
    State_KeyboardFocusChange = 0x00400000, // 键盘焦点变化
    State_ReadOnly = 0x02000000,      // 只读
    State_Small = 0x04000000,          // 小
    State_Mini = 0x08000000            // 迷你
};

样式引擎的调用链:

复制代码
控件::paintEvent()
    ↓
QStylePainter::drawControl()
    ↓
QStyle::drawControl()
    ↓
QCommonStyle::drawControl() (或自定义样式)
    ↓
具体绘制方法(如drawPushButton)
    ↓
QPainter绘制

性能考虑:

  1. 缓存样式选项:避免重复创建样式选项
  2. 减少重绘:只在必要时重绘
  3. 使用硬件加速:利用GPU加速绘制
  4. 优化绘制路径:减少不必要的绘制操作

3.2 QStyle绘制系统

QStyle的绘制系统提供了多个绘制方法,用于绘制不同类型的控件元素。理解这些方法对于自定义样式至关重要。

drawPrimitive() - 绘制基本元素

作用:

drawPrimitive()用于绘制基本图形元素,如按钮背景、复选框、单选按钮等。这些是构成控件的"原子"元素。

函数签名:

cpp 复制代码
virtual void drawPrimitive(PrimitiveElement pe,
                           const QStyleOption *opt,
                           QPainter *p,
                           const QWidget *w = nullptr) const = 0;

参数说明:

  • pe:基本元素类型(PrimitiveElement枚举)
  • opt:样式选项,包含绘制所需的信息
  • p:绘制器(QPainter),用于实际绘制
  • w:控件指针(可选)

常用PrimitiveElement类型:

cpp 复制代码
enum PrimitiveElement {
    PE_PanelButtonCommand,    // 按钮面板
    PE_PanelButtonBevel,      // 按钮斜面
    PE_PanelButtonTool,       // 工具按钮面板
    PE_PanelMenuBar,          // 菜单栏面板
    PE_PanelMenu,             // 菜单面板
    PE_PanelTipLabel,         // 提示标签面板
    PE_PanelItemViewItem,     // 列表项面板
    PE_PanelItemViewRow,      // 列表行面板
    PE_PanelScrollAreaCorner, // 滚动区域角落
    PE_Widget,                // 通用控件
    PE_IndicatorArrowUp,      // 上箭头
    PE_IndicatorArrowDown,    // 下箭头
    PE_IndicatorArrowLeft,     // 左箭头
    PE_IndicatorArrowRight,   // 右箭头
    PE_IndicatorBranch,        // 分支指示器
    PE_IndicatorViewItemCheck, // 列表项复选框
    PE_IndicatorItemViewItemCheck, // 列表项复选框(另一种)
    PE_IndicatorCheckBox,     // 复选框
    PE_IndicatorRadioButton,  // 单选按钮
    PE_IndicatorDockWidgetResizeHandle, // 停靠窗口调整手柄
    PE_FrameFocusRect,        // 焦点矩形
    PE_FrameDefaultButton,    // 默认按钮框架
    PE_FrameButtonBevel,      // 按钮斜面框架
    PE_FrameButtonTool,       // 工具按钮框架
    PE_Frame,                 // 通用框架
    PE_FrameLineEdit,         // 行编辑框框架
    PE_FrameMenu,              // 菜单框架
    PE_FrameTabWidget,        // 标签页控件框架
    PE_FrameTabBarBase,        // 标签栏基础框架
    PE_FrameStatusBar,         // 状态栏框架
    PE_FrameStatusBarItem,     // 状态栏项框架
    PE_FrameWindow,            // 窗口框架
    PE_FrameGroupBox,          // 分组框框架
    PE_FrameButtonBevel,       // 按钮斜面框架
    PE_IndicatorToolBarHandle, // 工具栏手柄
    PE_IndicatorToolBarSeparator, // 工具栏分隔符
    PE_IndicatorHeaderArrow,   // 表头箭头
    PE_IndicatorMenuCheckMark, // 菜单复选标记
    PE_IndicatorProgressChunk, // 进度块
    PE_IndicatorSpinMinus,     // 减号指示器
    PE_IndicatorSpinPlus,      // 加号指示器
    PE_IndicatorSpinUp,        // 上箭头指示器
    PE_IndicatorSpinDown,      // 下箭头指示器
    PE_IndicatorToolBarHandle, // 工具栏手柄
    PE_IndicatorColumnViewArrow, // 列视图箭头
    PE_PanelItemViewRow,       // 列表行面板
    PE_PanelItemViewItem,      // 列表项面板
    PE_PanelStatusBar,          // 状态栏面板
    PE_IndicatorTabTear,       // 标签页撕裂指示器
    PE_PanelTipLabel,          // 提示标签面板
    PE_PanelMenu,              // 菜单面板
    PE_PanelButtonCommand,      // 命令按钮面板
    PE_PanelButtonBevel,       // 斜面按钮面板
    PE_PanelButtonTool,        // 工具按钮面板
    PE_PanelLineEdit,          // 行编辑框面板
    PE_IndicatorItemViewItemDrop, // 列表项拖放指示器
    PE_PanelScrollAreaCorner,   // 滚动区域角落面板
    PE_Widget,                 // 通用控件
    // ... 更多类型
};

使用示例:

cpp 复制代码
// 自定义样式:绘制按钮面板
void CustomStyle::drawPrimitive(PrimitiveElement pe,
                                 const QStyleOption *opt,
                                 QPainter *p,
                                 const QWidget *w) const {
    if (pe == PE_PanelButtonCommand) {
        // 绘制按钮面板
        QRect rect = opt->rect;
        QColor backgroundColor;
        
        if (opt->state & QStyle::State_MouseOver) {
            backgroundColor = QColor(69, 160, 73);  // 悬停颜色
        } else if (opt->state & QStyle::State_Sunken) {
            backgroundColor = QColor(61, 139, 64);  // 按下颜色
        } else {
            backgroundColor = QColor(76, 175, 80);  // 默认颜色
        }
        
        p->fillRect(rect, backgroundColor);
        
        // 绘制边框
        p->setPen(QPen(QColor(69, 160, 73), 1));
        p->drawRect(rect.adjusted(0, 0, -1, -1));
    } else {
        // 使用基础样式绘制其他元素
        QProxyStyle::drawPrimitive(pe, opt, p, w);
    }
}
drawControl() - 绘制控件元素

作用:

drawControl()用于绘制完整的控件元素,如按钮、复选框、单选按钮、标签等。它通常内部调用drawPrimitive()来绘制基本元素。

函数签名:

cpp 复制代码
virtual void drawControl(ControlElement element,
                         const QStyleOption *opt,
                         QPainter *p,
                         const QWidget *w = nullptr) const = 0;

常用ControlElement类型:

cpp 复制代码
enum ControlElement {
    CE_PushButton,           // 按钮
    CE_PushButtonBevel,       // 斜面按钮
    CE_PushButtonLabel,       // 按钮标签
    CE_CheckBox,              // 复选框
    CE_CheckBoxLabel,         // 复选框标签
    CE_RadioButton,           // 单选按钮
    CE_RadioButtonLabel,      // 单选按钮标签
    CE_TabBarTab,             // 标签页
    CE_TabBarTabShape,        // 标签页形状
    CE_TabBarTabLabel,        // 标签页标签
    CE_ProgressBar,           // 进度条
    CE_ProgressBarGroove,     // 进度条槽
    CE_ProgressBarContents,   // 进度条内容
    CE_ProgressBarLabel,      // 进度条标签
    CE_MenuItem,              // 菜单项
    CE_MenuScroller,          // 菜单滚动器
    CE_MenuVMargin,           // 菜单垂直边距
    CE_MenuHMargin,           // 菜单水平边距
    CE_MenuTearoff,           // 菜单撕下
    CE_MenuEmptyArea,         // 菜单空区域
    CE_MenuBarItem,           // 菜单栏项
    CE_MenuBarEmptyArea,      // 菜单栏空区域
    CE_ToolButtonLabel,       // 工具按钮标签
    CE_Header,                 // 表头
    CE_HeaderSection,          // 表头节
    CE_HeaderLabel,            // 表头标签
    CE_ScrollBarAddLine,       // 滚动条增加线
    CE_ScrollBarSubLine,       // 滚动条减少线
    CE_ScrollBarAddPage,       // 滚动条增加页
    CE_ScrollBarSubPage,       // 滚动条减少页
    CE_ScrollBarSlider,        // 滚动条滑块
    CE_ScrollBarFirst,         // 滚动条第一个
    CE_ScrollBarLast,          // 滚动条最后一个
    CE_RubberBand,             // 橡皮筋选择框
    CE_FocusFrame,             // 焦点框架
    CE_ItemViewItem,           // 列表项
    CE_ShapedFrame,            // 形状框架
    CE_ToolBoxTab,             // 工具箱标签
    CE_SizeGrip,               // 大小调整手柄
    CE_Splitter,               // 分割器
    CE_ToolBar,                // 工具栏
    CE_ToolButton,             // 工具按钮
    CE_DockWidgetTitle,        // 停靠窗口标题
    CE_HeaderEmptyArea,        // 表头空区域
    CE_ColumnViewGrip,        // 列视图手柄
    CE_ItemViewItemCheckIndicator, // 列表项复选指示器
    CE_ShapedFrame,            // 形状框架
    // ... 更多类型
};

使用示例:

cpp 复制代码
// 自定义样式:绘制按钮
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_PushButton) {
        const QStyleOptionButton *btnOpt = qstyleoption_cast<const QStyleOptionButton *>(opt);
        if (!btnOpt) return;
        
        // 绘制按钮背景
        drawPrimitive(PE_PanelButtonCommand, opt, p, w);
        
        // 绘制按钮标签(文本和图标)
        QStyleOptionButton subOpt = *btnOpt;
        subOpt.rect = subControlRect(CC_PushButton, btnOpt, SC_PushButtonLabel, w);
        drawControl(CE_PushButtonLabel, &subOpt, p, w);
    } else {
        // 使用基础样式绘制其他元素
        QProxyStyle::drawControl(element, opt, p, w);
    }
}
drawComplexControl() - 绘制复杂控件

作用:

drawComplexControl()用于绘制复杂控件,如组合框、滚动条、滑块、标题栏等。这些控件由多个子控件组成。

函数签名:

cpp 复制代码
virtual void drawComplexControl(ComplexControl cc,
                                const QStyleOptionComplex *opt,
                                QPainter *p,
                                const QWidget *w = nullptr) const = 0;

常用ComplexControl类型:

cpp 复制代码
enum ComplexControl {
    CC_SpinBox,              // 数字输入框
    CC_ComboBox,             // 组合框
    CC_ScrollBar,             // 滚动条
    CC_Slider,                // 滑块
    CC_ToolButton,            // 工具按钮
    CC_TitleBar,              // 标题栏
    CC_Q3ListView,            // Q3列表视图
    CC_Dial,                  // 拨号盘
    CC_GroupBox,              // 分组框
    CC_MdiControls,           // MDI控件
    CC_CustomBase = 0xf0000000 // 自定义基础
};

使用示例:

cpp 复制代码
// 自定义样式:绘制组合框
void CustomStyle::drawComplexControl(ComplexControl cc,
                                     const QStyleOptionComplex *opt,
                                     QPainter *p,
                                     const QWidget *w) const {
    if (cc == CC_ComboBox) {
        const QStyleOptionComboBox *cbOpt = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
        if (!cbOpt) return;
        
        // 绘制组合框框架
        QRect editRect = subControlRect(CC_ComboBox, cbOpt, SC_ComboBoxEditField, w);
        QStyleOptionFrame frameOpt;
        frameOpt.rect = editRect;
        frameOpt.state = opt->state;
        drawPrimitive(PE_FrameLineEdit, &frameOpt, p, w);
        
        // 绘制下拉箭头
        QRect arrowRect = subControlRect(CC_ComboBox, cbOpt, SC_ComboBoxArrow, w);
        QStyleOption arrowOpt;
        arrowOpt.rect = arrowRect;
        arrowOpt.state = opt->state;
        drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, w);
    } else {
        // 使用基础样式绘制其他复杂控件
        QProxyStyle::drawComplexControl(cc, opt, p, w);
    }
}
drawItemText() - 绘制文本

作用:

drawItemText()用于绘制文本,考虑对齐、换行、省略等。

函数签名:

cpp 复制代码
virtual void drawItemText(QPainter *painter,
                           const QRect &rect,
                           int flags,
                           const QPalette &pal,
                           bool enabled,
                           const QString &text,
                           QPalette::ColorRole textRole = QPalette::NoRole) const;

参数说明:

  • painter:绘制器
  • rect:文本绘制区域
  • flags:对齐标志(Qt::AlignLeft、Qt::AlignCenter等)
  • pal:调色板
  • enabled:是否启用
  • text:要绘制的文本
  • textRole:文本颜色角色

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_PushButtonLabel) {
        const QStyleOptionButton *btnOpt = qstyleoption_cast<const QStyleOptionButton *>(opt);
        if (!btnOpt) return;
        
        // 绘制文本
        drawItemText(p, btnOpt->rect,
                     Qt::AlignCenter | Qt::TextShowMnemonic,
                     btnOpt->palette,
                     btnOpt->state & State_Enabled,
                     btnOpt->text,
                     QPalette::ButtonText);
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}
drawItemPixmap() - 绘制图片

作用:

drawItemPixmap()用于绘制图片(QPixmap),考虑对齐和缩放。

函数签名:

cpp 复制代码
virtual void drawItemPixmap(QPainter *painter,
                             const QRect &rect,
                             int alignment,
                             const QPixmap &pixmap) const;

参数说明:

  • painter:绘制器
  • rect:图片绘制区域
  • alignment:对齐标志
  • pixmap:要绘制的图片

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_PushButtonLabel) {
        const QStyleOptionButton *btnOpt = qstyleoption_cast<const QStyleOptionButton *>(opt);
        if (!btnOpt) return;
        
        // 绘制图标
        if (!btnOpt->icon.isNull()) {
            QRect iconRect = btnOpt->rect;
            iconRect.setWidth(btnOpt->iconSize.width());
            drawItemPixmap(p, iconRect,
                          Qt::AlignCenter,
                          btnOpt->icon.pixmap(btnOpt->iconSize));
        }
        
        // 绘制文本
        QRect textRect = btnOpt->rect;
        textRect.adjust(btnOpt->iconSize.width() + 4, 0, 0, 0);
        drawItemText(p, textRect,
                     Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic,
                     btnOpt->palette,
                     btnOpt->state & State_Enabled,
                     btnOpt->text,
                     QPalette::ButtonText);
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}

3.3 QStyle布局系统

QStyle不仅负责绘制控件,还负责计算控件的尺寸、间距等布局相关的度量值。

sizeFromContents() - 计算控件尺寸

作用:

sizeFromContents()根据控件的内容尺寸,计算控件的最佳尺寸(包括内边距、边框等)。

函数签名:

cpp 复制代码
virtual QSize sizeFromContents(ContentsType ct,
                                const QStyleOption *opt,
                                const QSize &contentsSize,
                                const QWidget *w = nullptr) const = 0;

参数说明:

  • ct:内容类型(ContentsType枚举)
  • opt:样式选项
  • contentsSize:内容尺寸(不包括内边距、边框等)
  • w:控件指针(可选)

常用ContentsType类型:

cpp 复制代码
enum ContentsType {
    CT_PushButton,        // 按钮
    CT_CheckBox,         // 复选框
    CT_RadioButton,       // 单选按钮
    CT_ToolButton,        // 工具按钮
    CT_ComboBox,          // 组合框
    CT_Splitter,          // 分割器
    CT_ProgressBar,       // 进度条
    CT_MenuItem,          // 菜单项
    CT_MenuBarItem,       // 菜单栏项
    CT_MenuCheckMark,     // 菜单复选标记
    CT_MenuItemCheckMark, // 菜单项复选标记
    CT_SizeGrip,          // 大小调整手柄
    CT_TabBarTab,         // 标签页
    CT_HeaderSection,     // 表头节
    CT_LineEdit,           // 行编辑框
    CT_Menu,              // 菜单
    CT_MenuBar,           // 菜单栏
    CT_ToolBar,           // 工具栏
    CT_CustomBase = 0xf0000000 // 自定义基础
};

使用示例:

cpp 复制代码
// 自定义样式:计算按钮尺寸
QSize CustomStyle::sizeFromContents(ContentsType ct,
                                     const QStyleOption *opt,
                                     const QSize &contentsSize,
                                     const QWidget *w) const {
    if (ct == CT_PushButton) {
        const QStyleOptionButton *btnOpt = qstyleoption_cast<const QStyleOptionButton *>(opt);
        if (!btnOpt) return contentsSize;
        
        // 计算按钮尺寸 = 内容尺寸 + 内边距 + 边框
        QSize size = contentsSize;
        size += QSize(20, 10);  // 左右内边距20px,上下内边距10px
        size += QSize(4, 4);    // 边框宽度2px * 2
        
        return size;
    }
    
    return QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
}
layoutSpacing() - 计算布局间距

作用:

layoutSpacing()计算布局中控件之间的间距。

函数签名:

cpp 复制代码
virtual int layoutSpacing(QSizePolicy::ControlType control1,
                          QSizePolicy::ControlType control2,
                          Qt::Orientation orientation,
                          const QStyleOption *option = nullptr,
                          const QWidget *widget = nullptr) const;

参数说明:

  • control1:第一个控件的类型
  • control2:第二个控件的类型
  • orientation:方向(水平或垂直)
  • option:样式选项(可选)
  • widget:控件指针(可选)

使用示例:

cpp 复制代码
// 自定义样式:计算布局间距
int CustomStyle::layoutSpacing(QSizePolicy::ControlType control1,
                                QSizePolicy::ControlType control2,
                                Qt::Orientation orientation,
                                const QStyleOption *option,
                                const QWidget *widget) const {
    // 按钮之间的间距
    if (control1 == QSizePolicy::PushButton && control2 == QSizePolicy::PushButton) {
        return orientation == Qt::Horizontal ? 10 : 5;
    }
    
    // 使用基础样式的间距
    return QProxyStyle::layoutSpacing(control1, control2, orientation, option, widget);
}
pixelMetric() - 获取像素度量

作用:

pixelMetric()返回样式相关的像素度量值,如边框宽度、间距、图标大小等。

函数签名:

cpp 复制代码
virtual int pixelMetric(PixelMetric metric,
                         const QStyleOption *option = nullptr,
                         const QWidget *widget = nullptr) const = 0;

常用PixelMetric类型:

cpp 复制代码
enum PixelMetric {
    PM_ButtonMargin,           // 按钮边距
    PM_ButtonDefaultIndicator, // 默认按钮指示器宽度
    PM_MenuButtonIndicator,    // 菜单按钮指示器宽度
    PM_ButtonShiftHorizontal,   // 按钮水平偏移
    PM_ButtonShiftVertical,    // 按钮垂直偏移
    PM_DefaultFrameWidth,       // 默认框架宽度
    PM_SpinBoxFrameWidth,      // 数字输入框框架宽度
    PM_ComboBoxFrameWidth,      // 组合框框架宽度
    PM_MaximumDragDistance,    // 最大拖拽距离
    PM_ScrollBarExtent,         // 滚动条宽度/高度
    PM_ScrollBarSliderMin,     // 滚动条滑块最小尺寸
    PM_SliderThickness,         // 滑块厚度
    PM_SliderControlThickness,  // 滑块控制厚度
    PM_SliderLength,            // 滑块长度
    PM_SliderTickmarkOffset,    // 滑块刻度偏移
    PM_SliderSpaceAvailable,    // 滑块可用空间
    PM_DockWidgetSeparatorExtent, // 停靠窗口分隔符宽度
    PM_DockWidgetHandleExtent,   // 停靠窗口手柄宽度
    PM_DockWidgetFrameWidth,     // 停靠窗口框架宽度
    PM_DockWidgetTitleMargin,    // 停靠窗口标题边距
    PM_MenuBarPanelWidth,        // 菜单栏面板宽度
    PM_MenuBarItemSpacing,       // 菜单栏项间距
    PM_MenuBarHMargin,            // 菜单栏水平边距
    PM_MenuBarVMargin,            // 菜单栏垂直边距
    PM_ToolBarFrameWidth,        // 工具栏框架宽度
    PM_ToolBarHandleExtent,      // 工具栏手柄宽度
    PM_ToolBarItemSpacing,       // 工具栏项间距
    PM_ToolBarItemMargin,        // 工具栏项边距
    PM_ToolBarSeparatorExtent,   // 工具栏分隔符宽度
    PM_ToolBarExtensionExtent,    // 工具栏扩展宽度
    PM_TabBarTabOverlap,         // 标签页重叠
    PM_TabBarTabHSpace,          // 标签页水平空间
    PM_TabBarTabVSpace,          // 标签页垂直空间
    PM_TabBarBaseHeight,         // 标签栏基础高度
    PM_TabBarBaseOverlap,        // 标签栏基础重叠
    PM_TabBarTabShiftHorizontal, // 标签页水平偏移
    PM_TabBarTabShiftVertical,   // 标签页垂直偏移
    PM_TabBarScrollButtonWidth,  // 标签栏滚动按钮宽度
    PM_TabBarTabShiftHorizontal, // 标签页水平偏移
    PM_TabBarTabShiftVertical,   // 标签页垂直偏移
    PM_ProgressBarChunkWidth,     // 进度条块宽度
    PM_SplitterWidth,             // 分割器宽度
    PM_TitleBarHeight,             // 标题栏高度
    PM_IndicatorWidth,            // 指示器宽度
    PM_IndicatorHeight,            // 指示器高度
    PM_ExclusiveIndicatorWidth,    // 独占指示器宽度
    PM_ExclusiveIndicatorHeight,   // 独占指示器高度
    PM_MenuPanelWidth,             // 菜单面板宽度
    PM_MenuHMargin,                // 菜单水平边距
    PM_MenuVMargin,                // 菜单垂直边距
    PM_MenuScrollerHeight,         // 菜单滚动器高度
    PM_MenuTearoffHeight,          // 菜单撕下高度
    PM_MenuDesktopFrameWidth,      // 菜单桌面框架宽度
    PM_MenuBarPanelWidth,          // 菜单栏面板宽度
    PM_HeaderMargin,               // 表头边距
    PM_HeaderMarkSize,             // 表头标记大小
    PM_HeaderGripMargin,           // 表头手柄边距
    PM_TabBarIconSize,             // 标签栏图标大小
    PM_ListViewIconSize,           // 列表视图图标大小
    PM_IconViewIconSize,           // 图标视图图标大小
    PM_SmallIconSize,              // 小图标大小
    PM_LargeIconSize,              // 大图标大小
    PM_FocusFrameHMargin,          // 焦点框架水平边距
    PM_FocusFrameVMargin,          // 焦点框架垂直边距
    PM_FocusFrameWidth,            // 焦点框架宽度
    PM_LayoutLeftMargin,           // 布局左边距
    PM_LayoutTopMargin,            // 布局上边距
    PM_LayoutRightMargin,          // 布局右边距
    PM_LayoutBottomMargin,         // 布局下边距
    PM_LayoutHorizontalSpacing,    // 布局水平间距
    PM_LayoutVerticalSpacing,      // 布局垂直间距
    PM_MaximumDragDistance,        // 最大拖拽距离
    PM_ScrollBarExtent,            // 滚动条宽度/高度
    PM_ScrollBarSliderMin,         // 滚动条滑块最小尺寸
    PM_SliderThickness,            // 滑块厚度
    PM_SliderControlThickness,     // 滑块控制厚度
    PM_SliderLength,               // 滑块长度
    PM_SliderTickmarkOffset,       // 滑块刻度偏移
    PM_SliderSpaceAvailable,       // 滑块可用空间
    PM_DockWidgetSeparatorExtent,  // 停靠窗口分隔符宽度
    PM_DockWidgetHandleExtent,     // 停靠窗口手柄宽度
    PM_DockWidgetFrameWidth,       // 停靠窗口框架宽度
    PM_DockWidgetTitleMargin,      // 停靠窗口标题边距
    PM_HeaderDefaultSectionSizeHorizontal, // 表头默认节大小(水平)
    PM_HeaderDefaultSectionSizeVertical,   // 表头默认节大小(垂直)
    PM_TabBarIconSize,             // 标签栏图标大小
    PM_TabBarTabShiftHorizontal,   // 标签页水平偏移
    PM_TabBarTabShiftVertical,     // 标签页垂直偏移
    PM_ProgressBarChunkWidth,      // 进度条块宽度
    PM_SplitterWidth,              // 分割器宽度
    PM_TitleBarHeight,             // 标题栏高度
    PM_IndicatorWidth,             // 指示器宽度
    PM_IndicatorHeight,            // 指示器高度
    PM_ExclusiveIndicatorWidth,    // 独占指示器宽度
    PM_ExclusiveIndicatorHeight,   // 独占指示器高度
    PM_MenuPanelWidth,             // 菜单面板宽度
    PM_MenuHMargin,                // 菜单水平边距
    PM_MenuVMargin,                // 菜单垂直边距
    PM_MenuScrollerHeight,         // 菜单滚动器高度
    PM_MenuTearoffHeight,          // 菜单撕下高度
    PM_MenuDesktopFrameWidth,     // 菜单桌面框架宽度
    PM_MenuBarPanelWidth,          // 菜单栏面板宽度
    PM_HeaderMargin,               // 表头边距
    PM_HeaderMarkSize,             // 表头标记大小
    PM_HeaderGripMargin,           // 表头手柄边距
    PM_TabBarIconSize,             // 标签栏图标大小
    PM_ListViewIconSize,           // 列表视图图标大小
    PM_IconViewIconSize,           // 图标视图图标大小
    PM_SmallIconSize,              // 小图标大小
    PM_LargeIconSize,              // 大图标大小
    PM_FocusFrameHMargin,          // 焦点框架水平边距
    PM_FocusFrameVMargin,          // 焦点框架垂直边距
    PM_FocusFrameWidth,            // 焦点框架宽度
    PM_LayoutLeftMargin,           // 布局左边距
    PM_LayoutTopMargin,            // 布局上边距
    PM_LayoutRightMargin,          // 布局右边距
    PM_LayoutBottomMargin,         // 布局下边距
    PM_LayoutHorizontalSpacing,   // 布局水平间距
    PM_LayoutVerticalSpacing,      // 布局垂直间距
    // ... 更多类型
};

使用示例:

cpp 复制代码
// 自定义样式:返回像素度量
int CustomStyle::pixelMetric(PixelMetric metric,
                              const QStyleOption *option,
                              const QWidget *widget) const {
    switch (metric) {
        case PM_ButtonMargin:
            return 8;  // 按钮边距8px
        case PM_DefaultFrameWidth:
            return 2;  // 默认框架宽度2px
        case PM_SmallIconSize:
            return 16; // 小图标大小16px
        case PM_LargeIconSize:
            return 32; // 大图标大小32px
        default:
            return QProxyStyle::pixelMetric(metric, option, widget);
    }
}
styleHint() - 获取样式提示

作用:

styleHint()返回样式相关的提示信息,如控件行为、布局规则等。

函数签名:

cpp 复制代码
virtual int styleHint(StyleHint hint,
                      const QStyleOption *option = nullptr,
                      const QWidget *widget = nullptr,
                      QStyleHintReturn *returnData = nullptr) const = 0;

常用StyleHint类型:

cpp 复制代码
enum StyleHint {
    SH_EtchDisabledText,          // 禁用文本是否使用蚀刻效果
    SH_DitherDisabledText,         // 禁用文本是否使用抖动
    SH_ScrollBar_ContextMenu,      // 滚动条上下文菜单
    SH_ScrollBar_LeftClickAbsolutePosition, // 滚动条左键绝对位置
    SH_ScrollBar_RightClickAbsolutePosition, // 滚动条右键绝对位置
    SH_ScrollBar_ScrollWhenPointerLeavesControl, // 指针离开控件时是否滚动
    SH_ScrollBar_StopMouseOverSlider, // 鼠标悬停滑块时是否停止
    SH_ScrollBar_StopMouseOverSlider, // 鼠标悬停滑块时是否停止
    SH_TabBar_Alignment,            // 标签栏对齐
    SH_Header_ArrowAlignment,      // 表头箭头对齐
    SH_Slider_SnapToValue,         // 滑块是否捕捉到值
    SH_Slider_SloppyKeyHandler,     // 滑块是否使用宽松的键盘处理
    SH_ProgressDialog_CenterCancelButton, // 进度对话框取消按钮是否居中
    SH_ProgressDialog_TextLabelAlignment, // 进度对话框文本标签对齐
    SH_ProgressDialog_ElideText,    // 进度对话框文本是否省略
    SH_ScrollView_FrameOnlyAroundContents, // 滚动视图框架是否只围绕内容
    SH_ScrollBar_LeftClickAbsolutePosition, // 滚动条左键绝对位置
    SH_ScrollBar_RightClickAbsolutePosition, // 滚动条右键绝对位置
    SH_MenuBar_AltKeyNavigation,    // 菜单栏Alt键导航
    SH_ComboBox_Popup,              // 组合框弹出方式
    SH_ComboBox_ListMouseTracking,  // 组合框列表鼠标跟踪
    SH_Menu_Scrollable,              // 菜单是否可滚动
    SH_Menu_SloppySubMenus,         // 菜单是否使用宽松的子菜单
    SH_Menu_SubMenuPopupDelay,      // 菜单子菜单弹出延迟
    SH_Menu_SubMenuUniDirection,     // 菜单子菜单单向
    SH_Menu_SubMenuSloppySelectOtherActions, // 菜单子菜单宽松选择其他操作
    SH_Menu_SubMenuSloppyCloseTimeout, // 菜单子菜单宽松关闭超时
    SH_Menu_SubMenuResetWhenReenteringParent, // 菜单子菜单重新进入父菜单时重置
    SH_Menu_SubMenuDontStartSloppyOnLeave, // 菜单子菜单离开时不开始宽松
    SH_Menu_SloppySubMenus,         // 菜单是否使用宽松的子菜单
    SH_Menu_SubMenuPopupDelay,      // 菜单子菜单弹出延迟
    SH_Menu_SubMenuUniDirection,     // 菜单子菜单单向
    SH_Menu_SubMenuSloppySelectOtherActions, // 菜单子菜单宽松选择其他操作
    SH_Menu_SubMenuSloppyCloseTimeout, // 菜单子菜单宽松关闭超时
    SH_Menu_SubMenuResetWhenReenteringParent, // 菜单子菜单重新进入父菜单时重置
    SH_Menu_SubMenuDontStartSloppyOnLeave, // 菜单子菜单离开时不开始宽松
    SH_ItemView_ActivateItemOnSingleClick, // 列表视图是否单击激活项
    SH_ItemView_ChangeHighlightOnFocus, // 列表视图焦点时是否改变高亮
    SH_ItemView_ShowDecorationSelected, // 列表视图是否显示装饰选中
    SH_ItemView_ArrowKeysNavigateIntoChildren, // 列表视图箭头键是否导航到子项
    SH_ItemView_MovementWithoutUpdatingSelection, // 列表视图是否不更新选择就移动
    SH_ItemView_ScrollMode,         // 列表视图滚动模式
    SH_TabBar_ElideMode,            // 标签栏省略模式
    SH_TabBar_CloseButtonPosition,  // 标签栏关闭按钮位置
    SH_DialogButtonBox_ButtonsHaveIcons, // 对话框按钮框按钮是否有图标
    SH_DialogButtonBox_Layout,      // 对话框按钮框布局
    SH_ComboBox_Editable,            // 组合框是否可编辑
    SH_ComboBox_Popup,              // 组合框弹出方式
    SH_ComboBox_ListMouseTracking,  // 组合框列表鼠标跟踪
    SH_ComboBox_AllowWheelScrolling, // 组合框是否允许滚轮滚动
    SH_MessageBox_TextInteractionFlags, // 消息框文本交互标志
    SH_MessageBox_CenterButtons,     // 消息框按钮是否居中
    SH_SpinBox_StepModifier,        // 数字输入框步进修改器
    SH_SpinBox_ButtonsInsideFrame,   // 数字输入框按钮是否在框架内
    SH_ToolButton_PopupDelay,       // 工具按钮弹出延迟
    SH_Menu_KeyboardSearch,         // 菜单键盘搜索
    SH_Menu_AllowActiveAndDisabled, // 菜单是否允许活动和禁用
    SH_Menu_SpaceActivatesItem,     // 菜单空格是否激活项
    SH_Menu_SubMenuPopupDelay,      // 菜单子菜单弹出延迟
    SH_ItemView_ActivateItemOnSingleClick, // 列表视图是否单击激活项
    SH_ItemView_ChangeHighlightOnFocus, // 列表视图焦点时是否改变高亮
    SH_ItemView_ShowDecorationSelected, // 列表视图是否显示装饰选中
    SH_ItemView_ArrowKeysNavigateIntoChildren, // 列表视图箭头键是否导航到子项
    SH_ItemView_MovementWithoutUpdatingSelection, // 列表视图是否不更新选择就移动
    SH_ItemView_ScrollMode,         // 列表视图滚动模式
    SH_TabBar_ElideMode,            // 标签栏省略模式
    SH_TabBar_CloseButtonPosition,  // 标签栏关闭按钮位置
    SH_DialogButtonBox_ButtonsHaveIcons, // 对话框按钮框按钮是否有图标
    SH_DialogButtonBox_Layout,      // 对话框按钮框布局
    SH_ComboBox_Editable,            // 组合框是否可编辑
    SH_ComboBox_Popup,              // 组合框弹出方式
    SH_ComboBox_ListMouseTracking,  // 组合框列表鼠标跟踪
    SH_ComboBox_AllowWheelScrolling, // 组合框是否允许滚轮滚动
    SH_MessageBox_TextInteractionFlags, // 消息框文本交互标志
    SH_MessageBox_CenterButtons,     // 消息框按钮是否居中
    SH_SpinBox_StepModifier,        // 数字输入框步进修改器
    SH_SpinBox_ButtonsInsideFrame,   // 数字输入框按钮是否在框架内
    SH_ToolButton_PopupDelay,       // 工具按钮弹出延迟
    SH_Menu_KeyboardSearch,         // 菜单键盘搜索
    SH_Menu_AllowActiveAndDisabled, // 菜单是否允许活动和禁用
    SH_Menu_SpaceActivatesItem,     // 菜单空格是否激活项
    SH_Menu_SubMenuPopupDelay,      // 菜单子菜单弹出延迟
    // ... 更多类型
};

使用示例:

cpp 复制代码
// 自定义样式:返回样式提示
int CustomStyle::styleHint(StyleHint hint,
                            const QStyleOption *option,
                            const QWidget *widget,
                            QStyleHintReturn *returnData) const {
    switch (hint) {
        case SH_EtchDisabledText:
            return true;  // 禁用文本使用蚀刻效果
        case SH_ItemView_ActivateItemOnSingleClick:
            return true;  // 列表视图单击激活项
        case SH_TabBar_ElideMode:
            return Qt::ElideRight;  // 标签栏文本右省略
        default:
            return QProxyStyle::styleHint(hint, option, widget, returnData);
    }
}

3.4 QStyle标准图标

QStyle提供了标准图标的支持,这些图标在不同平台上可能有不同的外观,但功能相同。

standardIcon() - 获取标准图标

作用:

standardIcon()返回标准图标(QIcon),用于工具栏、菜单等。

函数签名:

cpp 复制代码
virtual QIcon standardIcon(StandardPixmap standardIcon,
                           const QStyleOption *option = nullptr,
                           const QWidget *widget = nullptr) const;

常用StandardPixmap类型:

cpp 复制代码
enum StandardPixmap {
    SP_TitleBarMinButton,      // 标题栏最小化按钮
    SP_TitleBarMaxButton,      // 标题栏最大化按钮
    SP_TitleBarCloseButton,    // 标题栏关闭按钮
    SP_TitleBarNormalButton,   // 标题栏正常按钮
    SP_TitleBarShadeButton,    // 标题栏阴影按钮
    SP_TitleBarUnshadeButton,  // 标题栏取消阴影按钮
    SP_TitleBarContextHelpButton, // 标题栏上下文帮助按钮
    SP_MessageBoxInformation,  // 消息框信息图标
    SP_MessageBoxWarning,      // 消息框警告图标
    SP_MessageBoxCritical,     // 消息框严重图标
    SP_MessageBoxQuestion,     // 消息框问题图标
    SP_DesktopIcon,            // 桌面图标
    SP_TrashIcon,              // 垃圾桶图标
    SP_ComputerIcon,           // 计算机图标
    SP_DriveFDIcon,            // 软盘驱动器图标
    SP_DriveHDIcon,            // 硬盘驱动器图标
    SP_DriveCDIcon,            // CD驱动器图标
    SP_DriveDVDIcon,           // DVD驱动器图标
    SP_DriveNetIcon,           // 网络驱动器图标
    SP_DirOpenIcon,            // 打开目录图标
    SP_DirClosedIcon,           // 关闭目录图标
    SP_DirIcon,                // 目录图标
    SP_DirLinkIcon,            // 目录链接图标
    SP_FileIcon,               // 文件图标
    SP_FileLinkIcon,           // 文件链接图标
    SP_FileDialogStart,        // 文件对话框开始
    SP_FileDialogEnd,           // 文件对话框结束
    SP_FileDialogToParent,      // 文件对话框到父目录
    SP_FileDialogNewFolder,     // 文件对话框新建文件夹
    SP_FileDialogDetailedView, // 文件对话框详细视图
    SP_FileDialogInfoView,      // 文件对话框信息视图
    SP_FileDialogContentsView,  // 文件对话框内容视图
    SP_FileDialogListView,      // 文件对话框列表视图
    SP_FileDialogBack,          // 文件对话框返回
    SP_DockWidgetCloseButton,   // 停靠窗口关闭按钮
    SP_ToolBarHorizontalExtensionButton, // 工具栏水平扩展按钮
    SP_ToolBarVerticalExtensionButton,   // 工具栏垂直扩展按钮
    SP_DialogOkButton,          // 对话框确定按钮
    SP_DialogCancelButton,      // 对话框取消按钮
    SP_DialogHelpButton,        // 对话框帮助按钮
    SP_DialogOpenButton,        // 对话框打开按钮
    SP_DialogSaveButton,        // 对话框保存按钮
    SP_DialogCloseButton,       // 对话框关闭按钮
    SP_DialogApplyButton,       // 对话框应用按钮
    SP_DialogResetButton,       // 对话框重置按钮
    SP_DialogDiscardButton,     // 对话框丢弃按钮
    SP_DialogYesButton,         // 对话框是按钮
    SP_DialogNoButton,          // 对话框否按钮
    SP_ArrowUp,                 // 上箭头
    SP_ArrowDown,               // 下箭头
    SP_ArrowLeft,               // 左箭头
    SP_ArrowRight,              // 右箭头
    SP_ArrowBack,               // 返回箭头
    SP_ArrowForward,            // 前进箭头
    SP_FileIcon,                // 文件图标
    SP_DirIcon,                 // 目录图标
    SP_DirLinkIcon,             // 目录链接图标
    SP_FileLinkIcon,            // 文件链接图标
    SP_ComputerIcon,            // 计算机图标
    SP_DesktopIcon,             // 桌面图标
    SP_TrashIcon,               // 垃圾桶图标
    SP_DriveFDIcon,             // 软盘驱动器图标
    SP_DriveHDIcon,             // 硬盘驱动器图标
    SP_DriveCDIcon,             // CD驱动器图标
    SP_DriveDVDIcon,            // DVD驱动器图标
    SP_DriveNetIcon,             // 网络驱动器图标
    SP_DirOpenIcon,              // 打开目录图标
    SP_DirClosedIcon,            // 关闭目录图标
    // ... 更多类型
};

使用示例:

cpp 复制代码
// 获取标准图标
QIcon closeIcon = style()->standardIcon(QStyle::SP_TitleBarCloseButton);
QIcon infoIcon = style()->standardIcon(QStyle::SP_MessageBoxInformation);

// 在按钮上使用
QPushButton *closeBtn = new QPushButton(closeIcon, "关闭");
QPushButton *infoBtn = new QPushButton(infoIcon, "信息");
standardPixmap() - 获取标准像素图

作用:

standardPixmap()返回标准像素图(QPixmap),用于绘制。

函数签名:

cpp 复制代码
virtual QPixmap standardPixmap(StandardPixmap standardPixmap,
                               const QStyleOption *option = nullptr,
                               const QWidget *widget = nullptr) const;

使用示例:

cpp 复制代码
// 获取标准像素图
QPixmap closePixmap = style()->standardPixmap(QStyle::SP_TitleBarCloseButton);

// 在标签上使用
QLabel *label = new QLabel();
label->setPixmap(closePixmap);
系统图标的使用

获取系统图标:

cpp 复制代码
// 通过QStyle获取
QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxInformation);

// 在控件上使用
QPushButton *btn = new QPushButton(icon, "信息");
QAction *action = new QAction(icon, "信息", this);

在菜单中使用:

cpp 复制代码
QMenu *menu = new QMenu(this);
QAction *openAction = menu->addAction(
    style()->standardIcon(QStyle::SP_DialogOpenButton),
    "打开"
);
QAction *saveAction = menu->addAction(
    style()->standardIcon(QStyle::SP_DialogSaveButton),
    "保存"
);
自定义标准图标

重写standardIcon():

cpp 复制代码
class CustomStyle : public QProxyStyle {
public:
    QIcon standardIcon(StandardPixmap standardIcon,
                       const QStyleOption *option = nullptr,
                       const QWidget *widget = nullptr) const override {
        // 自定义特定图标
        if (standardIcon == SP_MessageBoxInformation) {
            return QIcon(":/icons/custom_info.png");
        }
        
        // 使用基础样式的图标
        return QProxyStyle::standardIcon(standardIcon, option, widget);
    }
};

3.5 Qt内置样式详解

Windows样式

特点:

  • 平台:Windows
  • 外观:使用Windows原生样式,提供原生Windows外观
  • 适用场景:Windows平台应用程序,需要原生体验

使用:

cpp 复制代码
qApp->setStyle("windows");

外观特征:

  • 按钮:Windows经典样式
  • 复选框/单选按钮:Windows原生样式
  • 滚动条:Windows原生样式
  • 菜单:Windows原生样式
Fusion样式

特点:

  • 平台:所有平台
  • 外观:现代扁平化设计,跨平台统一
  • 适用场景:需要跨平台统一样式的应用程序

使用:

cpp 复制代码
qApp->setStyle("fusion");

外观特征:

  • 按钮:扁平化设计,圆角边框
  • 复选框/单选按钮:现代扁平化样式
  • 滚动条:细滚动条,现代外观
  • 菜单:扁平化设计

优势:

  1. 跨平台统一:在所有平台上外观一致
  2. 现代设计:扁平化设计,符合现代UI趋势
  3. 可定制性强:易于通过QSS定制
macOS样式

特点:

  • 平台:macOS
  • 外观:使用macOS原生样式,提供原生macOS外观
  • 适用场景:macOS平台应用程序,需要原生体验

使用:

cpp 复制代码
qApp->setStyle("macos");

外观特征:

  • 按钮:macOS原生样式
  • 复选框/单选按钮:macOS原生样式
  • 滚动条:macOS原生样式
  • 菜单:macOS原生样式
Android/iOS样式

Android样式:

  • 平台:Android
  • 外观:Material Design风格
  • 使用 :qApp->setStyle("android");

iOS样式:

  • 平台:iOS
  • 外观:iOS原生风格
  • 使用 :qApp->setStyle("ios");
不同平台样式的差异

视觉差异:

平台 按钮样式 滚动条样式 菜单样式
Windows 经典3D效果 粗滚动条 经典菜单
macOS 扁平化 细滚动条 原生菜单
Fusion 扁平化 细滚动条 扁平化菜单

功能差异:

  • 快捷键:不同平台的快捷键可能不同
  • 菜单行为:菜单的弹出方式和行为可能不同
  • 对话框:对话框的外观和行为可能不同

选择建议:

  1. 需要原生体验:使用平台特定样式(Windows、macOS等)
  2. 需要统一样式:使用Fusion样式
  3. 需要自定义:继承QProxyStyle创建自定义样式


4. QPalette调色板系统

4.1 QPalette基础

QPalette是Qt的调色板系统,它定义了应用程序使用的颜色方案。理解QPalette对于创建一致、美观的界面至关重要。

调色板的概念和作用

QPalette是什么?

QPalette是Qt的颜色管理系统,它通过颜色角色(Color Roles)和颜色组(Color Groups)来组织颜色,确保界面颜色的一致性和可访问性。

调色板的核心作用:

  1. 统一颜色方案:为应用程序提供统一的颜色方案
  2. 状态适配:根据控件状态(活动、非活动、禁用)使用不同颜色
  3. 主题支持:支持亮色/暗色主题切换
  4. 可访问性:确保颜色对比度符合可访问性要求
  5. 平台适配:在不同平台上提供合适的颜色方案

调色板的设计理念:

QPalette的设计遵循"角色-组"模型:

  • 颜色角色(Color Roles):定义颜色的用途(如窗口背景、文本颜色等)
  • 颜色组(Color Groups):定义颜色的状态(如活动、非活动、禁用)

这种设计使得颜色管理更加清晰和灵活。

调色板与直接设置颜色的区别:

cpp 复制代码
// ❌ 直接设置颜色(不推荐)
QPushButton *btn = new QPushButton("按钮");
btn->setStyleSheet("background-color: blue; color: white;");

// ✅ 使用调色板(推荐)
QPalette palette;
palette.setColor(QPalette::Button, Qt::blue);
palette.setColor(QPalette::ButtonText, Qt::white);
btn->setPalette(palette);

调色板的优势:

  1. 一致性:确保所有控件使用统一的颜色方案
  2. 可维护性:修改调色板即可改变整个应用的颜色
  3. 状态管理:自动处理不同状态的颜色
  4. 主题支持:轻松实现主题切换
调色板的组成部分

1. 颜色角色(Color Roles)

颜色角色定义了颜色的用途。Qt定义了多个颜色角色:

cpp 复制代码
enum ColorRole {
    WindowText,        // 窗口文本色
    Button,            // 按钮背景色
    Light,             // 浅色(用于3D效果)
    Dark,              // 深色(用于3D效果)
    Mid,               // 中间色(用于3D效果)
    Text,              // 文本色
    BrightText,        // 亮文本色
    ButtonText,        // 按钮文本色
    Base,              // 基础背景色(如输入框背景)
    Window,            // 窗口背景色
    Shadow,            // 阴影色
    Highlight,         // 高亮色
    HighlightedText,   // 高亮文本色
    Link,              // 链接色
    LinkVisited,       // 已访问链接色
    AlternateBase,     // 交替背景色(如表格行)
    ToolTipBase,       // 工具提示背景色
    ToolTipText,       // 工具提示文本色
    PlaceholderText,   // 占位符文本色
    NoRole             // 无角色
};

2. 颜色组(Color Groups)

颜色组定义了颜色的状态。Qt定义了三个颜色组:

cpp 复制代码
enum ColorGroup {
    Active,    // 活动状态(窗口有焦点)
    Inactive, // 非活动状态(窗口无焦点)
    Disabled  // 禁用状态(控件被禁用)
};

调色板的结构:

复制代码
QPalette
├── Active组
│   ├── WindowText
│   ├── Button
│   ├── Text
│   └── ... (所有颜色角色)
├── Inactive组
│   ├── WindowText
│   ├── Button
│   ├── Text
│   └── ... (所有颜色角色)
└── Disabled组
    ├── WindowText
    ├── Button
    ├── Text
    └── ... (所有颜色角色)

创建调色板:

cpp 复制代码
// 创建默认调色板
QPalette palette;

// 创建并设置颜色
QPalette palette;
palette.setColor(QPalette::Window, Qt::white);
palette.setColor(QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Button, Qt::lightGray);
palette.setColor(QPalette::ButtonText, Qt::black);
palette.setColor(QPalette::Highlight, Qt::blue);
palette.setColor(QPalette::HighlightedText, Qt::white);
调色板的继承机制

继承规则:

QPalette遵循Qt对象树的继承机制:

  1. 子控件继承父控件的调色板:如果子控件没有设置调色板,它会继承父控件的调色板
  2. 应用级调色板:如果控件没有父控件或父控件没有调色板,使用应用级调色板
  3. 系统默认调色板:如果应用没有设置调色板,使用系统默认调色板

继承示例:

cpp 复制代码
// 应用级调色板
QPalette appPalette;
appPalette.setColor(QPalette::Window, Qt::white);
appPalette.setColor(QPalette::WindowText, Qt::black);
qApp->setPalette(appPalette);

// 父控件调色板
QWidget *parent = new QWidget();
QPalette parentPalette = parent->palette();
parentPalette.setColor(QPalette::Window, Qt::lightGray);
parent->setPalette(parentPalette);

// 子控件继承父控件的调色板
QLabel *label = new QLabel("文本", parent);
// label使用parent的调色板(lightGray背景)

// 子控件可以覆盖继承的调色板
QLabel *label2 = new QLabel("文本2", parent);
QPalette label2Palette = label2->palette();
label2Palette.setColor(QPalette::WindowText, Qt::red);
label2->setPalette(label2Palette);
// label2使用自己的调色板(红色文本)

调色板查找顺序:

当控件需要获取颜色时,Qt按以下顺序查找:

  1. 控件自身的调色板
  2. 父控件的调色板(向上查找)
  3. QApplication的调色板
  4. 系统默认调色板
调色板与QSS的关系

调色板与QSS的协同工作:

QPalette和QSS可以协同工作,但它们的优先级不同:

  1. QSS优先级更高:如果QSS中设置了颜色,会覆盖调色板的颜色
  2. QSS可以引用调色板 :QSS可以使用palette()函数引用调色板的颜色
  3. 调色板作为默认值:如果QSS没有设置颜色,使用调色板的颜色

QSS引用调色板:

cpp 复制代码
// 设置调色板
QPalette palette;
palette.setColor(QPalette::Button, Qt::blue);
palette.setColor(QPalette::ButtonText, Qt::white);
qApp->setPalette(palette);

// QSS引用调色板
qApp->setStyleSheet(
    "QPushButton {"
    "    background-color: palette(button);"      // 使用调色板的按钮颜色
    "    color: palette(button-text);"            // 使用调色板的按钮文本颜色
    "}"
);

调色板颜色角色在QSS中的名称:

cpp 复制代码
palette(window)           // Window
palette(window-text)      // WindowText
palette(base)             // Base
palette(alternate-base)   // AlternateBase
palette(text)             // Text
palette(button)           // Button
palette(button-text)      // ButtonText
palette(highlight)        // Highlight
palette(highlighted-text) // HighlightedText
palette(link)             // Link
palette(link-visited)     // LinkVisited
palette(tooltip-base)     // ToolTipBase
palette(tooltip-text)     // ToolTipText

实际应用:

cpp 复制代码
// 方式1:使用调色板
QPalette palette;
palette.setColor(QPalette::Button, Qt::blue);
qApp->setPalette(palette);

// 方式2:使用QSS
qApp->setStyleSheet("QPushButton { background-color: blue; }");

// 方式3:QSS引用调色板(推荐)
QPalette palette;
palette.setColor(QPalette::Button, Qt::blue);
qApp->setPalette(palette);
qApp->setStyleSheet("QPushButton { background-color: palette(button); }");

最佳实践:

  1. 使用调色板定义颜色方案:在调色板中定义主要的颜色
  2. 使用QSS定义样式细节:使用QSS定义边框、圆角等样式细节
  3. QSS引用调色板 :在QSS中使用palette()函数引用调色板的颜色
  4. 保持一致性:确保调色板和QSS的颜色方案一致

4.2 颜色角色(Color Roles)

颜色角色定义了颜色的用途。理解每个颜色角色的作用对于正确使用调色板至关重要。

Window - 窗口背景色

作用:窗口的背景颜色。

使用场景:

  • 主窗口背景
  • 对话框背景
  • 面板背景

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Window, Qt::white);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QWidget { background-color: palette(window); }");

注意事项:

  • Window颜色通常用于窗口和面板的背景
  • 应该与WindowText颜色有足够的对比度
WindowText - 窗口文本色

作用:窗口中的文本颜色。

使用场景:

  • 窗口标题文本
  • 标签文本
  • 一般文本

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::WindowText, Qt::black);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QLabel { color: palette(window-text); }");

注意事项:

  • WindowText颜色应该与Window颜色有足够的对比度
  • 通常用于窗口中的主要文本
Base - 基础背景色

作用:输入控件(如输入框、文本编辑器)的背景颜色。

使用场景:

  • QLineEdit背景
  • QTextEdit背景
  • QPlainTextEdit背景
  • 列表视图背景

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Base, Qt::white);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QLineEdit { background-color: palette(base); }");

注意事项:

  • Base颜色通常比Window颜色更亮或更暗,以区分输入区域
  • 应该与Text颜色有足够的对比度
AlternateBase - 交替背景色

作用:交替行的背景颜色(用于表格、列表等)。

使用场景:

  • QTableView的交替行
  • QTreeView的交替行
  • 列表的交替项

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::AlternateBase, QColor(240, 240, 240));
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QTableView { alternate-background-color: palette(alternate-base); }");

注意事项:

  • AlternateBase颜色应该与Base颜色有轻微差异
  • 用于提高表格和列表的可读性
Text - 文本色

作用:输入控件中的文本颜色。

使用场景:

  • QLineEdit中的文本
  • QTextEdit中的文本
  • 列表项文本

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Text, Qt::black);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QLineEdit { color: palette(text); }");

注意事项:

  • Text颜色应该与Base颜色有足够的对比度
  • 用于输入控件中的文本
Button - 按钮背景色

作用:按钮的背景颜色。

使用场景:

  • QPushButton背景
  • QToolButton背景
  • 按钮类控件

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Button, QColor(240, 240, 240));
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QPushButton { background-color: palette(button); }");

注意事项:

  • Button颜色通常比Window颜色稍暗或稍亮
  • 应该与ButtonText颜色有足够的对比度
ButtonText - 按钮文本色

作用:按钮中的文本颜色。

使用场景:

  • QPushButton文本
  • QToolButton文本
  • 按钮类控件的文本

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::ButtonText, Qt::black);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QPushButton { color: palette(button-text); }");

注意事项:

  • ButtonText颜色应该与Button颜色有足够的对比度
  • 用于按钮中的文本
Highlight - 高亮色

作用:选中项的高亮背景颜色。

使用场景:

  • 列表选中项背景
  • 表格选中行背景
  • 菜单选中项背景

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Highlight, Qt::blue);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QListView::item:selected { background-color: palette(highlight); }");

注意事项:

  • Highlight颜色应该醒目,但不要太刺眼
  • 应该与HighlightedText颜色有足够的对比度
HighlightedText - 高亮文本色

作用:选中项中的文本颜色。

使用场景:

  • 列表选中项文本
  • 表格选中行文本
  • 菜单选中项文本

示例:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::HighlightedText, Qt::white);
qApp->setPalette(palette);

// 在QSS中使用
qApp->setStyleSheet("QListView::item:selected { color: palette(highlighted-text); }");

注意事项:

  • HighlightedText颜色应该与Highlight颜色有足够的对比度
  • 通常使用白色或浅色
其他颜色角色

Light、Dark、Mid:

用于3D效果的阴影和高光:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Light, QColor(255, 255, 255));
palette.setColor(QPalette::Dark, QColor(128, 128, 128));
palette.setColor(QPalette::Mid, QColor(192, 192, 192));

BrightText:

用于在暗色背景上显示的亮文本:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::BrightText, Qt::yellow);

Link、LinkVisited:

用于链接文本:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Link, Qt::blue);
palette.setColor(QPalette::LinkVisited, Qt::magenta);

ToolTipBase、ToolTipText:

用于工具提示:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::ToolTipBase, Qt::yellow);
palette.setColor(QPalette::ToolTipText, Qt::black);

PlaceholderText:

用于占位符文本(Qt 5.12+):

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::PlaceholderText, QColor(128, 128, 128));

Shadow:

用于阴影效果:

cpp 复制代码
QPalette palette;
palette.setColor(QPalette::Shadow, QColor(64, 64, 64));

完整示例:

cpp 复制代码
// 创建完整的调色板
QPalette palette;

// 窗口相关
palette.setColor(QPalette::Window, Qt::white);
palette.setColor(QPalette::WindowText, Qt::black);

// 输入控件相关
palette.setColor(QPalette::Base, Qt::white);
palette.setColor(QPalette::AlternateBase, QColor(240, 240, 240));
palette.setColor(QPalette::Text, Qt::black);

// 按钮相关
palette.setColor(QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::ButtonText, Qt::black);

// 高亮相关
palette.setColor(QPalette::Highlight, Qt::blue);
palette.setColor(QPalette::HighlightedText, Qt::white);

// 链接相关
palette.setColor(QPalette::Link, Qt::blue);
palette.setColor(QPalette::LinkVisited, Qt::magenta);

// 工具提示相关
palette.setColor(QPalette::ToolTipBase, Qt::yellow);
palette.setColor(QPalette::ToolTipText, Qt::black);

// 应用调色板
qApp->setPalette(palette);

4.3 颜色组(Color Groups)

颜色组定义了颜色的状态。Qt定义了三个颜色组,用于在不同状态下显示不同的颜色。

Active - 活动状态

作用:窗口有焦点时的颜色。

使用场景:

  • 活动窗口的控件
  • 有焦点的控件

示例:

cpp 复制代码
QPalette palette;
// 设置活动状态的颜色
palette.setColor(QPalette::Active, QPalette::Window, Qt::white);
palette.setColor(QPalette::Active, QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Active, QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::Active, QPalette::ButtonText, Qt::black);
qApp->setPalette(palette);

特点:

  • 活动状态的颜色通常最鲜明
  • 用于表示控件处于活动状态
Inactive - 非活动状态

作用:窗口无焦点时的颜色。

使用场景:

  • 非活动窗口的控件
  • 无焦点的控件

示例:

cpp 复制代码
QPalette palette;
// 设置非活动状态的颜色
palette.setColor(QPalette::Inactive, QPalette::Window, QColor(245, 245, 245));
palette.setColor(QPalette::Inactive, QPalette::WindowText, QColor(128, 128, 128));
palette.setColor(QPalette::Inactive, QPalette::Button, QColor(245, 245, 245));
palette.setColor(QPalette::Inactive, QPalette::ButtonText, QColor(128, 128, 128));
qApp->setPalette(palette);

特点:

  • 非活动状态的颜色通常较淡
  • 用于表示控件处于非活动状态
Disabled - 禁用状态

作用:控件被禁用时的颜色。

使用场景:

  • 禁用状态的控件
  • 不可交互的控件

示例:

cpp 复制代码
QPalette palette;
// 设置禁用状态的颜色
palette.setColor(QPalette::Disabled, QPalette::Window, QColor(240, 240, 240));
palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(192, 192, 192));
palette.setColor(QPalette::Disabled, QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(192, 192, 192));
qApp->setPalette(palette);

特点:

  • 禁用状态的颜色通常较淡,对比度较低
  • 用于表示控件不可用
颜色组的切换机制

自动切换:

Qt会根据控件和窗口的状态自动切换颜色组:

  1. 窗口焦点变化:当窗口获得或失去焦点时,自动切换Active和Inactive组
  2. 控件启用/禁用:当控件被启用或禁用时,自动切换Enabled和Disabled组

手动切换:

cpp 复制代码
// 获取当前颜色组
QPalette::ColorGroup currentGroup = widget->palette().currentColorGroup();

// 设置特定颜色组的颜色
QPalette palette = widget->palette();
palette.setColor(QPalette::Active, QPalette::Button, Qt::blue);
palette.setColor(QPalette::Inactive, QPalette::Button, QColor(200, 200, 255));
palette.setColor(QPalette::Disabled, QPalette::Button, QColor(240, 240, 240));
widget->setPalette(palette);

颜色组优先级:

当控件需要获取颜色时,Qt按以下优先级选择颜色组:

  1. Disabled组:如果控件被禁用,使用Disabled组
  2. Active/Inactive组:根据窗口焦点状态选择Active或Inactive组

完整示例:

cpp 复制代码
// 创建完整的调色板(包含所有颜色组)
QPalette palette;

// 活动状态
palette.setColor(QPalette::Active, QPalette::Window, Qt::white);
palette.setColor(QPalette::Active, QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Active, QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::Active, QPalette::ButtonText, Qt::black);

// 非活动状态
palette.setColor(QPalette::Inactive, QPalette::Window, QColor(245, 245, 245));
palette.setColor(QPalette::Inactive, QPalette::WindowText, QColor(128, 128, 128));
palette.setColor(QPalette::Inactive, QPalette::Button, QColor(245, 245, 245));
palette.setColor(QPalette::Inactive, QPalette::ButtonText, QColor(128, 128, 128));

// 禁用状态
palette.setColor(QPalette::Disabled, QPalette::Window, QColor(240, 240, 240));
palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(192, 192, 192));
palette.setColor(QPalette::Disabled, QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(192, 192, 192));

qApp->setPalette(palette);

4.4 调色板的使用

获取和设置调色板

获取调色板:

cpp 复制代码
// 获取应用级调色板
QPalette appPalette = qApp->palette();

// 获取控件调色板
QPalette widgetPalette = widget->palette();

设置调色板:

cpp 复制代码
// 设置应用级调色板
QPalette palette;
palette.setColor(QPalette::Window, Qt::white);
qApp->setPalette(palette);

// 设置控件调色板
QPalette palette;
palette.setColor(QPalette::Button, Qt::blue);
widget->setPalette(palette);

复制调色板:

cpp 复制代码
// 复制调色板
QPalette newPalette = widget->palette();
newPalette.setColor(QPalette::Button, Qt::red);
widget->setPalette(newPalette);
修改特定颜色角色

修改单个颜色角色:

cpp 复制代码
// 获取调色板
QPalette palette = widget->palette();

// 修改特定颜色角色
palette.setColor(QPalette::Button, Qt::blue);
palette.setColor(QPalette::ButtonText, Qt::white);

// 应用调色板
widget->setPalette(palette);

修改特定颜色组的颜色角色:

cpp 复制代码
// 获取调色板
QPalette palette = widget->palette();

// 修改活动状态的按钮颜色
palette.setColor(QPalette::Active, QPalette::Button, Qt::blue);

// 修改禁用状态的按钮颜色
palette.setColor(QPalette::Disabled, QPalette::Button, QColor(240, 240, 240));

// 应用调色板
widget->setPalette(palette);

批量修改颜色角色:

cpp 复制代码
// 创建调色板
QPalette palette;

// 批量设置颜色
palette.setColor(QPalette::Window, Qt::white);
palette.setColor(QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Base, Qt::white);
palette.setColor(QPalette::Text, Qt::black);
palette.setColor(QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::ButtonText, Qt::black);
palette.setColor(QPalette::Highlight, Qt::blue);
palette.setColor(QPalette::HighlightedText, Qt::white);

// 应用调色板
qApp->setPalette(palette);
调色板的全局设置

应用级调色板:

通过QApplication::setPalette()设置应用级调色板,影响所有控件:

cpp 复制代码
// 创建调色板
QPalette palette;
palette.setColor(QPalette::Window, Qt::white);
palette.setColor(QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Button, QColor(240, 240, 240));
palette.setColor(QPalette::ButtonText, Qt::black);

// 设置应用级调色板
qApp->setPalette(palette);

// 所有控件都会使用这个调色板
QPushButton *btn1 = new QPushButton("按钮1");
QPushButton *btn2 = new QPushButton("按钮2");
QLabel *label = new QLabel("标签");

全局调色板的特点:

  1. 影响范围:影响应用程序中的所有控件
  2. 继承机制:子控件会继承父控件的调色板,如果没有父控件,使用应用级调色板
  3. 优先级:控件自身的调色板优先级最高
调色板的局部设置

控件级调色板:

通过QWidget::setPalette()设置控件级调色板,只影响该控件及其子控件:

cpp 复制代码
// 应用级调色板
QPalette appPalette;
appPalette.setColor(QPalette::Button, Qt::blue);
qApp->setPalette(appPalette);

// 局部调色板(只影响sidebar及其子控件)
QWidget *sidebar = new QWidget();
QPalette sidebarPalette;
sidebarPalette.setColor(QPalette::Button, Qt::green);
sidebar->setPalette(sidebarPalette);

// 主窗口使用应用级调色板(蓝色按钮)
QPushButton *mainBtn = new QPushButton("主窗口按钮");

// 侧边栏使用局部调色板(绿色按钮)
QPushButton *sidebarBtn = new QPushButton("侧边栏按钮", sidebar);

局部调色板的特点:

  1. 影响范围:只影响该控件及其子控件
  2. 优先级:局部调色板优先级高于应用级调色板
  3. 继承机制:子控件会继承父控件的局部调色板

实际应用:

cpp 复制代码
// 全局主题
QPalette globalPalette;
globalPalette.setColor(QPalette::Window, Qt::white);
globalPalette.setColor(QPalette::WindowText, Qt::black);
qApp->setPalette(globalPalette);

// 侧边栏局部主题
QWidget *sidebar = new QWidget();
QPalette sidebarPalette;
sidebarPalette.setColor(QPalette::Window, QColor(50, 50, 50));
sidebarPalette.setColor(QPalette::WindowText, Qt::white);
sidebar->setPalette(sidebarPalette);

// 主内容区使用全局主题(白色背景)
QWidget *mainContent = new QWidget();

// 侧边栏使用局部主题(深色背景)
QWidget *sidebarContent = new QWidget(sidebar);

4.5 调色板与主题

亮色主题

亮色主题的特点:

  • 背景颜色较亮(白色或浅灰色)
  • 文本颜色较深(黑色或深灰色)
  • 高对比度,适合阅读

创建亮色主题:

cpp 复制代码
QPalette lightPalette;

// 窗口相关
lightPalette.setColor(QPalette::Window, Qt::white);
lightPalette.setColor(QPalette::WindowText, Qt::black);

// 输入控件相关
lightPalette.setColor(QPalette::Base, Qt::white);
lightPalette.setColor(QPalette::AlternateBase, QColor(240, 240, 240));
lightPalette.setColor(QPalette::Text, Qt::black);

// 按钮相关
lightPalette.setColor(QPalette::Button, QColor(240, 240, 240));
lightPalette.setColor(QPalette::ButtonText, Qt::black);

// 高亮相关
lightPalette.setColor(QPalette::Highlight, QColor(0, 120, 215));
lightPalette.setColor(QPalette::HighlightedText, Qt::white);

// 链接相关
lightPalette.setColor(QPalette::Link, QColor(0, 102, 204));
lightPalette.setColor(QPalette::LinkVisited, QColor(128, 0, 128));

// 工具提示相关
lightPalette.setColor(QPalette::ToolTipBase, Qt::yellow);
lightPalette.setColor(QPalette::ToolTipText, Qt::black);

qApp->setPalette(lightPalette);
暗色主题

暗色主题的特点:

  • 背景颜色较暗(深灰色或黑色)
  • 文本颜色较亮(白色或浅灰色)
  • 低对比度,适合长时间使用

创建暗色主题:

cpp 复制代码
QPalette darkPalette;

// 窗口相关
darkPalette.setColor(QPalette::Window, QColor(45, 45, 45));
darkPalette.setColor(QPalette::WindowText, Qt::white);

// 输入控件相关
darkPalette.setColor(QPalette::Base, QColor(35, 35, 35));
darkPalette.setColor(QPalette::AlternateBase, QColor(50, 50, 50));
darkPalette.setColor(QPalette::Text, Qt::white);

// 按钮相关
darkPalette.setColor(QPalette::Button, QColor(60, 60, 60));
darkPalette.setColor(QPalette::ButtonText, Qt::white);

// 高亮相关
darkPalette.setColor(QPalette::Highlight, QColor(0, 120, 215));
darkPalette.setColor(QPalette::HighlightedText, Qt::white);

// 链接相关
darkPalette.setColor(QPalette::Link, QColor(100, 150, 255));
darkPalette.setColor(QPalette::LinkVisited, QColor(200, 100, 255));

// 工具提示相关
darkPalette.setColor(QPalette::ToolTipBase, QColor(60, 60, 60));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);

qApp->setPalette(darkPalette);
主题切换的实现

主题管理器类:

cpp 复制代码
class ThemeManager {
public:
    enum Theme {
        Light,
        Dark
    };
    
    static void setTheme(Theme theme) {
        QPalette palette;
        
        if (theme == Dark) {
            // 暗色主题
            palette.setColor(QPalette::Window, QColor(45, 45, 45));
            palette.setColor(QPalette::WindowText, Qt::white);
            palette.setColor(QPalette::Base, QColor(35, 35, 35));
            palette.setColor(QPalette::AlternateBase, QColor(50, 50, 50));
            palette.setColor(QPalette::Text, Qt::white);
            palette.setColor(QPalette::Button, QColor(60, 60, 60));
            palette.setColor(QPalette::ButtonText, Qt::white);
            palette.setColor(QPalette::Highlight, QColor(0, 120, 215));
            palette.setColor(QPalette::HighlightedText, Qt::white);
        } else {
            // 亮色主题
            palette.setColor(QPalette::Window, Qt::white);
            palette.setColor(QPalette::WindowText, Qt::black);
            palette.setColor(QPalette::Base, Qt::white);
            palette.setColor(QPalette::AlternateBase, QColor(240, 240, 240));
            palette.setColor(QPalette::Text, Qt::black);
            palette.setColor(QPalette::Button, QColor(240, 240, 240));
            palette.setColor(QPalette::ButtonText, Qt::black);
            palette.setColor(QPalette::Highlight, QColor(0, 120, 215));
            palette.setColor(QPalette::HighlightedText, Qt::white);
        }
        
        qApp->setPalette(palette);
    }
};

// 使用
ThemeManager::setTheme(ThemeManager::Dark);

结合QSS的主题切换:

cpp 复制代码
void MainWindow::switchTheme(bool dark) {
    QPalette palette;
    QString styleSheet;
    
    if (dark) {
        // 暗色主题调色板
        palette.setColor(QPalette::Window, QColor(45, 45, 45));
        palette.setColor(QPalette::WindowText, Qt::white);
        palette.setColor(QPalette::Button, QColor(60, 60, 60));
        palette.setColor(QPalette::ButtonText, Qt::white);
        
        // 暗色主题QSS
        styleSheet = R"(
            QWidget {
                background-color: palette(window);
                color: palette(window-text);
            }
            QPushButton {
                background-color: palette(button);
                color: palette(button-text);
                border: 1px solid #555;
                padding: 5px;
            }
            QPushButton:hover {
                background-color: #666;
            }
        )";
    } else {
        // 亮色主题调色板
        palette.setColor(QPalette::Window, Qt::white);
        palette.setColor(QPalette::WindowText, Qt::black);
        palette.setColor(QPalette::Button, QColor(240, 240, 240));
        palette.setColor(QPalette::ButtonText, Qt::black);
        
        // 亮色主题QSS
        styleSheet = R"(
            QWidget {
                background-color: palette(window);
                color: palette(window-text);
            }
            QPushButton {
                background-color: palette(button);
                color: palette(button-text);
                border: 1px solid #ccc;
                padding: 5px;
            }
            QPushButton:hover {
                background-color: #e0e0e0;
            }
        )";
    }
    
    qApp->setPalette(palette);
    qApp->setStyleSheet(styleSheet);
}
系统主题的适配

检测系统主题:

cpp 复制代码
// Windows系统
#ifdef Q_OS_WIN
    QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
                       QSettings::NativeFormat);
    bool isDark = settings.value("AppsUseLightTheme", 1).toInt() == 0;
    if (isDark) {
        ThemeManager::setTheme(ThemeManager::Dark);
    } else {
        ThemeManager::setTheme(ThemeManager::Light);
    }
#endif

// macOS系统
#ifdef Q_OS_MAC
    // macOS 10.14+
    if (@available(macOS 10.14, *)) {
        NSAppearance *appearance = [NSAppearance currentAppearance];
        NSString *name = [appearance name];
        if ([name containsString:@"Dark"]) {
            ThemeManager::setTheme(ThemeManager::Dark);
        } else {
            ThemeManager::setTheme(ThemeManager::Light);
        }
    }
#endif

监听系统主题变化:

cpp 复制代码
// Windows系统
#ifdef Q_OS_WIN
    // 监听注册表变化
    QWinEventNotifier *notifier = new QWinEventNotifier(this);
    connect(notifier, &QWinEventNotifier::activated, [this]() {
        // 检测系统主题并切换
        // ...
    });
#endif

// macOS系统
#ifdef Q_OS_MAC
    // 监听NSAppearance变化
    [[NSDistributedNotificationCenter defaultCenter]
        addObserverForName:@"AppleInterfaceThemeChangedNotification"
        object:nil
        queue:nil
        usingBlock:^(NSNotification *note) {
            // 检测系统主题并切换
            // ...
        }];
#endif

自动跟随系统主题:

cpp 复制代码
class SystemThemeWatcher : public QObject {
    Q_OBJECT
public:
    SystemThemeWatcher(QObject *parent = nullptr) : QObject(parent) {
        // 初始化时检测系统主题
        detectSystemTheme();
        
        // 设置定时器定期检测(作为备选方案)
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &SystemThemeWatcher::detectSystemTheme);
        timer->start(1000);  // 每秒检测一次
    }
    
private slots:
    void detectSystemTheme() {
        // 检测系统主题并切换
        // ...
    }
};


5. QStyleOption样式配置

5.1 QStyleOption基础

QStyleOption是Qt样式系统中用于传递样式信息的核心类。理解QStyleOption对于自定义样式和控件绘制至关重要。

QStyleOption的作用

QStyleOption是什么?

QStyleOption是一个数据结构,用于在控件和QStyle之间传递绘制所需的信息。它包含了绘制控件所需的所有参数,如位置、大小、状态、文本、图标等。

QStyleOption的核心作用:

  1. 信息传递:将控件的状态和信息传递给QStyle
  2. 解耦合:将控件和样式引擎解耦,样式引擎不需要直接访问控件
  3. 性能优化:避免样式引擎频繁访问控件属性
  4. 线程安全:样式选项是值类型,可以安全地在不同线程间传递

为什么需要QStyleOption?

在Qt的早期版本中,QStyle的方法直接接受QWidget指针,这导致:

  • 样式引擎需要访问控件的所有属性
  • 控件和样式引擎紧密耦合
  • 难以实现线程安全的绘制

QStyleOption解决了这些问题:

  • 控件将信息打包到QStyleOption中
  • 样式引擎只使用QStyleOption中的信息
  • 实现了控件和样式引擎的解耦

QStyleOption的基本结构:

cpp 复制代码
class QStyleOption {
public:
    enum StyleOptionType {
        SO_Default,
        SO_Button,
        SO_ComboBox,
        SO_Complex,
        // ... 更多类型
    };
    
    enum StyleOptionVersion {
        Version = 1
    };
    
    StyleOptionType type;      // 选项类型
    int version;                // 版本号
    QRect rect;                 // 绘制区域
    QPalette palette;           // 调色板
    QFont font;                 // 字体
    State state;                // 状态
    Direction direction;        // 方向(LTR/RTL)
    // ... 更多属性
};
QStyleOption的继承体系

继承层次结构:

复制代码
QStyleOption (基类)
├── QStyleOptionButton (按钮)
├── QStyleOptionComboBox (组合框)
├── QStyleOptionComplex (复杂控件基类)
│   ├── QStyleOptionComboBox
│   ├── QStyleOptionSlider (滑块)
│   ├── QStyleOptionSpinBox (数字输入框)
│   ├── QStyleOptionProgressBar (进度条)
│   ├── QStyleOptionTab (标签页)
│   ├── QStyleOptionTabBarBase (标签栏)
│   ├── QStyleOptionTitleBar (标题栏)
│   ├── QStyleOptionGroupBox (分组框)
│   ├── QStyleOptionToolButton (工具按钮)
│   └── QStyleOptionHeader (表头)
├── QStyleOptionFocusRect (焦点矩形)
├── QStyleOptionFrame (框架)
├── QStyleOptionGraphicsItem (图形项)
├── QStyleOptionMenuItem (菜单项)
├── QStyleOptionRubberBand (橡皮筋)
├── QStyleOptionSizeGrip (大小调整手柄)
├── QStyleOptionTab (标签页)
├── QStyleOptionTabWidgetFrame (标签页控件框架)
├── QStyleOptionTitleBar (标题栏)
├── QStyleOptionToolBar (工具栏)
├── QStyleOptionToolBox (工具箱)
└── QStyleOptionViewItem (列表项)

QStyleOption(基类):

所有样式选项的基类,包含通用属性:

cpp 复制代码
class QStyleOption {
public:
    QRect rect;              // 绘制区域
    QPalette palette;        // 调色板
    QFont font;              // 字体
    State state;             // 状态
    Direction direction;     // 方向
    // ...
};

QStyleOptionComplex(复杂控件基类):

用于复杂控件的样式选项基类:

cpp 复制代码
class QStyleOptionComplex : public QStyleOption {
public:
    QStyle::SubControls subControls;  // 子控件
    QStyle::ComplexControlFeatures activeSubControls; // 活动子控件
    // ...
};
QStyleOption的工作原理

工作流程:

复制代码
控件需要绘制
    ↓
控件创建QStyleOption对象
    ↓
填充QStyleOption的属性(位置、大小、状态等)
    ↓
调用QStyle::drawControl()或drawComplexControl()
    ↓
QStyle使用QStyleOption中的信息绘制控件
    ↓
完成绘制

示例流程:

cpp 复制代码
// 控件绘制流程(简化)
void QPushButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QStyleOptionButton option;
    
    // 初始化样式选项
    initStyleOption(&option);
    
    // 调用样式引擎绘制
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
}

// 初始化样式选项
void QPushButton::initStyleOption(QStyleOptionButton *option) const {
    option->initFrom(this);  // 从控件初始化基本属性
    option->features = QStyleOptionButton::None;
    option->text = text();
    option->icon = icon();
    option->iconSize = iconSize();
    // ... 设置其他属性
}

QStyleOption的特点:

  1. 值类型:QStyleOption是值类型,可以安全地复制和传递
  2. 版本控制:每个QStyleOption子类都有版本号,用于兼容性检查
  3. 类型安全:使用类型枚举确保类型安全
  4. 线程安全:值类型,可以在不同线程间安全传递
何时需要使用QStyleOption

1. 自定义控件绘制:

当自定义控件需要绘制时,必须创建QStyleOption:

cpp 复制代码
class CustomButton : public QPushButton {
protected:
    void paintEvent(QPaintEvent *event) override {
        QStylePainter painter(this);
        QStyleOptionButton option;
        initStyleOption(&option);
        
        // 自定义绘制逻辑
        style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
    }
};

2. 自定义样式:

当创建自定义样式时,需要处理QStyleOption:

cpp 复制代码
class CustomStyle : public QProxyStyle {
public:
    void drawControl(ControlElement element,
                     const QStyleOption *opt,
                     QPainter *p,
                     const QWidget *w) const override {
        if (element == CE_PushButton) {
            const QStyleOptionButton *btnOpt = 
                qstyleoption_cast<const QStyleOptionButton *>(opt);
            if (btnOpt) {
                // 使用btnOpt中的信息绘制按钮
                // ...
            }
        } else {
            QProxyStyle::drawControl(element, opt, p, w);
        }
    }
};

3. 绘制到其他设备:

当需要将控件绘制到其他设备(如打印机、图片)时:

cpp 复制代码
void printWidget(QWidget *widget, QPrinter *printer) {
    QPainter painter(printer);
    QStyleOption opt;
    opt.initFrom(widget);
    opt.rect = widget->rect();
    
    // 使用样式选项绘制到打印机
    widget->style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, widget);
}

4. 预览控件:

在控件设计器中预览控件时:

cpp 复制代码
void previewWidget(QWidget *widget) {
    QPixmap pixmap(widget->size());
    QPainter painter(&pixmap);
    
    QStyleOption opt;
    opt.initFrom(widget);
    opt.rect = widget->rect();
    
    widget->style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, widget);
}

最佳实践:

  1. 总是使用initFrom() :使用initFrom()初始化基本属性
  2. 类型转换 :使用qstyleoption_cast<>()进行安全的类型转换
  3. 检查版本:检查QStyleOption的版本号以确保兼容性
  4. 不要直接访问控件:在样式引擎中,只使用QStyleOption中的信息

5.2 常用QStyleOption类

QStyleOptionButton - 按钮样式选项

作用:用于传递按钮控件的样式信息。

主要属性:

cpp 复制代码
class QStyleOptionButton : public QStyleOption {
public:
    enum ButtonFeature {
        None = 0x00,
        Flat = 0x01,           // 扁平按钮
        HasMenu = 0x02,        // 有菜单
        DefaultButton = 0x04,  // 默认按钮
        AutoDefaultButton = 0x08, // 自动默认按钮
        CommandLinkButton = 0x10 // 命令链接按钮
    };
    
    QString text;              // 按钮文本
    QIcon icon;                // 按钮图标
    QSize iconSize;            // 图标大小
    ButtonFeature features;    // 按钮特性
    // ...
};

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_PushButton) {
        const QStyleOptionButton *btnOpt = 
            qstyleoption_cast<const QStyleOptionButton *>(opt);
        if (!btnOpt) return;
        
        // 使用按钮选项中的信息
        QString text = btnOpt->text;
        QIcon icon = btnOpt->icon;
        bool isFlat = btnOpt->features & QStyleOptionButton::Flat;
        bool isDefault = btnOpt->features & QStyleOptionButton::DefaultButton;
        
        // 绘制按钮
        // ...
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}
QStyleOptionComboBox - 组合框样式选项

作用:用于传递组合框控件的样式信息。

主要属性:

cpp 复制代码
class QStyleOptionComboBox : public QStyleOptionComplex {
public:
    bool editable;              // 是否可编辑
    QRect frameRect;           // 框架矩形
    QRect currentTextRect;     // 当前文本矩形
    QRect popupRect;           // 弹出框矩形
    QString currentText;       // 当前文本
    QIcon currentIcon;         // 当前图标
    int currentIndex;          // 当前索引
    QSize iconSize;            // 图标大小
    // ...
};

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawComplexControl(ComplexControl cc,
                                     const QStyleOptionComplex *opt,
                                     QPainter *p,
                                     const QWidget *w) const {
    if (cc == CC_ComboBox) {
        const QStyleOptionComboBox *cbOpt = 
            qstyleoption_cast<const QStyleOptionComboBox *>(opt);
        if (!cbOpt) return;
        
        // 使用组合框选项中的信息
        bool editable = cbOpt->editable;
        QString text = cbOpt->currentText;
        QIcon icon = cbOpt->currentIcon;
        
        // 绘制组合框
        // ...
    } else {
        QProxyStyle::drawComplexControl(cc, opt, p, w);
    }
}
QStyleOptionSlider - 滑块样式选项

作用:用于传递滑块控件的样式信息。

主要属性:

cpp 复制代码
class QStyleOptionSlider : public QStyleOptionComplex {
public:
    Qt::Orientation orientation; // 方向(水平/垂直)
    int minimum;                 // 最小值
    int maximum;                 // 最大值
    int sliderPosition;          // 滑块位置
    int sliderValue;             // 滑块值
    int tickPosition;            // 刻度位置
    int tickInterval;            // 刻度间隔
    bool upsideDown;             // 是否倒置
    // ...
};

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawComplexControl(ComplexControl cc,
                                     const QStyleOptionComplex *opt,
                                     QPainter *p,
                                     const QWidget *w) const {
    if (cc == CC_Slider) {
        const QStyleOptionSlider *sliderOpt = 
            qstyleoption_cast<const QStyleOptionSlider *>(opt);
        if (!sliderOpt) return;
        
        // 使用滑块选项中的信息
        Qt::Orientation orientation = sliderOpt->orientation;
        int min = sliderOpt->minimum;
        int max = sliderOpt->maximum;
        int value = sliderOpt->sliderValue;
        
        // 绘制滑块
        // ...
    } else {
        QProxyStyle::drawComplexControl(cc, opt, p, w);
    }
}
QStyleOptionProgressBar - 进度条样式选项

作用:用于传递进度条控件的样式信息。

主要属性:

cpp 复制代码
class QStyleOptionProgressBar : public QStyleOption {
public:
    Qt::Orientation orientation; // 方向(水平/垂直)
    int minimum;                 // 最小值
    int maximum;                 // 最大值
    int progress;                // 当前进度
    QString text;                // 文本
    bool textVisible;            // 文本是否可见
    int textAlignment;           // 文本对齐
    // ...
};

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_ProgressBar) {
        const QStyleOptionProgressBar *pbOpt = 
            qstyleoption_cast<const QStyleOptionProgressBar *>(opt);
        if (!pbOpt) return;
        
        // 使用进度条选项中的信息
        int min = pbOpt->minimum;
        int max = pbOpt->maximum;
        int progress = pbOpt->progress;
        QString text = pbOpt->text;
        
        // 计算进度百分比
        double percentage = (double)(progress - min) / (max - min) * 100.0;
        
        // 绘制进度条
        // ...
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}
QStyleOptionTab - 标签页样式选项

作用:用于传递标签页控件的样式信息。

主要属性:

cpp 复制代码
class QStyleOptionTab : public QStyleOption {
public:
    enum TabPosition {
        Beginning,  // 开始位置
        Middle,     // 中间位置
        End,        // 结束位置
        OnlyOneTab  // 只有一个标签
    };
    
    enum SelectedPosition {
        NotAdjacent,  // 不相邻
        NextIsSelected, // 下一个被选中
        PreviousIsSelected // 上一个被选中
    };
    
    enum CornerWidget {
        NoCornerWidgets = 0x00,
        LeftCornerWidget = 0x01,
        RightCornerWidget = 0x02
    };
    
    TabPosition position;      // 位置
    SelectedPosition selectedPosition; // 选中位置
    CornerWidget cornerWidgets; // 角落控件
    QString text;               // 文本
    QIcon icon;                 // 图标
    QSize iconSize;             // 图标大小
    int row;                    // 行号
    // ...
};

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_TabBarTab) {
        const QStyleOptionTab *tabOpt = 
            qstyleoption_cast<const QStyleOptionTab *>(opt);
        if (!tabOpt) return;
        
        // 使用标签页选项中的信息
        QString text = tabOpt->text;
        QIcon icon = tabOpt->icon;
        bool isSelected = tabOpt->state & QStyle::State_Selected;
        QStyleOptionTab::TabPosition position = tabOpt->position;
        
        // 绘制标签页
        // ...
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}
QStyleOptionMenuItem - 菜单项样式选项

作用:用于传递菜单项控件的样式信息。

主要属性:

cpp 复制代码
class QStyleOptionMenuItem : public QStyleOption {
public:
    enum MenuItemType {
        Normal,      // 普通菜单项
        DefaultItem, // 默认菜单项
        Separator,   // 分隔符
        SubMenu,     // 子菜单
        Scroller,    // 滚动器
        TearOff,     // 撕下
        Margin       // 边距
    };
    
    enum CheckType {
        NotCheckable,  // 不可选中
        Exclusive,     // 独占(单选)
        NonExclusive   // 非独占(多选)
    };
    
    MenuItemType menuItemType;  // 菜单项类型
    CheckType checkType;        // 选中类型
    bool checked;               // 是否选中
    QString text;               // 文本
    QIcon icon;                 // 图标
    QString shortcut;           // 快捷键
    int maxIconWidth;           // 最大图标宽度
    int tabWidth;               // 标签宽度
    // ...
};

使用示例:

cpp 复制代码
// 在自定义样式中使用
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_MenuItem) {
        const QStyleOptionMenuItem *menuItemOpt = 
            qstyleoption_cast<const QStyleOptionMenuItem *>(opt);
        if (!menuItemOpt) return;
        
        // 使用菜单项选项中的信息
        QString text = menuItemOpt->text;
        QIcon icon = menuItemOpt->icon;
        bool isChecked = menuItemOpt->checked;
        QStyleOptionMenuItem::MenuItemType type = menuItemOpt->menuItemType;
        
        // 绘制菜单项
        if (type == QStyleOptionMenuItem::Separator) {
            // 绘制分隔符
        } else {
            // 绘制普通菜单项
        }
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}

5.3 QStyleOption的使用

初始化QStyleOption

使用initFrom()方法:

initFrom()是初始化QStyleOption的最常用方法,它从控件中复制基本属性:

cpp 复制代码
QStyleOptionButton option;
option.initFrom(button);  // 从按钮控件初始化
// 现在option包含了按钮的基本属性:rect、palette、font、state等

initFrom()做了什么:

cpp 复制代码
void QStyleOption::initFrom(const QWidget *widget) {
    rect = widget->rect();
    palette = widget->palette();
    font = widget->font();
    state = QStyle::State_None;
    
    if (widget->isEnabled())
        state |= QStyle::State_Enabled;
    if (widget->hasFocus())
        state |= QStyle::State_HasFocus;
    if (widget->window()->isActiveWindow())
        state |= QStyle::State_Active;
    if (widget->underMouse())
        state |= QStyle::State_MouseOver;
    // ... 更多状态
}

手动初始化:

cpp 复制代码
QStyleOptionButton option;
option.initFrom(button);

// 手动设置按钮特定属性
option.text = button->text();
option.icon = button->icon();
option.iconSize = button->iconSize();
option.features = QStyleOptionButton::None;
if (button->isFlat())
    option.features |= QStyleOptionButton::Flat;
if (button->isDefault())
    option.features |= QStyleOptionButton::DefaultButton;
设置样式选项参数

设置基本属性:

cpp 复制代码
QStyleOptionButton option;
option.initFrom(button);

// 设置文本
option.text = "确定";

// 设置图标
option.icon = QIcon(":/icons/ok.png");
option.iconSize = QSize(16, 16);

// 设置特性
option.features = QStyleOptionButton::None;
if (isFlat) {
    option.features |= QStyleOptionButton::Flat;
}

设置状态:

cpp 复制代码
QStyleOptionButton option;
option.initFrom(button);

// 设置状态
if (button->isEnabled()) {
    option.state |= QStyle::State_Enabled;
}
if (button->hasFocus()) {
    option.state |= QStyle::State_HasFocus;
}
if (button->underMouse()) {
    option.state |= QStyle::State_MouseOver;
}
if (button->isDown()) {
    option.state |= QStyle::State_Sunken;
}

设置区域:

cpp 复制代码
QStyleOptionButton option;
option.initFrom(button);

// 设置绘制区域
option.rect = QRect(10, 10, 100, 30);

// 或者使用控件的区域
option.rect = button->rect();
传递给QStyle绘制函数

基本用法:

cpp 复制代码
void CustomButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QStyleOptionButton option;
    initStyleOption(&option);
    
    // 传递给样式引擎
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
}

类型转换:

在样式引擎中,需要进行类型转换:

cpp 复制代码
void CustomStyle::drawControl(ControlElement element,
                               const QStyleOption *opt,
                               QPainter *p,
                               const QWidget *w) const {
    if (element == CE_PushButton) {
        // 安全的类型转换
        const QStyleOptionButton *btnOpt = 
            qstyleoption_cast<const QStyleOptionButton *>(opt);
        if (!btnOpt) {
            // 类型转换失败,使用基础样式
            QProxyStyle::drawControl(element, opt, p, w);
            return;
        }
        
        // 使用btnOpt绘制按钮
        // ...
    } else {
        QProxyStyle::drawControl(element, opt, p, w);
    }
}

检查版本:

cpp 复制代码
const QStyleOptionButton *btnOpt = 
    qstyleoption_cast<const QStyleOptionButton *>(opt);
if (btnOpt && btnOpt->version >= QStyleOptionButton::Version) {
    // 版本兼容,可以使用
    // ...
}
从控件初始化样式选项

使用initStyleOption()方法:

Qt控件通常提供initStyleOption()方法来初始化样式选项:

cpp 复制代码
class QPushButton {
public:
    void initStyleOption(QStyleOptionButton *option) const;
};

// 使用
QPushButton *button = new QPushButton("确定");
QStyleOptionButton option;
button->initStyleOption(&option);

自定义控件的initStyleOption():

cpp 复制代码
class CustomButton : public QPushButton {
public:
    void initStyleOption(QStyleOptionButton *option) const {
        // 调用基类方法
        QPushButton::initStyleOption(option);
        
        // 添加自定义属性
        option->features |= QStyleOptionButton::Flat;
        // ... 其他自定义设置
    }
};

完整示例:

cpp 复制代码
class CustomButton : public QPushButton {
protected:
    void paintEvent(QPaintEvent *event) override {
        QStylePainter painter(this);
        QStyleOptionButton option;
        
        // 初始化样式选项
        initStyleOption(&option);
        
        // 自定义修改
        if (m_customState) {
            option.state |= QStyle::State_MouseOver;
        }
        
        // 绘制
        style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
    }
    
private:
    bool m_customState = false;
};

5.4 QStyleOption高级应用

自定义样式选项

创建自定义样式选项:

cpp 复制代码
class QStyleOptionCustomButton : public QStyleOptionButton {
public:
    enum StyleOptionType {
        Type = SO_Button + 1
    };
    
    enum StyleOptionVersion {
        Version = 1
    };
    
    QStyleOptionCustomButton() : QStyleOptionButton(Version, Type) {}
    
    // 自定义属性
    QColor customColor;
    int customRadius;
    // ...
};

使用自定义样式选项:

cpp 复制代码
class CustomButton : public QPushButton {
protected:
    void paintEvent(QPaintEvent *event) override {
        QStylePainter painter(this);
        QStyleOptionCustomButton option;
        
        // 初始化基本属性
        option.initFrom(this);
        option.text = text();
        option.icon = icon();
        
        // 设置自定义属性
        option.customColor = QColor(76, 175, 80);
        option.customRadius = 5;
        
        // 传递给自定义样式
        style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
    }
};

在自定义样式中处理:

cpp 复制代码
class CustomStyle : public QProxyStyle {
public:
    void drawControl(ControlElement element,
                     const QStyleOption *opt,
                     QPainter *p,
                     const QWidget *w) const override {
        if (element == CE_PushButton) {
            // 尝试转换为自定义选项
            const QStyleOptionCustomButton *customOpt = 
                qstyleoption_cast<const QStyleOptionCustomButton *>(opt);
            
            if (customOpt) {
                // 使用自定义选项
                QColor color = customOpt->customColor;
                int radius = customOpt->customRadius;
                // 绘制自定义按钮
                // ...
                return;
            }
            
            // 使用标准选项
            const QStyleOptionButton *btnOpt = 
                qstyleoption_cast<const QStyleOptionButton *>(opt);
            if (btnOpt) {
                // 绘制标准按钮
                // ...
            }
        } else {
            QProxyStyle::drawControl(element, opt, p, w);
        }
    }
};
样式选项的扩展

使用QStyleOption的子类:

cpp 复制代码
// 扩展QStyleOptionButton
class ExtendedButtonOption : public QStyleOptionButton {
public:
    // 扩展属性
    QLinearGradient gradient;
    QPixmap backgroundPixmap;
    QString tooltip;
    // ...
};

在控件中使用扩展选项:

cpp 复制代码
class ExtendedButton : public QPushButton {
protected:
    void paintEvent(QPaintEvent *event) override {
        QStylePainter painter(this);
        ExtendedButtonOption option;
        
        // 初始化基本属性
        option.initFrom(this);
        option.text = text();
        
        // 设置扩展属性
        option.gradient = createGradient();
        option.backgroundPixmap = m_backgroundPixmap;
        option.tooltip = toolTip();
        
        // 注意:需要自定义样式支持
        // 或者使用属性系统
        setProperty("extendedOption", QVariant::fromValue(option));
        
        // 绘制
        style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
    }
};
样式选项的优化

缓存样式选项:

对于频繁绘制的控件,可以缓存样式选项:

cpp 复制代码
class CachedButton : public QPushButton {
private:
    mutable QStyleOptionButton m_cachedOption;
    mutable bool m_optionDirty = true;
    
    void updateCachedOption() const {
        if (m_optionDirty) {
            initStyleOption(&m_cachedOption);
            m_optionDirty = false;
        }
    }
    
protected:
    void paintEvent(QPaintEvent *event) override {
        updateCachedOption();
        
        QStylePainter painter(this);
        style()->drawControl(QStyle::CE_PushButton, &m_cachedOption, &painter, this);
    }
    
    void changeEvent(QEvent *event) override {
        if (event->type() == QEvent::EnabledChange ||
            event->type() == QEvent::FontChange ||
            event->type() == QEvent::PaletteChange) {
            m_optionDirty = true;
        }
        QPushButton::changeEvent(event);
    }
};

减少样式选项创建:

cpp 复制代码
// ❌ 每次绘制都创建新选项(性能差)
void paintEvent(QPaintEvent *event) {
    QStyleOptionButton option;
    initStyleOption(&option);
    // ...
}

// ✅ 复用样式选项(性能好)
class CustomButton : public QPushButton {
private:
    QStyleOptionButton m_option;
    
protected:
    void paintEvent(QPaintEvent *event) override {
        initStyleOption(&m_option);
        // ...
    }
};

最佳实践:

  1. 总是使用initFrom():确保基本属性正确初始化
  2. 类型安全 :使用qstyleoption_cast<>()进行类型转换
  3. 版本检查:检查版本号以确保兼容性
  4. 避免直接访问控件:在样式引擎中只使用QStyleOption
  5. 缓存优化:对于频繁绘制的控件,考虑缓存样式选项


6. 自定义样式的实现

相关推荐
实心儿儿17 小时前
Qt — Qt 事件
开发语言·qt
bkspiderx1 天前
Qt 的消息机制:事件驱动与信号槽的底层逻辑
开发语言·qt·信号槽·事件驱动·qt 的消息机制
江湖人称菠萝包1 天前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter11-数据库
笔记·qt·qt5
励志不掉头发的内向程序员1 天前
【从零写一个CAD 02】画完第一条线之后:实体容器、Esc 取消,和按了没反应的键盘
开发语言·c++·qt·学习·系统架构
Quz1 天前
QML TreeView:排序与搜索过滤
qt
Quz1 天前
QML TreeView:基础树视图与自定义委托
qt
Junfeng的技术笔记2 天前
基于 C++/Qt 的雷达回波信号检测与测距仿真系统
qt
Quz2 天前
QML TableView:自定义样式与排序表格
qt
Quz2 天前
QML TableView:可编辑表格与 SQLite 数据持久化
qt·sqlite
Quz2 天前
QML ListView:C++ 数据模型与 ProxyModel 实时过滤
qt