Qt样式系统详解(下)

目录

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

6. 自定义样式的实现

6.1 继承QStyle创建自定义样式

创建自定义样式是Qt样式系统的高级应用。通过继承QStyle,可以完全控制控件的绘制逻辑,实现独特的视觉风格。

QProxyStyle的使用

QProxyStyle是什么?

QProxyStyle是一个代理样式类,它允许在不修改原始样式的情况下扩展样式功能。这是创建自定义样式最简单、最推荐的方式。

QProxyStyle的优势

  1. 简单易用:只需要重写需要修改的方法
  2. 向后兼容:可以基于现有样式扩展
  3. 灵活性强:可以选择性地覆盖特定功能
  4. 维护性好:代码量少,易于维护

基本用法

cpp 复制代码
class CustomStyle : public QProxyStyle {
public:
    CustomStyle(QStyle *baseStyle = nullptr) 
        : QProxyStyle(baseStyle) {
        // 如果没有指定基础样式,使用Fusion样式
        if (!baseStyle) {
            setBaseStyle(QStyleFactory::create("fusion"));
        }
    }
    
    // 重写需要修改的方法
    void drawPrimitive(PrimitiveElement pe,
                       const QStyleOption *opt,
                       QPainter *p,
                       const QWidget *w = nullptr) const override {
        if (pe == PE_PanelButtonCommand) {
            // 自定义按钮绘制
            drawCustomButton(opt, p, w);
        } else {
            // 使用基础样式绘制其他元素
            QProxyStyle::drawPrimitive(pe, opt, p, w);
        }
    }
    
private:
    void drawCustomButton(const QStyleOption *opt, 
                          QPainter *p, 
                          const QWidget *w) const {
        // 自定义绘制逻辑
        // ...
    }
};

// 使用
QStyle *customStyle = new CustomStyle();
qApp->setStyle(customStyle);

基于现有样式扩展

cpp 复制代码
// 基于Fusion样式扩展
QStyle *fusionStyle = QStyleFactory::create("fusion");
QStyle *customStyle = new CustomStyle(fusionStyle);
qApp->setStyle(customStyle);

// 基于Windows样式扩展
QStyle *windowsStyle = QStyleFactory::create("windows");
QStyle *customStyle = new CustomStyle(windowsStyle);
qApp->setStyle(customStyle);

选择性覆盖

cpp 复制代码
class CustomStyle : public QProxyStyle {
public:
    // 只重写按钮绘制
    void drawControl(ControlElement element,
                     const QStyleOption *opt,
                     QPainter *p,
                     const QWidget *w = nullptr) const override {
        if (element == CE_PushButton) {
            // 自定义按钮绘制
            drawCustomButton(opt, p, w);
        } else {
            // 其他控件使用基础样式
            QProxyStyle::drawControl(element, opt, p, w);
        }
    }
    
    // 只重写按钮尺寸计算
    QSize sizeFromContents(ContentsType ct,
                            const QStyleOption *opt,
                            const QSize &contentsSize,
                            const QWidget *w = nullptr) const override {
        if (ct == CT_PushButton) {
            // 自定义按钮尺寸
            return contentsSize + QSize(20, 10);
        }
        return QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
    }
};
完全自定义QStyle

何时需要完全自定义?

当需要完全控制所有控件的绘制,或者需要实现全新的视觉风格时,可以完全自定义QStyle。

继承QCommonStyle

cpp 复制代码
class MyCustomStyle : public QCommonStyle {
    Q_OBJECT
    
public:
    MyCustomStyle() {}
    
    // 必须实现所有纯虚函数
    void drawPrimitive(PrimitiveElement pe,
                       const QStyleOption *opt,
                       QPainter *p,
                       const QWidget *w = nullptr) const override {
        // 实现所有基本元素的绘制
        switch (pe) {
            case PE_PanelButtonCommand:
                drawButtonPanel(opt, p, w);
                break;
            case PE_FrameLineEdit:
                drawLineEditFrame(opt, p, w);
                break;
            // ... 处理所有基本元素
            default:
                QCommonStyle::drawPrimitive(pe, opt, p, w);
                break;
        }
    }
    
    void drawControl(ControlElement element,
                     const QStyleOption *opt,
                     QPainter *p,
                     const QWidget *w = nullptr) const override {
        // 实现所有控件元素的绘制
        switch (element) {
            case CE_PushButton:
                drawPushButton(opt, p, w);
                break;
            case CE_PushButtonLabel:
                drawPushButtonLabel(opt, p, w);
                break;
            // ... 处理所有控件元素
            default:
                QCommonStyle::drawControl(element, opt, p, w);
                break;
        }
    }
    
    void drawComplexControl(ComplexControl cc,
                            const QStyleOptionComplex *opt,
                            QPainter *p,
                            const QWidget *w = nullptr) const override {
        // 实现所有复杂控件的绘制
        switch (cc) {
            case CC_ComboBox:
                drawComboBox(opt, p, w);
                break;
            case CC_ScrollBar:
                drawScrollBar(opt, p, w);
                break;
            // ... 处理所有复杂控件
            default:
                QCommonStyle::drawComplexControl(cc, opt, p, w);
                break;
        }
    }
    
    QSize sizeFromContents(ContentsType ct,
                            const QStyleOption *opt,
                            const QSize &contentsSize,
                            const QWidget *w = nullptr) const override {
        // 实现所有内容类型的尺寸计算
        // ...
    }
    
    int pixelMetric(PixelMetric metric,
                     const QStyleOption *option = nullptr,
                     const QWidget *widget = nullptr) const override {
        // 实现所有像素度量
        // ...
    }
    
    int styleHint(StyleHint hint,
                  const QStyleOption *option = nullptr,
                  const QWidget *widget = nullptr,
                  QStyleHintReturn *returnData = nullptr) const override {
        // 实现所有样式提示
        // ...
    }
    
    QRect subControlRect(ComplexControl cc,
                         const QStyleOptionComplex *opt,
                         SubControl sc,
                         const QWidget *w = nullptr) const override {
        // 实现所有子控件区域计算
        // ...
    }
    
    QIcon standardIcon(StandardPixmap standardIcon,
                       const QStyleOption *option = nullptr,
                       const QWidget *widget = nullptr) const override {
        // 实现所有标准图标
        // ...
    }
    
private:
    // 辅助绘制方法
    void drawButtonPanel(const QStyleOption *opt, QPainter *p, const QWidget *w) const;
    void drawLineEditFrame(const QStyleOption *opt, QPainter *p, const QWidget *w) const;
    void drawPushButton(const QStyleOption *opt, QPainter *p, const QWidget *w) const;
    void drawPushButtonLabel(const QStyleOption *opt, QPainter *p, const QWidget *w) const;
    void drawComboBox(const QStyleOptionComplex *opt, QPainter *p, const QWidget *w) const;
    void drawScrollBar(const QStyleOptionComplex *opt, QPainter *p, const QWidget *w) const;
};

完全自定义的注意事项

  1. 工作量大:需要实现所有方法
  2. 维护困难:代码量大,维护成本高
  3. 兼容性:需要确保与Qt版本兼容
  4. 测试:需要全面测试所有控件

建议

  • 优先使用QProxyStyle
  • 只在必要时完全自定义
  • 可以参考Qt源码中的样式实现
重写绘制函数

drawPrimitive()

cpp 复制代码
void CustomStyle::drawPrimitive(PrimitiveElement pe,
                                 const QStyleOption *opt,
                                 QPainter *p,
                                 const QWidget *w) const {
    switch (pe) {
        case PE_PanelButtonCommand: {
            // 绘制按钮面板
            const QStyleOptionButton *btnOpt = 
                qstyleoption_cast<const QStyleOptionButton *>(opt);
            if (btnOpt) {
                QRect rect = btnOpt->rect;
                QColor backgroundColor;
                
                // 根据状态选择颜色
                if (btnOpt->state & State_MouseOver) {
                    backgroundColor = QColor(69, 160, 73);
                } else if (btnOpt->state & 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));
            }
            break;
        }
        default:
            QProxyStyle::drawPrimitive(pe, opt, p, w);
            break;
    }
}

drawControl()

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()

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);
    }
}
重写度量函数

sizeFromContents()

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) {
            QSize size = contentsSize;
            // 添加内边距
            size += QSize(20, 10);
            // 添加边框
            size += QSize(4, 4);
            return size;
        }
    }
    return QProxyStyle::sizeFromContents(ct, opt, contentsSize, w);
}

pixelMetric()

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()

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);
    }
}

6.2 自定义控件的样式支持

自定义控件需要正确实现样式支持,才能与Qt的样式系统良好集成。

paintEvent()的实现

基本实现

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);
    }
};

完整的paintEvent()实现

cpp 复制代码
void CustomButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QStyleOptionButton option;
    
    // 初始化样式选项
    initStyleOption(&option);
    
    // 自定义修改(可选)
    if (m_customState) {
        option.state |= QStyle::State_MouseOver;
    }
    
    // 绘制按钮
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
    
    // 自定义绘制(在样式绘制之后)
    if (m_showCustomIndicator) {
        painter.setPen(Qt::red);
        painter.drawEllipse(rect().adjusted(5, 5, -5, -5));
    }
}

initStyleOption()的实现

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

QStylePainter的优势

  1. 自动设置:自动设置绘制器的状态(字体、调色板等)
  2. 样式集成:与样式系统良好集成
  3. 简化代码:减少手动设置绘制器状态的代码

基本用法

cpp 复制代码
void CustomButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QStyleOptionButton option;
    initStyleOption(&option);
    
    // QStylePainter自动设置了字体、调色板等
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
}

与QPainter的区别

cpp 复制代码
// 使用QPainter(需要手动设置)
void paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.setFont(font());
    painter.setPen(palette().color(QPalette::ButtonText));
    // ... 手动设置所有状态
    // 绘制...
}

// 使用QStylePainter(自动设置)
void paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);  // 自动设置字体、调色板等
    // 直接绘制...
}
支持QSS样式

基本支持

自定义控件默认支持QSS,只需要正确设置属性:

cpp 复制代码
class CustomButton : public QPushButton {
    Q_OBJECT
    Q_PROPERTY(QColor customColor READ customColor WRITE setCustomColor)
    
public:
    QColor customColor() const { return m_customColor; }
    void setCustomColor(const QColor &color) {
        m_customColor = color;
        update();  // 触发重绘
    }
    
private:
    QColor m_customColor = Qt::blue;
};

// QSS中使用
qApp->setStyleSheet("CustomButton { custom-color: red; }");

支持QSS属性

cpp 复制代码
class CustomButton : public QPushButton {
    Q_OBJECT
    Q_PROPERTY(int borderRadius READ borderRadius WRITE setBorderRadius)
    Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor)
    
public:
    int borderRadius() const { return m_borderRadius; }
    void setBorderRadius(int radius) {
        m_borderRadius = radius;
        update();
    }
    
    QColor hoverColor() const { return m_hoverColor; }
    void setHoverColor(const QColor &color) {
        m_hoverColor = color;
        update();
    }
    
private:
    int m_borderRadius = 5;
    QColor m_hoverColor = Qt::green;
};

// QSS中使用
qApp->setStyleSheet(
    "CustomButton {"
    "    border-radius: 10px;"
    "    hover-color: #4CAF50;"
    "}"
);

在paintEvent()中使用QSS属性

cpp 复制代码
void CustomButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QStyleOptionButton option;
    initStyleOption(&option);
    
    // 获取QSS属性
    QVariant borderRadiusVar = property("border-radius");
    if (borderRadiusVar.isValid()) {
        int radius = borderRadiusVar.toInt();
        // 使用圆角半径绘制
    }
    
    style()->drawControl(QStyle::CE_PushButton, &option, &painter, this);
}
支持QPalette

基本支持

自定义控件默认支持QPalette,通过palette()方法获取:

cpp 复制代码
void CustomButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    
    // 获取调色板
    QPalette pal = palette();
    
    // 使用调色板颜色
    QColor bgColor = pal.color(QPalette::Button);
    QColor textColor = pal.color(QPalette::ButtonText);
    
    // 绘制
    painter.fillRect(rect(), bgColor);
    painter.setPen(textColor);
    painter.drawText(rect(), Qt::AlignCenter, text());
}

根据状态使用不同颜色

cpp 复制代码
void CustomButton::paintEvent(QPaintEvent *event) {
    QStylePainter painter(this);
    QPalette pal = palette();
    
    QColor bgColor;
    QColor textColor;
    
    // 根据状态选择颜色
    if (!isEnabled()) {
        bgColor = pal.color(QPalette::Disabled, QPalette::Button);
        textColor = pal.color(QPalette::Disabled, QPalette::ButtonText);
    } else if (underMouse()) {
        bgColor = pal.color(QPalette::Active, QPalette::Highlight);
        textColor = pal.color(QPalette::Active, QPalette::HighlightedText);
    } else {
        bgColor = pal.color(QPalette::Active, QPalette::Button);
        textColor = pal.color(QPalette::Active, QPalette::ButtonText);
    }
    
    painter.fillRect(rect(), bgColor);
    painter.setPen(textColor);
    painter.drawText(rect(), Qt::AlignCenter, text());
}

响应调色板变化

cpp 复制代码
class CustomButton : public QPushButton {
protected:
    void changeEvent(QEvent *event) override {
        if (event->type() == QEvent::PaletteChange) {
            // 调色板变化,更新绘制
            update();
        }
        QPushButton::changeEvent(event);
    }
};

6.3 样式插件

样式插件的原理

样式插件的作用

样式插件允许将自定义样式打包为动态库,在运行时动态加载,无需重新编译应用程序。

样式插件的优势

  1. 模块化:样式与应用程序分离
  2. 动态加载:运行时加载,无需重新编译
  3. 易于分发:可以单独分发样式插件
  4. 版本管理:可以独立更新样式

样式插件的结构

复制代码
样式插件
├── 样式类(继承QStyle)
├── 插件接口(实现QStylePlugin)
└── 导出函数
创建样式插件

1. 创建插件项目

cpp 复制代码
// customstyleplugin.h
#include <QStylePlugin>

class CustomStylePlugin : public QStylePlugin {
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "customstyle.json")
    
public:
    QStringList keys() const override;
    QStyle *create(const QString &key) override;
};

2. 实现插件接口

cpp 复制代码
// customstyleplugin.cpp
#include "customstyleplugin.h"
#include "customstyle.h"

QStringList CustomStylePlugin::keys() const {
    return QStringList() << "CustomStyle";
}

QStyle *CustomStylePlugin::create(const QString &key) {
    if (key.toLower() == "customstyle") {
        return new CustomStyle();
    }
    return nullptr;
}

#include "customstyleplugin.moc"

3. 插件元数据

json 复制代码
// customstyle.json
{
    "Keys": ["CustomStyle"],
    "Version": 1
}

4. CMakeLists.txt

cmake 复制代码
cmake_minimum_required(VERSION 3.16)
project(CustomStylePlugin)

find_package(Qt6 REQUIRED COMPONENTS Core Widgets)

qt_add_plugin(CustomStylePlugin
    TYPE styles
    SOURCES
        customstyleplugin.cpp
        customstyleplugin.h
        customstyle.cpp
        customstyle.h
)

target_link_libraries(CustomStylePlugin
    Qt6::Core
    Qt6::Widgets
)

5. 完整的样式类

cpp 复制代码
// customstyle.h
#include <QProxyStyle>

class CustomStyle : public QProxyStyle {
    Q_OBJECT
    
public:
    CustomStyle();
    
    void drawPrimitive(PrimitiveElement pe,
                       const QStyleOption *opt,
                       QPainter *p,
                       const QWidget *w = nullptr) const override;
    
    void drawControl(ControlElement element,
                     const QStyleOption *opt,
                     QPainter *p,
                     const QWidget *w = nullptr) const override;
};
部署样式插件

Windows平台

复制代码
应用程序目录/
├── application.exe
└── styles/
    └── qcustomstyleplugin.dll

Linux平台

复制代码
应用程序目录/
├── application
└── plugins/
    └── styles/
        └── libqcustomstyleplugin.so

macOS平台

复制代码
应用程序包/
└── Contents/
    ├── MacOS/
    │   └── application
    └── PlugIns/
        └── styles/
            └── libqcustomstyleplugin.dylib

Qt插件查找路径

Qt会在以下路径查找插件:

  1. 应用程序目录下的plugins/styles/
  2. Qt安装目录下的plugins/styles/
  3. 环境变量QT_PLUGIN_PATH指定的路径
动态加载样式插件

自动加载

如果插件在Qt的插件路径中,Qt会自动发现并加载:

cpp 复制代码
// 获取可用样式(包括插件样式)
QStringList styles = QStyleFactory::keys();
qDebug() << "可用样式:" << styles;
// 输出:可用样式:("windows", "fusion", "CustomStyle", ...)

// 使用插件样式
qApp->setStyle("CustomStyle");

手动加载插件

cpp 复制代码
#include <QPluginLoader>
#include <QDir>

void loadStylePlugin(const QString &pluginPath) {
    QPluginLoader loader(pluginPath);
    QObject *plugin = loader.instance();
    
    if (plugin) {
        QStylePlugin *stylePlugin = qobject_cast<QStylePlugin *>(plugin);
        if (stylePlugin) {
            QStringList keys = stylePlugin->keys();
            qDebug() << "插件提供的样式:" << keys;
            
            // 创建样式
            QStyle *style = stylePlugin->create("CustomStyle");
            if (style) {
                qApp->setStyle(style);
            }
        }
    } else {
        qDebug() << "加载插件失败:" << loader.errorString();
    }
}

// 使用
QString pluginPath = QDir::currentPath() + "/plugins/styles/libqcustomstyleplugin.so";
loadStylePlugin(pluginPath);

检查插件是否可用

cpp 复制代码
bool isStylePluginAvailable(const QString &styleName) {
    QStringList availableStyles = QStyleFactory::keys();
    return availableStyles.contains(styleName, Qt::CaseInsensitive);
}

// 使用
if (isStylePluginAvailable("CustomStyle")) {
    qApp->setStyle("CustomStyle");
} else {
    qApp->setStyle("fusion");  // 回退到默认样式
}

6.4 样式的打包和分发

样式资源的组织

目录结构

复制代码
styles/
├── customstyle/
│   ├── style.qss          # QSS样式表
│   ├── images/            # 图片资源
│   │   ├── button.png
│   │   └── icon.png
│   ├── fonts/             # 字体资源
│   │   └── customfont.ttf
│   └── theme.json         # 主题配置
└── darkstyle/
    ├── style.qss
    └── images/

资源文件命名规范

  • 使用小写字母和下划线
  • 避免空格和特殊字符
  • 使用有意义的名称
qrc资源文件的使用

创建资源文件

xml 复制代码
<!-- styles.qrc -->
<RCC>
    <qresource prefix="/styles">
        <file>customstyle/style.qss</file>
        <file>customstyle/images/button.png</file>
        <file>customstyle/images/icon.png</file>
        <file>customstyle/fonts/customfont.ttf</file>
    </qresource>
</RCC>

在代码中使用资源

cpp 复制代码
// 加载QSS样式表
QFile file(":/styles/customstyle/style.qss");
if (file.open(QFile::ReadOnly)) {
    QString styleSheet = QLatin1String(file.readAll());
    qApp->setStyleSheet(styleSheet);
}

// 使用图片资源
QPushButton *btn = new QPushButton();
btn->setIcon(QIcon(":/styles/customstyle/images/button.png"));

// 加载字体
int fontId = QFontDatabase::addApplicationFont(":/styles/customstyle/fonts/customfont.ttf");
QStringList fontFamilies = QFontDatabase::applicationFontFamilies(fontId);
if (!fontFamilies.isEmpty()) {
    QFont font(fontFamilies.at(0));
    qApp->setFont(font);
}

在QSS中引用资源

css 复制代码
/* style.qss */
QPushButton {
    background-image: url(:/styles/customstyle/images/button.png);
    border-image: url(:/styles/customstyle/images/border.png);
}
样式文件的加载

从资源文件加载

cpp 复制代码
void loadStyleFromResource(const QString &resourcePath) {
    QFile file(resourcePath);
    if (file.open(QFile::ReadOnly)) {
        QString styleSheet = QLatin1String(file.readAll());
        qApp->setStyleSheet(styleSheet);
    }
}

// 使用
loadStyleFromResource(":/styles/customstyle/style.qss");

从文件系统加载

cpp 复制代码
void loadStyleFromFile(const QString &filePath) {
    QFile file(filePath);
    if (file.open(QFile::ReadOnly)) {
        QString styleSheet = QLatin1String(file.readAll());
        qApp->setStyleSheet(styleSheet);
    } else {
        qWarning() << "无法打开样式文件:" << filePath;
    }
}

// 使用
QString stylePath = QApplication::applicationDirPath() + "/styles/customstyle/style.qss";
loadStyleFromFile(stylePath);

样式管理器类

cpp 复制代码
class StyleManager {
public:
    static void loadStyle(const QString &styleName) {
        QString resourcePath = QString(":/styles/%1/style.qss").arg(styleName);
        QFile file(resourcePath);
        
        if (file.open(QFile::ReadOnly)) {
            QString styleSheet = QLatin1String(file.readAll());
            
            // 替换资源路径(如果需要)
            styleSheet.replace("url(:/styles/", 
                              QString("url(:/styles/%1/").arg(styleName));
            
            qApp->setStyleSheet(styleSheet);
        } else {
            qWarning() << "无法加载样式:" << styleName;
        }
    }
    
    static QStringList availableStyles() {
        QStringList styles;
        QDir resourceDir(":/styles");
        QStringList entries = resourceDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
        for (const QString &entry : entries) {
            if (QFile::exists(QString(":/styles/%1/style.qss").arg(entry))) {
                styles << entry;
            }
        }
        return styles;
    }
};

// 使用
QStringList styles = StyleManager::availableStyles();
qDebug() << "可用样式:" << styles;

StyleManager::loadStyle("customstyle");
跨平台样式分发

平台特定资源

cpp 复制代码
QString getStylePath(const QString &styleName) {
    QString basePath;
    
#ifdef Q_OS_WIN
    basePath = QApplication::applicationDirPath() + "/styles/";
#elif defined(Q_OS_MAC)
    basePath = QApplication::applicationDirPath() + "/../Resources/styles/";
#else
    basePath = QApplication::applicationDirPath() + "/styles/";
#endif
    
    return basePath + styleName + "/style.qss";
}

打包样式资源

Windows

  • 将样式文件放在应用程序目录下的styles/文件夹
  • 或打包到资源文件中

Linux

  • 将样式文件放在应用程序目录下的styles/文件夹
  • 或安装到系统目录(如/usr/share/appname/styles/

macOS

  • 将样式文件放在应用程序包的Contents/Resources/styles/目录
  • 或打包到资源文件中

安装脚本示例

bash 复制代码
#!/bin/bash
# install_styles.sh

APP_DIR="/path/to/application"
STYLES_DIR="$APP_DIR/styles"

# 创建样式目录
mkdir -p "$STYLES_DIR"

# 复制样式文件
cp -r customstyle "$STYLES_DIR/"
cp -r darkstyle "$STYLES_DIR/"

echo "样式安装完成"

CMake安装配置

cmake 复制代码
# 安装样式文件
install(DIRECTORY styles/
    DESTINATION ${CMAKE_INSTALL_PREFIX}/styles
    FILES_MATCHING
    PATTERN "*.qss"
    PATTERN "*.png"
    PATTERN "*.ttf"
)


7. 样式系统的高级应用

7.1 动态样式切换

动态样式切换是现代应用程序的重要特性,允许用户在运行时切换不同的视觉风格。

运行时更改样式

切换QSS样式表

cpp 复制代码
void MainWindow::switchStyle(const QString &styleName) {
    QString stylePath = QString(":/styles/%1/style.qss").arg(styleName);
    QFile file(stylePath);
    
    if (file.open(QFile::ReadOnly)) {
        QString styleSheet = QLatin1String(file.readAll());
        qApp->setStyleSheet(styleSheet);
    }
}

// 使用
switchStyle("dark");
switchStyle("light");

切换QStyle样式引擎

cpp 复制代码
void MainWindow::switchStyleEngine(const QString &styleName) {
    QStyle *style = QStyleFactory::create(styleName);
    if (style) {
        qApp->setStyle(style);
    } else {
        qWarning() << "无法创建样式:" << styleName;
    }
}

// 使用
switchStyleEngine("fusion");
switchStyleEngine("windows");

切换QPalette调色板

cpp 复制代码
void MainWindow::switchPalette(const QString &themeName) {
    QPalette palette;
    
    if (themeName == "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::Text, Qt::white);
        // ... 更多颜色
    } else {
        palette.setColor(QPalette::Window, Qt::white);
        palette.setColor(QPalette::WindowText, Qt::black);
        palette.setColor(QPalette::Base, Qt::white);
        palette.setColor(QPalette::Text, Qt::black);
        // ... 更多颜色
    }
    
    qApp->setPalette(palette);
}

完整的样式切换管理器

cpp 复制代码
class StyleManager : public QObject {
    Q_OBJECT
    
public:
    static StyleManager* instance() {
        static StyleManager instance;
        return &instance;
    }
    
    void switchTheme(const QString &themeName) {
        // 加载QSS
        QString qssPath = QString(":/styles/%1/style.qss").arg(themeName);
        QFile qssFile(qssPath);
        if (qssFile.open(QFile::ReadOnly)) {
            QString styleSheet = QLatin1String(qssFile.readAll());
            qApp->setStyleSheet(styleSheet);
        }
        
        // 加载调色板
        QPalette palette = loadPalette(themeName);
        qApp->setPalette(palette);
        
        // 保存当前主题
        m_currentTheme = themeName;
        emit themeChanged(themeName);
    }
    
    QString currentTheme() const {
        return m_currentTheme;
    }
    
signals:
    void themeChanged(const QString &themeName);
    
private:
    QPalette loadPalette(const QString &themeName) {
        QPalette palette;
        // 根据主题名称加载调色板
        // ...
        return palette;
    }
    
    QString m_currentTheme = "light";
};

// 使用
StyleManager::instance()->switchTheme("dark");
平滑过渡效果

使用QPropertyAnimation实现颜色过渡

cpp 复制代码
class AnimatedStyleSwitch : public QObject {
    Q_OBJECT
    Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
    
public:
    AnimatedStyleSwitch(QWidget *widget) : m_widget(widget) {
        m_backgroundColor = widget->palette().color(QPalette::Window);
    }
    
    QColor backgroundColor() const { return m_backgroundColor; }
    void setBackgroundColor(const QColor &color) {
        m_backgroundColor = color;
        QPalette palette = m_widget->palette();
        palette.setColor(QPalette::Window, color);
        m_widget->setPalette(palette);
    }
    
    void animateToTheme(const QString &themeName) {
        QColor targetColor = getThemeColor(themeName);
        
        QPropertyAnimation *animation = new QPropertyAnimation(this, "backgroundColor");
        animation->setDuration(300);
        animation->setStartValue(m_backgroundColor);
        animation->setEndValue(targetColor);
        animation->start(QAbstractAnimation::DeleteWhenStopped);
    }
    
private:
    QWidget *m_widget;
    QColor m_backgroundColor;
    QColor getThemeColor(const QString &themeName) {
        // 返回主题颜色
        return themeName == "dark" ? QColor(45, 45, 45) : Qt::white;
    }
};

使用QGraphicsOpacityEffect实现淡入淡出

cpp 复制代码
void fadeStyleSwitch(QWidget *widget, const QString &newStyle) {
    // 创建淡出效果
    QGraphicsOpacityEffect *fadeOut = new QGraphicsOpacityEffect(widget);
    widget->setGraphicsEffect(fadeOut);
    
    QPropertyAnimation *fadeOutAnim = new QPropertyAnimation(fadeOut, "opacity");
    fadeOutAnim->setDuration(150);
    fadeOutAnim->setStartValue(1.0);
    fadeOutAnim->setEndValue(0.0);
    
    // 淡出完成后切换样式并淡入
    connect(fadeOutAnim, &QPropertyAnimation::finished, [widget, newStyle, fadeOut]() {
        // 切换样式
        StyleManager::instance()->switchTheme(newStyle);
        
        // 淡入
        QPropertyAnimation *fadeInAnim = new QPropertyAnimation(fadeOut, "opacity");
        fadeInAnim->setDuration(150);
        fadeInAnim->setStartValue(0.0);
        fadeInAnim->setEndValue(1.0);
        fadeInAnim->start(QAbstractAnimation::DeleteWhenStopped);
        
        connect(fadeInAnim, &QPropertyAnimation::finished, [widget, fadeOut]() {
            widget->setGraphicsEffect(nullptr);
            delete fadeOut;
        });
    });
    
    fadeOutAnim->start(QAbstractAnimation::DeleteWhenStopped);
}
样式切换的性能优化

缓存样式表

cpp 复制代码
class StyleCache {
public:
    static QString getStyleSheet(const QString &styleName) {
        static QMap<QString, QString> cache;
        
        if (!cache.contains(styleName)) {
            QString stylePath = QString(":/styles/%1/style.qss").arg(styleName);
            QFile file(stylePath);
            if (file.open(QFile::ReadOnly)) {
                cache[styleName] = QLatin1String(file.readAll());
            }
        }
        
        return cache.value(styleName);
    }
};

// 使用
QString styleSheet = StyleCache::getStyleSheet("dark");
qApp->setStyleSheet(styleSheet);

延迟应用样式

cpp 复制代码
void delayedStyleSwitch(const QString &styleName) {
    // 使用定时器延迟应用,避免频繁切换
    QTimer::singleShot(100, [styleName]() {
        StyleManager::instance()->switchTheme(styleName);
    });
}

批量更新

cpp 复制代码
void batchStyleUpdate(const QStringList &widgets, const QString &style) {
    // 暂停重绘
    for (QWidget *widget : widgets) {
        widget->setUpdatesEnabled(false);
    }
    
    // 批量应用样式
    StyleManager::instance()->switchTheme(style);
    
    // 恢复重绘
    for (QWidget *widget : widgets) {
        widget->setUpdatesEnabled(true);
        widget->update();
    }
}

减少重绘

cpp 复制代码
void optimizedStyleSwitch(const QString &styleName) {
    // 获取所有需要更新的控件
    QList<QWidget*> widgets = qApp->allWidgets();
    
    // 暂停重绘
    for (QWidget *widget : widgets) {
        widget->setUpdatesEnabled(false);
    }
    
    // 切换样式
    StyleManager::instance()->switchTheme(styleName);
    
    // 恢复重绘并更新
    for (QWidget *widget : widgets) {
        widget->setUpdatesEnabled(true);
        widget->update();
    }
}
样式切换的状态保持

保存当前状态

cpp 复制代码
class StyleState {
public:
    QString currentTheme;
    QString currentStyleEngine;
    QMap<QString, QVariant> customProperties;
    
    void save() {
        QSettings settings;
        settings.setValue("style/theme", currentTheme);
        settings.setValue("style/engine", currentStyleEngine);
        // 保存自定义属性
    }
    
    void load() {
        QSettings settings;
        currentTheme = settings.value("style/theme", "light").toString();
        currentStyleEngine = settings.value("style/engine", "fusion").toString();
        // 加载自定义属性
    }
};

恢复控件状态

cpp 复制代码
void restoreWidgetState(QWidget *widget, const QVariantMap &state) {
    // 恢复控件属性
    for (auto it = state.begin(); it != state.end(); ++it) {
        widget->setProperty(it.key().toUtf8().constData(), it.value());
    }
    
    // 刷新样式
    widget->style()->unpolish(widget);
    widget->style()->polish(widget);
    widget->update();
}

状态持久化

cpp 复制代码
class StyleManager : public QObject {
public:
    void switchTheme(const QString &themeName) {
        // 保存当前状态
        saveCurrentState();
        
        // 切换主题
        // ...
        
        // 恢复状态
        restoreState();
        
        // 保存新主题
        QSettings settings;
        settings.setValue("style/currentTheme", themeName);
    }
    
private:
    void saveCurrentState() {
        // 保存控件状态
        // ...
    }
    
    void restoreState() {
        // 恢复控件状态
        // ...
    }
};

7.2 样式继承与覆盖

理解样式的继承和覆盖机制对于正确应用样式至关重要。

样式的层级覆盖

样式层级(从高到低)

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

示例

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

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

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

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

覆盖规则

  • 更具体的选择器优先级更高
  • 后定义的规则覆盖先定义的规则(如果优先级相同)
  • ID选择器 > 类选择器 > 类型选择器 > 通用选择器
局部样式与全局样式

全局样式

cpp 复制代码
// 应用级样式,影响所有控件
qApp->setStyleSheet(
    "QWidget { background-color: white; }"
    "QPushButton { background-color: #4CAF50; color: white; }"
    "QLabel { color: black; }"
);

局部样式

cpp 复制代码
// 局部样式,只影响该控件及其子控件
QWidget *sidebar = new QWidget();
sidebar->setStyleSheet(
    "QWidget { background-color: #2b2b2b; }"
    "QPushButton { background-color: #3c3f41; color: white; }"
);

// 主窗口使用全局样式
QWidget *mainWindow = new QWidget();
// mainWindow使用全局样式(白色背景)

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

样式作用域查找顺序

  1. 控件自身的styleSheet
  2. 父控件的styleSheet(向上查找)
  3. QApplicationstyleSheet
样式的合并规则

相同选择器的合并

cpp 复制代码
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"   // 先定义
    "QPushButton { color: white; }"              // 后定义,合并
);
// 结果:QPushButton有蓝色背景和白色文本

不同选择器的合并

cpp 复制代码
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"
    "QPushButton:hover { background-color: lightblue; }"  // 合并,但优先级更高
);

继承合并

cpp 复制代码
// 父控件样式
parent->setStyleSheet("QPushButton { padding: 10px; }");

// 子控件样式
button->setStyleSheet("background-color: green;");

// 结果:button有10px内边距(继承)和绿色背景(覆盖)
样式冲突的解决

冲突检测

cpp 复制代码
class StyleConflictResolver {
public:
    static QString resolveConflict(const QString &style1, 
                                    const QString &style2,
                                    const QString &selector) {
        // 分析两个样式的优先级
        int priority1 = calculatePriority(style1, selector);
        int priority2 = calculatePriority(style2, selector);
        
        // 返回优先级更高的样式
        return priority1 > priority2 ? style1 : style2;
    }
    
private:
    static int calculatePriority(const QString &style, const QString &selector) {
        // 计算样式优先级
        // ID选择器 = 100
        // 类选择器 = 10
        // 类型选择器 = 1
        // ...
        return 0;
    }
};

使用!important

cpp 复制代码
// QSS不支持!important,但可以通过提高选择器优先级
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"
    "#myButton { background-color: green; }"  // ID选择器优先级更高
);

显式覆盖

cpp 复制代码
// 在需要覆盖的地方使用更具体的选择器
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"
    "QWidget#container QPushButton { background-color: green; }"  // 更具体
);

7.3 样式的模块化设计

模块化设计使样式更易于维护和复用。

样式的分离和组织

按功能分离

复制代码
styles/
├── base.qss          # 基础样式
├── buttons.qss       # 按钮样式
├── inputs.qss        # 输入框样式
├── menus.qss         # 菜单样式
├── dialogs.qss       # 对话框样式
└── theme.qss         # 主题样式

按组件分离

复制代码
styles/
├── components/
│   ├── button.qss
│   ├── input.qss
│   └── menu.qss
├── layouts/
│   ├── sidebar.qss
│   └── toolbar.qss
└── themes/
    ├── light.qss
    └── dark.qss

加载多个样式文件

cpp 复制代码
void loadStyleModules(const QStringList &modules) {
    QString combinedStyle;
    
    for (const QString &module : modules) {
        QString filePath = QString(":/styles/%1.qss").arg(module);
        QFile file(filePath);
        if (file.open(QFile::ReadOnly)) {
            combinedStyle += QLatin1String(file.readAll());
            combinedStyle += "\n";
        }
    }
    
    qApp->setStyleSheet(combinedStyle);
}

// 使用
loadStyleModules({"base", "buttons", "inputs", "menus"});
可重用样式组件

定义样式组件

css 复制代码
/* components/button.qss */
.Button {
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
    font-size: 14px;
}

.Button-Primary {
    background-color: #4CAF50;
    color: white;
}

.Button-Secondary {
    background-color: #2196F3;
    color: white;
}

.Button-Danger {
    background-color: #f44336;
    color: white;
}

使用样式组件

cpp 复制代码
QPushButton *primaryBtn = new QPushButton("确定");
primaryBtn->setProperty("class", "Button Button-Primary");

QPushButton *secondaryBtn = new QPushButton("取消");
secondaryBtn->setProperty("class", "Button Button-Secondary");

样式组件库

cpp 复制代码
class StyleComponentLibrary {
public:
    static QString getComponent(const QString &componentName) {
        QString filePath = QString(":/styles/components/%1.qss").arg(componentName);
        QFile file(filePath);
        if (file.open(QFile::ReadOnly)) {
            return QLatin1String(file.readAll());
        }
        return QString();
    }
    
    static void loadComponent(const QString &componentName) {
        QString component = getComponent(componentName);
        QString currentStyle = qApp->styleSheet();
        qApp->setStyleSheet(currentStyle + "\n" + component);
    }
};

// 使用
StyleComponentLibrary::loadComponent("button");
StyleComponentLibrary::loadComponent("input");
样式变量和常量

使用QSS变量(通过属性)

cpp 复制代码
// 定义颜色常量
class StyleConstants {
public:
    static const QColor PrimaryColor = QColor(76, 175, 80);
    static const QColor SecondaryColor = QColor(33, 150, 243);
    static const QColor DangerColor = QColor(244, 67, 54);
    static const int BorderRadius = 5;
    static const int Padding = 10;
};

// 在QSS中使用(通过属性)
qApp->setProperty("primaryColor", StyleConstants::PrimaryColor);
qApp->setProperty("borderRadius", StyleConstants::BorderRadius);

// QSS中引用(需要自定义处理)

使用配置文件

json 复制代码
// style-config.json
{
    "colors": {
        "primary": "#4CAF50",
        "secondary": "#2196F3",
        "danger": "#f44336"
    },
    "sizes": {
        "borderRadius": 5,
        "padding": 10
    }
}

在代码中替换变量

cpp 复制代码
QString loadStyleWithVariables(const QString &stylePath) {
    QFile file(stylePath);
    if (!file.open(QFile::ReadOnly)) {
        return QString();
    }
    
    QString styleSheet = QLatin1String(file.readAll());
    
    // 替换变量
    styleSheet.replace("$primaryColor", "#4CAF50");
    styleSheet.replace("$secondaryColor", "#2196F3");
    styleSheet.replace("$borderRadius", "5px");
    styleSheet.replace("$padding", "10px");
    
    return styleSheet;
}
样式的条件应用

根据平台应用样式

cpp 复制代码
void loadPlatformSpecificStyle() {
    QString styleSheet;
    
#ifdef Q_OS_WIN
    styleSheet = loadStyle("windows-style.qss");
#elif defined(Q_OS_MAC)
    styleSheet = loadStyle("macos-style.qss");
#else
    styleSheet = loadStyle("linux-style.qss");
#endif
    
    qApp->setStyleSheet(styleSheet);
}

根据主题应用样式

cpp 复制代码
void loadThemeStyle(const QString &theme) {
    QString stylePath = QString(":/styles/themes/%1.qss").arg(theme);
    QString styleSheet = loadStyle(stylePath);
    
    // 根据主题替换变量
    if (theme == "dark") {
        styleSheet.replace("$bgColor", "#2b2b2b");
        styleSheet.replace("$textColor", "#ffffff");
    } else {
        styleSheet.replace("$bgColor", "#ffffff");
        styleSheet.replace("$textColor", "#000000");
    }
    
    qApp->setStyleSheet(styleSheet);
}

根据控件状态应用样式

cpp 复制代码
void applyConditionalStyle(QWidget *widget, const QString &condition) {
    QString style;
    
    if (condition == "enabled") {
        style = "QPushButton { background-color: #4CAF50; }";
    } else if (condition == "disabled") {
        style = "QPushButton { background-color: #cccccc; }";
    }
    
    widget->setStyleSheet(style);
}

7.4 样式的国际化

不同语言的样式适配

字体适配

cpp 复制代码
void adaptFontForLanguage(const QString &language) {
    QFont font;
    
    if (language == "zh_CN" || language == "zh_TW") {
        font.setFamily("Microsoft YaHei");
    } else if (language == "ja") {
        font.setFamily("Meiryo");
    } else if (language == "ko") {
        font.setFamily("Malgun Gothic");
    } else {
        font.setFamily("Arial");
    }
    
    qApp->setFont(font);
}

尺寸适配

cpp 复制代码
void adaptSizeForLanguage(const QString &language) {
    // 某些语言的文本可能更长,需要调整控件尺寸
    int buttonWidth = 100;
    
    if (language == "de" || language == "ru") {
        buttonWidth = 120;  // 德语和俄语文本通常较长
    }
    
    qApp->setProperty("buttonMinWidth", buttonWidth);
}

颜色适配

cpp 复制代码
void adaptColorForLanguage(const QString &language) {
    // 某些语言可能有特定的颜色偏好
    QPalette palette = qApp->palette();
    
    if (language == "ar" || language == "he") {
        // 阿拉伯语和希伯来语可能需要不同的颜色方案
        palette.setColor(QPalette::Window, QColor(250, 250, 250));
    }
    
    qApp->setPalette(palette);
}
RTL(从右到左)布局

检测RTL语言

cpp 复制代码
bool isRTLanguage(const QString &language) {
    QStringList rtlLanguages = {"ar", "he", "fa", "ur"};
    return rtlLanguages.contains(language);
}

设置布局方向

cpp 复制代码
void setLayoutDirection(const QString &language) {
    if (isRTLanguage(language)) {
        qApp->setLayoutDirection(Qt::RightToLeft);
    } else {
        qApp->setLayoutDirection(Qt::LeftToRight);
    }
}

RTL样式适配

cpp 复制代码
void adaptStyleForRTL(bool isRTL) {
    QString styleSheet = qApp->styleSheet();
    
    if (isRTL) {
        // RTL特定的样式调整
        styleSheet += R"(
            QPushButton {
                text-align: right;
            }
            QMenuBar {
                layout-direction: rtl;
            }
        )";
    }
    
    qApp->setStyleSheet(styleSheet);
}

镜像图标

cpp 复制代码
QIcon getMirroredIcon(const QIcon &icon, bool mirror) {
    if (!mirror) {
        return icon;
    }
    
    QPixmap pixmap = icon.pixmap(16, 16);
    QTransform transform;
    transform.scale(-1, 1);
    QPixmap mirrored = pixmap.transformed(transform);
    
    return QIcon(mirrored);
}
字体的国际化支持

字体回退机制

cpp 复制代码
QFont getInternationalFont(const QString &text) {
    QFont font;
    
    // 检测文本语言
    QString language = detectLanguage(text);
    
    if (language == "zh_CN") {
        font.setFamily("Microsoft YaHei, SimHei, Arial");
    } else if (language == "ja") {
        font.setFamily("Meiryo, MS Gothic, Arial");
    } else if (language == "ko") {
        font.setFamily("Malgun Gothic, Gulim, Arial");
    } else {
        font.setFamily("Arial, sans-serif");
    }
    
    return font;
}

字体大小适配

cpp 复制代码
int getFontSizeForLanguage(const QString &language) {
    // 不同语言的字体大小可能需要调整
    QMap<QString, int> fontSizeMap = {
        {"zh_CN", 12},
        {"ja", 11},
        {"ko", 11},
        {"ar", 12},
        {"default", 10}
    };
    
    return fontSizeMap.value(language, fontSizeMap["default"]);
}

字体加载

cpp 复制代码
void loadInternationalFonts() {
    // 加载中文字体
    QFontDatabase::addApplicationFont(":/fonts/chinese.ttf");
    
    // 加载日文字体
    QFontDatabase::addApplicationFont(":/fonts/japanese.ttf");
    
    // 加载阿拉伯字体
    QFontDatabase::addApplicationFont(":/fonts/arabic.ttf");
}

7.5 响应式样式设计

不同屏幕尺寸的适配

检测屏幕尺寸

cpp 复制代码
QString getScreenSizeCategory() {
    QScreen *screen = qApp->primaryScreen();
    QSize screenSize = screen->size();
    int width = screenSize.width();
    
    if (width < 640) {
        return "small";      // 手机
    } else if (width < 1024) {
        return "medium";     // 平板
    } else {
        return "large";      // 桌面
    }
}

根据屏幕尺寸加载样式

cpp 复制代码
void loadResponsiveStyle() {
    QString sizeCategory = getScreenSizeCategory();
    QString stylePath = QString(":/styles/responsive/%1.qss").arg(sizeCategory);
    
    QFile file(stylePath);
    if (file.open(QFile::ReadOnly)) {
        QString styleSheet = QLatin1String(file.readAll());
        qApp->setStyleSheet(styleSheet);
    }
}

动态调整样式

cpp 复制代码
void adaptStyleForScreenSize() {
    QString sizeCategory = getScreenSizeCategory();
    QString currentStyle = qApp->styleSheet();
    
    if (sizeCategory == "small") {
        // 小屏幕:减小字体、减小内边距
        currentStyle += R"(
            QPushButton { font-size: 12px; padding: 5px; }
            QLabel { font-size: 12px; }
        )";
    } else if (sizeCategory == "medium") {
        // 中等屏幕:中等字体、中等内边距
        currentStyle += R"(
            QPushButton { font-size: 14px; padding: 8px; }
            QLabel { font-size: 14px; }
        )";
    } else {
        // 大屏幕:正常字体、正常内边距
        currentStyle += R"(
            QPushButton { font-size: 16px; padding: 10px; }
            QLabel { font-size: 16px; }
        )";
    }
    
    qApp->setStyleSheet(currentStyle);
}
DPI感知样式

检测DPI

cpp 复制代码
qreal getDevicePixelRatio() {
    QScreen *screen = qApp->primaryScreen();
    return screen->devicePixelRatio();
}

qreal getLogicalDpi() {
    QScreen *screen = qApp->primaryScreen();
    return screen->logicalDotsPerInch();
}

DPI适配

cpp 复制代码
void adaptStyleForDPI() {
    qreal dpi = getLogicalDpi();
    qreal scaleFactor = dpi / 96.0;  // 96 DPI是标准DPI
    
    QString currentStyle = qApp->styleSheet();
    
    // 根据DPI调整字体大小
    int baseFontSize = 12;
    int scaledFontSize = static_cast<int>(baseFontSize * scaleFactor);
    
    currentStyle += QString("QWidget { font-size: %1px; }").arg(scaledFontSize);
    
    qApp->setStyleSheet(currentStyle);
}

高DPI图标

cpp 复制代码
QIcon getScaledIcon(const QString &iconPath, int baseSize) {
    qreal dpi = getDevicePixelRatio();
    int scaledSize = static_cast<int>(baseSize * dpi);
    
    QIcon icon;
    QPixmap pixmap(iconPath);
    icon.addPixmap(pixmap.scaled(scaledSize, scaledSize, 
                                 Qt::KeepAspectRatio, 
                                 Qt::SmoothTransformation));
    return icon;
}
高分辨率屏幕适配

检测高分辨率屏幕

cpp 复制代码
bool isHighDpiScreen() {
    qreal dpi = getLogicalDpi();
    return dpi > 120;  // 120 DPI以上认为是高分辨率
}

高分辨率样式适配

cpp 复制代码
void adaptStyleForHighDpi() {
    if (isHighDpiScreen()) {
        QString currentStyle = qApp->styleSheet();
        
        // 高分辨率屏幕:使用更细的边框、更大的图标
        currentStyle += R"(
            QPushButton { border-width: 1px; }
            QLineEdit { border-width: 1px; }
        )";
        
        qApp->setStyleSheet(currentStyle);
    }
}

使用SVG图标

cpp 复制代码
// SVG图标可以无损缩放,适合高分辨率屏幕
QIcon getSVGIcon(const QString &svgPath, const QSize &size) {
    QIcon icon;
    QSvgRenderer renderer(svgPath);
    QPixmap pixmap(size);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    renderer.render(&painter);
    icon.addPixmap(pixmap);
    return icon;
}
窗口大小变化的样式调整

监听窗口大小变化

cpp 复制代码
class ResponsiveWindow : public QMainWindow {
    Q_OBJECT
    
protected:
    void resizeEvent(QResizeEvent *event) override {
        QMainWindow::resizeEvent(event);
        adaptStyleForWindowSize(event->size());
    }
    
private:
    void adaptStyleForWindowSize(const QSize &size) {
        QString currentStyle = qApp->styleSheet();
        
        if (size.width() < 800) {
            // 小窗口:紧凑布局
            currentStyle += R"(
                QPushButton { padding: 5px; min-width: 60px; }
                QLabel { font-size: 12px; }
            )";
        } else if (size.width() < 1200) {
            // 中等窗口:正常布局
            currentStyle += R"(
                QPushButton { padding: 8px; min-width: 80px; }
                QLabel { font-size: 14px; }
            )";
        } else {
            // 大窗口:宽松布局
            currentStyle += R"(
                QPushButton { padding: 10px; min-width: 100px; }
                QLabel { font-size: 16px; }
            )";
        }
        
        qApp->setStyleSheet(currentStyle);
    }
};

使用媒体查询(模拟)

cpp 复制代码
QString applyMediaQueries(const QString &styleSheet, const QSize &windowSize) {
    QString result = styleSheet;
    
    // 模拟CSS媒体查询
    // @media (max-width: 800px) { ... }
    if (windowSize.width() <= 800) {
        result += R"(
            QPushButton { font-size: 12px; padding: 5px; }
        )";
    }
    
    // @media (min-width: 1200px) { ... }
    if (windowSize.width() >= 1200) {
        result += R"(
            QPushButton { font-size: 16px; padding: 12px; }
        )";
    }
    
    return result;
}


8. 主题系统与皮肤切换

8.1 主题系统设计

主题系统是现代应用程序的重要组成部分,它允许用户自定义应用程序的视觉外观。

主题的定义和组成

主题是什么?

主题是一套完整的视觉样式配置,包括:

  • 颜色方案(QPalette)
  • 样式表(QSS)
  • 图标和图片资源
  • 字体配置
  • 其他视觉元素

主题的组成部分

复制代码
主题
├── 颜色方案(QPalette)
│   ├── 窗口颜色
│   ├── 文本颜色
│   ├── 按钮颜色
│   └── 高亮颜色
├── 样式表(QSS)
│   ├── 基础样式
│   ├── 组件样式
│   └── 布局样式
├── 资源文件
│   ├── 图标
│   ├── 图片
│   └── 字体
└── 配置信息
    ├── 主题名称
    ├── 主题版本
    └── 主题描述

主题类设计

cpp 复制代码
class Theme {
public:
    QString name;              // 主题名称
    QString version;           // 主题版本
    QString description;      // 主题描述
    QString author;            // 主题作者
    
    QPalette palette;          // 调色板
    QString styleSheet;        // 样式表
    QMap<QString, QString> resources;  // 资源文件路径
    
    bool isValid() const {
        return !name.isEmpty() && !styleSheet.isEmpty();
    }
    
    void apply() {
        qApp->setPalette(palette);
        qApp->setStyleSheet(styleSheet);
    }
};
主题配置文件

JSON格式配置

json 复制代码
{
    "name": "Dark Theme",
    "version": "1.0.0",
    "description": "A dark theme for the application",
    "author": "Developer",
    "palette": {
        "window": "#2b2b2b",
        "windowText": "#ffffff",
        "base": "#353535",
        "text": "#ffffff",
        "button": "#3c3f41",
        "buttonText": "#ffffff",
        "highlight": "#0078d4",
        "highlightedText": "#ffffff"
    },
    "styleSheet": "themes/dark/style.qss",
    "resources": {
        "icons": "themes/dark/icons/",
        "images": "themes/dark/images/",
        "fonts": "themes/dark/fonts/"
    }
}

XML格式配置

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<theme>
    <name>Dark Theme</name>
    <version>1.0.0</version>
    <description>A dark theme for the application</description>
    <author>Developer</author>
    <palette>
        <color role="window">#2b2b2b</color>
        <color role="windowText">#ffffff</color>
        <color role="base">#353535</color>
        <color role="text">#ffffff</color>
        <color role="button">#3c3f41</color>
        <color role="buttonText">#ffffff</color>
        <color role="highlight">#0078d4</color>
        <color role="highlightedText">#ffffff</color>
    </palette>
    <styleSheet>themes/dark/style.qss</styleSheet>
    <resources>
        <icons>themes/dark/icons/</icons>
        <images>themes/dark/images/</images>
        <fonts>themes/dark/fonts/</fonts>
    </resources>
</theme>

加载主题配置

cpp 复制代码
Theme loadThemeFromJson(const QString &configPath) {
    Theme theme;
    
    QFile file(configPath);
    if (!file.open(QFile::ReadOnly)) {
        return theme;
    }
    
    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    QJsonObject obj = doc.object();
    
    theme.name = obj["name"].toString();
    theme.version = obj["version"].toString();
    theme.description = obj["description"].toString();
    theme.author = obj["author"].toString();
    
    // 加载调色板
    QJsonObject paletteObj = obj["palette"].toObject();
    theme.palette.setColor(QPalette::Window, QColor(paletteObj["window"].toString()));
    theme.palette.setColor(QPalette::WindowText, QColor(paletteObj["windowText"].toString()));
    // ... 加载其他颜色
    
    // 加载样式表
    QString styleSheetPath = obj["styleSheet"].toString();
    QFile styleFile(styleSheetPath);
    if (styleFile.open(QFile::ReadOnly)) {
        theme.styleSheet = QLatin1String(styleFile.readAll());
    }
    
    return theme;
}
主题管理器

主题管理器类

cpp 复制代码
class ThemeManager : public QObject {
    Q_OBJECT
    
public:
    static ThemeManager* instance() {
        static ThemeManager instance;
        return &instance;
    }
    
    // 加载主题
    bool loadTheme(const QString &themeName) {
        QString configPath = getThemeConfigPath(themeName);
        Theme theme = loadThemeFromJson(configPath);
        
        if (!theme.isValid()) {
            qWarning() << "主题无效:" << themeName;
            return false;
        }
        
        // 应用主题
        theme.apply();
        
        // 加载资源
        loadThemeResources(theme);
        
        // 保存当前主题
        m_currentTheme = themeName;
        saveCurrentTheme();
        
        emit themeChanged(themeName);
        return true;
    }
    
    // 获取可用主题列表
    QStringList availableThemes() const {
        QStringList themes;
        QDir themesDir(getThemesDirectory());
        QStringList entries = themesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
        
        for (const QString &entry : entries) {
            QString configPath = themesDir.filePath(entry + "/theme.json");
            if (QFile::exists(configPath)) {
                themes << entry;
            }
        }
        
        return themes;
    }
    
    // 获取当前主题
    QString currentTheme() const {
        return m_currentTheme;
    }
    
signals:
    void themeChanged(const QString &themeName);
    
private:
    QString getThemesDirectory() const {
        return QApplication::applicationDirPath() + "/themes";
    }
    
    QString getThemeConfigPath(const QString &themeName) const {
        return getThemesDirectory() + "/" + themeName + "/theme.json";
    }
    
    void loadThemeResources(const Theme &theme) {
        // 加载图标、图片、字体等资源
        // ...
    }
    
    void saveCurrentTheme() {
        QSettings settings;
        settings.setValue("theme/current", m_currentTheme);
    }
    
    QString m_currentTheme;
};

使用主题管理器

cpp 复制代码
// 获取可用主题
QStringList themes = ThemeManager::instance()->availableThemes();
qDebug() << "可用主题:" << themes;

// 加载主题
ThemeManager::instance()->loadTheme("dark");

// 监听主题变化
connect(ThemeManager::instance(), &ThemeManager::themeChanged,
        [](const QString &themeName) {
    qDebug() << "主题已切换为:" << themeName;
});
主题的持久化存储

保存主题设置

cpp 复制代码
void saveThemeSettings(const QString &themeName) {
    QSettings settings;
    settings.setValue("theme/current", themeName);
    settings.setValue("theme/lastChanged", QDateTime::currentDateTime().toString());
}

// 使用
saveThemeSettings("dark");

加载主题设置

cpp 复制代码
QString loadThemeSettings() {
    QSettings settings;
    QString themeName = settings.value("theme/current", "light").toString();
    return themeName;
}

// 应用启动时加载
QString savedTheme = loadThemeSettings();
ThemeManager::instance()->loadTheme(savedTheme);

主题历史记录

cpp 复制代码
class ThemeHistory {
public:
    static void addTheme(const QString &themeName) {
        QSettings settings;
        QStringList history = settings.value("theme/history").toStringList();
        
        // 移除重复项
        history.removeAll(themeName);
        
        // 添加到开头
        history.prepend(themeName);
        
        // 限制历史记录数量
        if (history.size() > 10) {
            history = history.mid(0, 10);
        }
        
        settings.setValue("theme/history", history);
    }
    
    static QStringList getHistory() {
        QSettings settings;
        return settings.value("theme/history").toStringList();
    }
};

8.2 皮肤切换实现

皮肤是主题的扩展,通常包含更多的视觉元素和自定义资源。

皮肤资源的组织

皮肤目录结构

复制代码
skins/
├── default/
│   ├── skin.json          # 皮肤配置
│   ├── style.qss          # 样式表
│   ├── icons/             # 图标
│   │   ├── button.png
│   │   └── icon.png
│   ├── images/            # 图片
│   │   ├── background.png
│   │   └── border.png
│   └── fonts/             # 字体
│       └── customfont.ttf
├── dark/
│   └── ...
└── custom/
    └── ...

皮肤配置文件

json 复制代码
{
    "name": "Default Skin",
    "version": "1.0.0",
    "author": "Developer",
    "description": "Default skin for the application",
    "styleSheet": "style.qss",
    "icons": {
        "button": "icons/button.png",
        "icon": "icons/icon.png"
    },
    "images": {
        "background": "images/background.png",
        "border": "images/border.png"
    },
    "fonts": {
        "main": "fonts/customfont.ttf"
    }
}
皮肤的动态加载

皮肤加载器

cpp 复制代码
class SkinLoader {
public:
    static bool loadSkin(const QString &skinName) {
        QString skinPath = getSkinPath(skinName);
        QString configPath = skinPath + "/skin.json";
        
        // 加载配置
        QJsonObject config = loadConfig(configPath);
        if (config.isEmpty()) {
            return false;
        }
        
        // 加载样式表
        QString styleSheetPath = skinPath + "/" + config["styleSheet"].toString();
        QString styleSheet = loadStyleSheet(styleSheetPath);
        
        // 替换资源路径
        styleSheet = replaceResourcePaths(styleSheet, skinPath);
        
        // 应用样式表
        qApp->setStyleSheet(styleSheet);
        
        // 加载资源
        loadResources(config, skinPath);
        
        return true;
    }
    
private:
    static QString getSkinPath(const QString &skinName) {
        return QApplication::applicationDirPath() + "/skins/" + skinName;
    }
    
    static QJsonObject loadConfig(const QString &configPath) {
        QFile file(configPath);
        if (!file.open(QFile::ReadOnly)) {
            return QJsonObject();
        }
        
        QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
        return doc.object();
    }
    
    static QString loadStyleSheet(const QString &styleSheetPath) {
        QFile file(styleSheetPath);
        if (!file.open(QFile::ReadOnly)) {
            return QString();
        }
        return QLatin1String(file.readAll());
    }
    
    static QString replaceResourcePaths(const QString &styleSheet, const QString &skinPath) {
        QString result = styleSheet;
        // 替换相对路径为绝对路径
        result.replace("url(icons/", QString("url(%1/icons/").arg(skinPath));
        result.replace("url(images/", QString("url(%1/images/").arg(skinPath));
        return result;
    }
    
    static void loadResources(const QJsonObject &config, const QString &skinPath) {
        // 加载字体
        QJsonObject fonts = config["fonts"].toObject();
        for (auto it = fonts.begin(); it != fonts.end(); ++it) {
            QString fontPath = skinPath + "/" + it.value().toString();
            QFontDatabase::addApplicationFont(fontPath);
        }
    }
};
皮肤的预览功能

皮肤预览窗口

cpp 复制代码
class SkinPreviewWidget : public QWidget {
    Q_OBJECT
    
public:
    SkinPreviewWidget(const QString &skinName, QWidget *parent = nullptr)
        : QWidget(parent), m_skinName(skinName)
    {
        setupPreview();
        loadSkinPreview(skinName);
    }
    
private:
    void setupPreview() {
        QVBoxLayout *layout = new QVBoxLayout(this);
        
        // 预览控件
        QPushButton *btn = new QPushButton("预览按钮", this);
        QLineEdit *edit = new QLineEdit("预览输入框", this);
        QLabel *label = new QLabel("预览标签", this);
        
        layout->addWidget(btn);
        layout->addWidget(edit);
        layout->addWidget(label);
    }
    
    void loadSkinPreview(const QString &skinName) {
        // 临时加载皮肤
        QString originalStyle = qApp->styleSheet();
        SkinLoader::loadSkin(skinName);
        m_previewStyle = qApp->styleSheet();
        
        // 恢复原样式(不影响主窗口)
        qApp->setStyleSheet(originalStyle);
        
        // 应用预览样式到预览窗口
        setStyleSheet(m_previewStyle);
    }
    
    QString m_skinName;
    QString m_previewStyle;
};

皮肤预览对话框

cpp 复制代码
class SkinPreviewDialog : public QDialog {
    Q_OBJECT
    
public:
    SkinPreviewDialog(const QString &skinName, QWidget *parent = nullptr)
        : QDialog(parent)
    {
        setWindowTitle("皮肤预览 - " + skinName);
        resize(400, 300);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        
        // 预览区域
        SkinPreviewWidget *preview = new SkinPreviewWidget(skinName, this);
        layout->addWidget(preview);
        
        // 按钮
        QPushButton *applyBtn = new QPushButton("应用", this);
        QPushButton *cancelBtn = new QPushButton("取消", this);
        
        QHBoxLayout *btnLayout = new QHBoxLayout;
        btnLayout->addStretch();
        btnLayout->addWidget(applyBtn);
        btnLayout->addWidget(cancelBtn);
        layout->addLayout(btnLayout);
        
        connect(applyBtn, &QPushButton::clicked, [this, skinName]() {
            SkinLoader::loadSkin(skinName);
            accept();
        });
        
        connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
    }
};
皮肤的导入导出

导出皮肤

cpp 复制代码
class SkinExporter {
public:
    static bool exportSkin(const QString &skinName, const QString &exportPath) {
        QString skinPath = getSkinPath(skinName);
        
        // 创建导出目录
        QDir exportDir(exportPath);
        if (!exportDir.exists()) {
            exportDir.mkpath(".");
        }
        
        // 复制皮肤文件
        QDir skinDir(skinPath);
        QStringList files = skinDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
        
        for (const QString &file : files) {
            QString srcPath = skinPath + "/" + file;
            QString dstPath = exportPath + "/" + file;
            
            if (QFileInfo(srcPath).isDir()) {
                // 复制目录
                copyDirectory(srcPath, dstPath);
            } else {
                // 复制文件
                QFile::copy(srcPath, dstPath);
            }
        }
        
        return true;
    }
    
private:
    static void copyDirectory(const QString &src, const QString &dst) {
        QDir dir;
        dir.mkpath(dst);
        
        QDir srcDir(src);
        QStringList files = srcDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
        
        for (const QString &file : files) {
            QString srcPath = src + "/" + file;
            QString dstPath = dst + "/" + file;
            
            if (QFileInfo(srcPath).isDir()) {
                copyDirectory(srcPath, dstPath);
            } else {
                QFile::copy(srcPath, dstPath);
            }
        }
    }
    
    static QString getSkinPath(const QString &skinName) {
        return QApplication::applicationDirPath() + "/skins/" + skinName;
    }
};

导入皮肤

cpp 复制代码
class SkinImporter {
public:
    static bool importSkin(const QString &skinPath, const QString &skinName) {
        QString targetPath = getSkinPath(skinName);
        
        // 检查皮肤是否已存在
        if (QDir(targetPath).exists()) {
            qWarning() << "皮肤已存在:" << skinName;
            return false;
        }
        
        // 验证皮肤配置
        QString configPath = skinPath + "/skin.json";
        if (!QFile::exists(configPath)) {
            qWarning() << "皮肤配置文件不存在";
            return false;
        }
        
        // 复制皮肤文件
        SkinExporter::exportSkin(skinPath, targetPath);
        
        return true;
    }
    
private:
    static QString getSkinPath(const QString &skinName) {
        return QApplication::applicationDirPath() + "/skins/" + skinName;
    }
};

打包皮肤为ZIP

cpp 复制代码
bool packageSkin(const QString &skinName, const QString &zipPath) {
    QString skinPath = getSkinPath(skinName);
    
    // 使用QuaZip或其他ZIP库打包
    // 这里使用简化的示例
    QFileInfoList files = QDir(skinPath).entryInfoList(
        QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
        QDir::Name
    );
    
    // 创建ZIP文件并添加文件
    // ...
    
    return true;
}

8.3 系统主题集成

Windows主题检测

检测Windows主题

cpp 复制代码
#ifdef Q_OS_WIN
#include <windows.h>
#include <QSettings>

bool isWindowsDarkTheme() {
    QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
                       QSettings::NativeFormat);
    int appsUseLightTheme = settings.value("AppsUseLightTheme", 1).toInt();
    return appsUseLightTheme == 0;
}

QString getWindowsTheme() {
    return isWindowsDarkTheme() ? "dark" : "light";
}
#endif

监听Windows主题变化

cpp 复制代码
#ifdef Q_OS_WIN
#include <QWinEventNotifier>
#include <windows.h>

class WindowsThemeWatcher : public QObject {
    Q_OBJECT
    
public:
    WindowsThemeWatcher(QObject *parent = nullptr) : QObject(parent) {
        // 注册表键路径
        QString keyPath = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
        
        // 创建事件通知器(需要自定义实现)
        // Windows API: RegNotifyChangeKeyValue
        // ...
    }
    
signals:
    void themeChanged(bool isDark);
};
#endif
macOS主题检测

检测macOS主题

cpp 复制代码
#ifdef Q_OS_MAC
#include <QMacNativeWidget>

bool isMacOSDarkTheme() {
    // macOS 10.14+
    if (@available(macOS 10.14, *)) {
        NSAppearance *appearance = [NSAppearance currentAppearance];
        NSString *name = [appearance name];
        return [name containsString:@"Dark"];
    }
    return false;
}

QString getMacOSTheme() {
    return isMacOSDarkTheme() ? "dark" : "light";
}
#endif

监听macOS主题变化

cpp 复制代码
#ifdef Q_OS_MAC
class MacOSThemeWatcher : public QObject {
    Q_OBJECT
    
public:
    MacOSThemeWatcher(QObject *parent = nullptr) : QObject(parent) {
        // 监听系统通知
        [[NSDistributedNotificationCenter defaultCenter]
            addObserverForName:@"AppleInterfaceThemeChangedNotification"
            object:nil
            queue:nil
            usingBlock:^(NSNotification *note) {
                bool isDark = isMacOSDarkTheme();
                emit themeChanged(isDark);
            }];
    }
    
signals:
    void themeChanged(bool isDark);
};
#endif
Linux主题检测

检测Linux主题

cpp 复制代码
#ifdef Q_OS_LINUX
QString getLinuxTheme() {
    // 检测GTK主题
    QProcess process;
    process.start("gsettings", QStringList() << "get" << "org.gnome.desktop.interface" << "gtk-theme");
    process.waitForFinished();
    
    QString theme = process.readAllStandardOutput().trimmed();
    if (theme.contains("dark", Qt::CaseInsensitive)) {
        return "dark";
    }
    
    return "light";
}
#endif

监听Linux主题变化

cpp 复制代码
#ifdef Q_OS_LINUX
class LinuxThemeWatcher : public QObject {
    Q_OBJECT
    
public:
    LinuxThemeWatcher(QObject *parent = nullptr) : QObject(parent) {
        // 使用QFileSystemWatcher监听gsettings变化
        // 或使用D-Bus监听主题变化
        // ...
    }
    
signals:
    void themeChanged(const QString &theme);
};
#endif
自动跟随系统主题

系统主题适配器

cpp 复制代码
class SystemThemeAdapter : public QObject {
    Q_OBJECT
    
public:
    SystemThemeAdapter(QObject *parent = nullptr) : QObject(parent) {
        detectSystemTheme();
        setupWatcher();
    }
    
    void setAutoFollow(bool enabled) {
        m_autoFollow = enabled;
        if (enabled) {
            applySystemTheme();
        }
    }
    
signals:
    void systemThemeChanged(const QString &theme);
    
private:
    void detectSystemTheme() {
#ifdef Q_OS_WIN
        m_systemTheme = getWindowsTheme();
#elif defined(Q_OS_MAC)
        m_systemTheme = getMacOSTheme();
#elif defined(Q_OS_LINUX)
        m_systemTheme = getLinuxTheme();
#else
        m_systemTheme = "light";
#endif
    }
    
    void setupWatcher() {
#ifdef Q_OS_WIN
        m_watcher = new WindowsThemeWatcher(this);
        connect(m_watcher, &WindowsThemeWatcher::themeChanged,
                this, &SystemThemeAdapter::onSystemThemeChanged);
#elif defined(Q_OS_MAC)
        m_watcher = new MacOSThemeWatcher(this);
        connect(m_watcher, &MacOSThemeWatcher::themeChanged,
                this, &SystemThemeAdapter::onSystemThemeChanged);
#elif defined(Q_OS_LINUX)
        m_watcher = new LinuxThemeWatcher(this);
        connect(m_watcher, &LinuxThemeWatcher::themeChanged,
                this, &SystemThemeAdapter::onSystemThemeChanged);
#endif
    }
    
    void applySystemTheme() {
        ThemeManager::instance()->loadTheme(m_systemTheme);
    }
    
private slots:
    void onSystemThemeChanged(bool isDark) {
        m_systemTheme = isDark ? "dark" : "light";
        if (m_autoFollow) {
            applySystemTheme();
        }
        emit systemThemeChanged(m_systemTheme);
    }
    
    bool m_autoFollow = false;
    QString m_systemTheme;
    QObject *m_watcher = nullptr;
};

// 使用
SystemThemeAdapter *adapter = new SystemThemeAdapter();
adapter->setAutoFollow(true);

8.4 暗色模式支持

暗色模式的设计原则

设计原则

  1. 对比度:确保文本和背景有足够的对比度(WCAG AA标准:4.5:1)
  2. 层次感:使用不同的灰度层次区分内容层级
  3. 舒适性:避免纯黑背景,使用深灰色(如#2b2b2b)
  4. 一致性:保持与亮色模式相同的视觉层次和结构
  5. 可读性:确保所有文本清晰可读

颜色选择

  • 背景色:深灰色(#2b2b2b, #1e1e1e)而不是纯黑
  • 文本色:浅灰色(#ffffff, #e0e0e0)而不是纯白
  • 强调色:使用较亮的颜色作为强调
  • 边框色:使用中等灰色区分元素
颜色方案的设计

完整的暗色主题颜色方案

cpp 复制代码
QPalette createDarkPalette() {
    QPalette palette;
    
    // 窗口相关
    palette.setColor(QPalette::Window, QColor(43, 43, 43));        // #2b2b2b
    palette.setColor(QPalette::WindowText, QColor(224, 224, 224)); // #e0e0e0
    
    // 输入控件相关
    palette.setColor(QPalette::Base, QColor(53, 53, 53));          // #353535
    palette.setColor(QPalette::AlternateBase, QColor(60, 60, 60)); // #3c3c3c
    palette.setColor(QPalette::Text, QColor(224, 224, 224));      // #e0e0e0
    palette.setColor(QPalette::PlaceholderText, QColor(128, 128, 128)); // #808080
    
    // 按钮相关
    palette.setColor(QPalette::Button, QColor(60, 60, 60));        // #3c3c3c
    palette.setColor(QPalette::ButtonText, QColor(224, 224, 224)); // #e0e0e0
    
    // 高亮相关
    palette.setColor(QPalette::Highlight, QColor(0, 120, 212));    // #0078d4
    palette.setColor(QPalette::HighlightedText, Qt::white);
    
    // 链接相关
    palette.setColor(QPalette::Link, QColor(100, 150, 255));       // #6496ff
    palette.setColor(QPalette::LinkVisited, QColor(200, 100, 255)); // #c864ff
    
    // 工具提示相关
    palette.setColor(QPalette::ToolTipBase, QColor(60, 60, 60));   // #3c3c3c
    palette.setColor(QPalette::ToolTipText, QColor(224, 224, 224)); // #e0e0e0
    
    // 禁用状态
    palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(128, 128, 128));
    palette.setColor(QPalette::Disabled, QPalette::Text, QColor(128, 128, 128));
    palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(128, 128, 128));
    
    return palette;
}

暗色主题QSS

css 复制代码
/* dark-theme.qss */
QWidget {
    background-color: #2b2b2b;
    color: #e0e0e0;
}

QPushButton {
    background-color: #3c3c3c;
    color: #e0e0e0;
    border: 1px solid #555555;
    padding: 8px 16px;
    border-radius: 4px;
}

QPushButton:hover {
    background-color: #4a4a4a;
}

QPushButton:pressed {
    background-color: #2d2d2d;
}

QPushButton:disabled {
    background-color: #2b2b2b;
    color: #808080;
}

QLineEdit, QTextEdit {
    background-color: #353535;
    color: #e0e0e0;
    border: 1px solid #555555;
    padding: 5px;
    border-radius: 3px;
}

QLineEdit:focus, QTextEdit:focus {
    border: 2px solid #0078d4;
}

QScrollBar:vertical {
    background-color: #2b2b2b;
    width: 12px;
}

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

QScrollBar::handle:vertical:hover {
    background-color: #666666;
}
暗色模式的实现

暗色模式管理器

cpp 复制代码
class DarkModeManager : public QObject {
    Q_OBJECT
    
public:
    static DarkModeManager* instance() {
        static DarkModeManager instance;
        return &instance;
    }
    
    void enableDarkMode(bool enabled) {
        if (enabled) {
            applyDarkMode();
        } else {
            applyLightMode();
        }
        
        m_darkModeEnabled = enabled;
        saveDarkModeState();
        emit darkModeChanged(enabled);
    }
    
    bool isDarkModeEnabled() const {
        return m_darkModeEnabled;
    }
    
signals:
    void darkModeChanged(bool enabled);
    
private:
    void applyDarkMode() {
        // 应用暗色调色板
        QPalette darkPalette = createDarkPalette();
        qApp->setPalette(darkPalette);
        
        // 应用暗色样式表
        QString darkStyleSheet = loadDarkStyleSheet();
        qApp->setStyleSheet(darkStyleSheet);
    }
    
    void applyLightMode() {
        // 应用亮色调色板
        QPalette lightPalette = createLightPalette();
        qApp->setPalette(lightPalette);
        
        // 应用亮色样式表
        QString lightStyleSheet = loadLightStyleSheet();
        qApp->setStyleSheet(lightStyleSheet);
    }
    
    QString loadDarkStyleSheet() {
        QFile file(":/styles/dark-theme.qss");
        if (file.open(QFile::ReadOnly)) {
            return QLatin1String(file.readAll());
        }
        return QString();
    }
    
    QString loadLightStyleSheet() {
        QFile file(":/styles/light-theme.qss");
        if (file.open(QFile::ReadOnly)) {
            return QLatin1String(file.readAll());
        }
        return QString();
    }
    
    void saveDarkModeState() {
        QSettings settings;
        settings.setValue("darkMode/enabled", m_darkModeEnabled);
    }
    
    bool m_darkModeEnabled = false;
};
暗色模式的切换

切换按钮

cpp 复制代码
class DarkModeToggleButton : public QPushButton {
    Q_OBJECT
    
public:
    DarkModeToggleButton(QWidget *parent = nullptr) : QPushButton(parent) {
        setCheckable(true);
        setText("暗色模式");
        
        // 加载保存的状态
        QSettings settings;
        bool darkMode = settings.value("darkMode/enabled", false).toBool();
        setChecked(darkMode);
        
        connect(this, &QPushButton::toggled, [](bool checked) {
            DarkModeManager::instance()->enableDarkMode(checked);
        });
        
        // 监听暗色模式变化
        connect(DarkModeManager::instance(), &DarkModeManager::darkModeChanged,
                this, &DarkModeToggleButton::setChecked);
    }
};

平滑切换动画

cpp 复制代码
void animateDarkModeSwitch(bool toDark) {
    // 使用淡入淡出效果
    QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect(qApp->activeWindow());
    qApp->activeWindow()->setGraphicsEffect(effect);
    
    QPropertyAnimation *fadeOut = new QPropertyAnimation(effect, "opacity");
    fadeOut->setDuration(150);
    fadeOut->setStartValue(1.0);
    fadeOut->setEndValue(0.0);
    
    connect(fadeOut, &QPropertyAnimation::finished, [toDark, effect]() {
        // 切换模式
        DarkModeManager::instance()->enableDarkMode(toDark);
        
        // 淡入
        QPropertyAnimation *fadeIn = new QPropertyAnimation(effect, "opacity");
        fadeIn->setDuration(150);
        fadeIn->setStartValue(0.0);
        fadeIn->setEndValue(1.0);
        
        connect(fadeIn, &QPropertyAnimation::finished, [effect]() {
            qApp->activeWindow()->setGraphicsEffect(nullptr);
            delete effect;
        });
        
        fadeIn->start(QAbstractAnimation::DeleteWhenStopped);
    });
    
    fadeOut->start(QAbstractAnimation::DeleteWhenStopped);
}

自动跟随系统主题

cpp 复制代码
void setupAutoDarkMode() {
    SystemThemeAdapter *adapter = new SystemThemeAdapter();
    
    connect(adapter, &SystemThemeAdapter::systemThemeChanged,
            [](const QString &theme) {
        bool isDark = (theme == "dark");
        DarkModeManager::instance()->enableDarkMode(isDark);
    });
    
    // 初始应用
    QString systemTheme = adapter->property("systemTheme").toString();
    if (systemTheme == "dark") {
        DarkModeManager::instance()->enableDarkMode(true);
    }
}


9. 样式性能优化

9.1 QSS性能优化

QSS性能优化是提高应用程序响应速度的关键。理解QSS的性能特性对于优化应用程序至关重要。

QSS解析的性能开销

QSS解析过程

QSS样式表的解析和应用需要经过以下步骤:

  1. 解析样式表:将QSS文本解析为内部数据结构
  2. 选择器匹配:为每个控件匹配适用的样式规则
  3. 样式计算:计算最终的样式属性值
  4. 应用样式:将样式应用到控件

性能开销分析

cpp 复制代码
// 性能测试
QElapsedTimer timer;
timer.start();

// 解析大型QSS文件
QString largeQSS = loadLargeStyleSheet();  // 假设有10000行
qApp->setStyleSheet(largeQSS);

qint64 parseTime = timer.elapsed();
qDebug() << "QSS解析耗时:" << parseTime << "毫秒";
// 结果:可能耗时50-200毫秒(取决于QSS大小和复杂度)

影响性能的因素

  1. QSS文件大小:文件越大,解析时间越长
  2. 选择器复杂度:复杂选择器匹配时间更长
  3. 规则数量:规则越多,匹配时间越长
  4. 控件数量:控件越多,应用样式时间越长

优化建议

  • 将大型QSS文件拆分为多个小文件
  • 避免过于复杂的选择器
  • 减少不必要的样式规则
  • 使用样式缓存
QSS缓存机制

实现QSS缓存

cpp 复制代码
class QSSCache {
public:
    static QString getStyleSheet(const QString &key) {
        static QMap<QString, QString> cache;
        
        if (!cache.contains(key)) {
            QString styleSheet = loadStyleSheet(key);
            cache[key] = styleSheet;
        }
        
        return cache.value(key);
    }
    
    static void clearCache() {
        static QMap<QString, QString> cache;
        cache.clear();
    }
    
private:
    static QString loadStyleSheet(const QString &key) {
        QString filePath = QString(":/styles/%1.qss").arg(key);
        QFile file(filePath);
        if (file.open(QFile::ReadOnly)) {
            return QLatin1String(file.readAll());
        }
        return QString();
    }
};

// 使用
QString styleSheet = QSSCache::getStyleSheet("dark-theme");
qApp->setStyleSheet(styleSheet);

解析结果缓存

cpp 复制代码
class ParsedStyleCache {
public:
    struct ParsedStyle {
        QString originalQSS;
        QMap<QString, QVariantMap> rules;  // 选择器 -> 属性映射
        QDateTime timestamp;
    };
    
    static ParsedStyle getParsedStyle(const QString &qss) {
        static QMap<QString, ParsedStyle> cache;
        
        // 使用QSS内容的哈希作为键
        QString hash = QCryptographicHash::hash(qss.toUtf8(), 
                                                QCryptographicHash::Md5).toHex();
        
        if (!cache.contains(hash)) {
            ParsedStyle parsed = parseStyleSheet(qss);
            cache[hash] = parsed;
        }
        
        return cache.value(hash);
    }
    
private:
    static ParsedStyle parseStyleSheet(const QString &qss) {
        ParsedStyle parsed;
        parsed.originalQSS = qss;
        parsed.timestamp = QDateTime::currentDateTime();
        // 解析QSS...
        return parsed;
    }
};

缓存失效策略

cpp 复制代码
class StyleCacheManager {
public:
    static void setStyleSheet(const QString &key, const QString &qss) {
        m_cache[key] = qss;
        m_timestamps[key] = QDateTime::currentDateTime();
    }
    
    static QString getStyleSheet(const QString &key) {
        if (m_cache.contains(key)) {
            // 检查是否过期(例如1小时)
            QDateTime timestamp = m_timestamps.value(key);
            if (timestamp.secsTo(QDateTime::currentDateTime()) < 3600) {
                return m_cache.value(key);
            } else {
                // 缓存过期,清除
                m_cache.remove(key);
                m_timestamps.remove(key);
            }
        }
        return QString();
    }
    
    static void clearExpiredCache() {
        QDateTime now = QDateTime::currentDateTime();
        QStringList keys = m_timestamps.keys();
        for (const QString &key : keys) {
            if (m_timestamps.value(key).secsTo(now) >= 3600) {
                m_cache.remove(key);
                m_timestamps.remove(key);
            }
        }
    }
    
private:
    static QMap<QString, QString> m_cache;
    static QMap<QString, QDateTime> m_timestamps;
};
避免复杂选择器

选择器复杂度对比

cpp 复制代码
// ❌ 复杂选择器(性能差)
qApp->setStyleSheet(
    "QMainWindow QWidget QVBoxLayout QHBoxLayout QPushButton:hover:!disabled {"
    "    background-color: blue;"
    "}"
);
// 需要遍历多层对象树,匹配多个条件

// ✅ 简单选择器(性能好)
qApp->setStyleSheet(
    "QPushButton:hover {"
    "    background-color: blue;"
    "}"
);
// 直接匹配,性能更好

选择器性能优化

  1. 使用ID选择器:ID选择器匹配最快

    cpp 复制代码
    // ✅ 使用ID选择器
    "#myButton { background-color: blue; }"
  2. 避免深层嵌套:减少选择器深度

    cpp 复制代码
    // ❌ 深层嵌套
    "QMainWindow QWidget QWidget QPushButton { ... }"
    
    // ✅ 扁平化
    "QPushButton { ... }"
  3. 使用类选择器:类选择器比类型选择器更具体

    cpp 复制代码
    // ✅ 使用类选择器
    ".primary-button { background-color: blue; }"
  4. 避免通用选择器:通用选择器匹配所有控件

    cpp 复制代码
    // ❌ 通用选择器(性能差)
    "* { color: black; }"
    
    // ✅ 具体选择器(性能好)
    "QPushButton { color: black; }"

选择器性能测试

cpp 复制代码
void testSelectorPerformance() {
    QElapsedTimer timer;
    
    // 测试复杂选择器
    timer.start();
    qApp->setStyleSheet("QMainWindow QWidget QPushButton { background-color: blue; }");
    qint64 complexTime = timer.elapsed();
    
    // 测试简单选择器
    timer.restart();
    qApp->setStyleSheet("QPushButton { background-color: blue; }");
    qint64 simpleTime = timer.elapsed();
    
    qDebug() << "复杂选择器耗时:" << complexTime << "毫秒";
    qDebug() << "简单选择器耗时:" << simpleTime << "毫秒";
}
QSS的最小化

移除注释和空白

cpp 复制代码
QString minimizeQSS(const QString &qss) {
    QString result = qss;
    
    // 移除单行注释
    QRegularExpression commentRegex(R"(//.*)");
    result.remove(commentRegex);
    
    // 移除多行注释
    QRegularExpression multiCommentRegex(R"(/\*.*?\*/)");
    result.remove(multiCommentRegex);
    
    // 移除多余空白
    result = result.simplified();
    
    // 移除换行(可选)
    result.replace('\n', ' ');
    result.replace('\r', ' ');
    
    // 压缩空格
    result.replace(QRegularExpression(R"(\s+)"), " ");
    
    return result;
}

压缩QSS文件

cpp 复制代码
QString compressQSS(const QString &qss) {
    QString result = qss;
    
    // 移除注释
    result = removeComments(result);
    
    // 移除空白
    result = removeWhitespace(result);
    
    // 优化属性值
    result = optimizePropertyValues(result);
    
    return result;
}

QString optimizePropertyValues(const QString &qss) {
    QString result = qss;
    
    // 简化颜色值
    result.replace("#ffffff", "white");
    result.replace("#000000", "black");
    result.replace("#ff0000", "red");
    
    // 简化尺寸值
    result.replace("0px", "0");
    result.replace(" 0px", " 0");
    
    return result;
}

使用工具压缩

cpp 复制代码
// 开发时使用完整QSS,发布时使用压缩版本
#ifdef QT_DEBUG
    QString styleSheet = loadStyleSheet("style.qss");
#else
    QString styleSheet = loadStyleSheet("style.min.qss");  // 压缩版本
#endif

qApp->setStyleSheet(styleSheet);

9.2 绘制性能优化

绘制性能直接影响应用程序的流畅度。优化绘制性能可以显著提升用户体验。

减少重绘次数

使用update()而不是repaint()

cpp 复制代码
// ❌ repaint()立即重绘(阻塞)
widget->repaint();

// ✅ update()延迟重绘(非阻塞,合并多次更新)
widget->update();

批量更新

cpp 复制代码
// ❌ 多次更新(性能差)
for (int i = 0; i < 100; i++) {
    buttons[i]->setStyleSheet("background-color: blue;");
    buttons[i]->update();
}

// ✅ 批量更新(性能好)
for (int i = 0; i < 100; i++) {
    buttons[i]->setStyleSheet("background-color: blue;");
}
// 只更新一次
QApplication::processEvents();

使用setUpdatesEnabled()

cpp 复制代码
// 暂停更新
widget->setUpdatesEnabled(false);

// 批量修改
for (QWidget *child : widget->findChildren<QWidget*>()) {
    child->setStyleSheet("...");
}

// 恢复更新
widget->setUpdatesEnabled(true);
widget->update();

区域更新

cpp 复制代码
// 只更新需要重绘的区域
void updateRegion(const QRect &region) {
    widget->update(region);  // 只更新指定区域
}
使用缓存绘制

缓存绘制结果

cpp 复制代码
class CachedWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        if (m_cache.isNull() || m_cache.size() != size()) {
            // 重新生成缓存
            m_cache = QPixmap(size());
            m_cache.fill(Qt::transparent);
            
            QPainter painter(&m_cache);
            drawContent(&painter);
        }
        
        // 使用缓存绘制
        QPainter painter(this);
        painter.drawPixmap(0, 0, m_cache);
    }
    
private:
    void drawContent(QPainter *painter) {
        // 绘制内容
        // ...
    }
    
    QPixmap m_cache;
};

条件缓存

cpp 复制代码
class SmartCachedWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        // 检查是否需要重新生成缓存
        if (needsCacheUpdate()) {
            updateCache();
        }
        
        // 使用缓存绘制
        QPainter painter(this);
        painter.drawPixmap(0, 0, m_cache);
    }
    
private:
    bool needsCacheUpdate() const {
        return m_cache.isNull() || 
               m_cache.size() != size() ||
               m_dirty;
    }
    
    void updateCache() {
        m_cache = QPixmap(size());
        m_cache.fill(Qt::transparent);
        
        QPainter painter(&m_cache);
        drawContent(&painter);
        
        m_dirty = false;
    }
    
    QPixmap m_cache;
    bool m_dirty = true;
};

部分缓存

cpp 复制代码
class PartialCachedWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        
        // 只缓存不变的部分
        if (m_backgroundCache.isNull()) {
            m_backgroundCache = QPixmap(size());
            QPainter bgPainter(&m_backgroundCache);
            drawBackground(&bgPainter);
        }
        
        painter.drawPixmap(0, 0, m_backgroundCache);
        
        // 动态部分直接绘制
        drawDynamicContent(&painter);
    }
    
private:
    QPixmap m_backgroundCache;
};
避免不必要的样式计算

缓存样式选项

cpp 复制代码
class OptimizedButton : public QPushButton {
protected:
    void paintEvent(QPaintEvent *event) override {
        // 缓存样式选项
        if (m_optionDirty) {
            initStyleOption(&m_cachedOption);
            m_optionDirty = false;
        }
        
        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);
    }
    
private:
    QStyleOptionButton m_cachedOption;
    bool m_optionDirty = true;
};

避免重复计算

cpp 复制代码
// ❌ 每次绘制都计算(性能差)
void paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    QColor color = calculateColor();  // 每次都计算
    painter.fillRect(rect(), color);
}

// ✅ 缓存计算结果(性能好)
void paintEvent(QPaintEvent *event) {
    if (m_colorDirty) {
        m_cachedColor = calculateColor();
        m_colorDirty = false;
    }
    
    QPainter painter(this);
    painter.fillRect(rect(), m_cachedColor);
}

延迟计算

cpp 复制代码
class LazyStyleCalculator {
public:
    QColor getColor() {
        if (!m_colorCalculated) {
            m_cachedColor = expensiveColorCalculation();
            m_colorCalculated = true;
        }
        return m_cachedColor;
    }
    
    void invalidate() {
        m_colorCalculated = false;
    }
    
private:
    QColor m_cachedColor;
    bool m_colorCalculated = false;
    
    QColor expensiveColorCalculation() {
        // 复杂的颜色计算
        // ...
        return QColor();
    }
};
使用硬件加速

启用OpenGL渲染

cpp 复制代码
// 在main函数中
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    // 启用OpenGL渲染
    QSurfaceFormat format;
    format.setRenderableType(QSurfaceFormat::OpenGL);
    QSurfaceFormat::setDefaultFormat(format);
    
    // 使用QOpenGLWidget
    // ...
}

使用QGraphicsView

cpp 复制代码
// QGraphicsView使用硬件加速
QGraphicsView *view = new QGraphicsView();
view->setViewport(new QOpenGLWidget());  // 使用OpenGL视口

优化绘制路径

cpp 复制代码
void optimizedPaintEvent(QPaintEvent *event) {
    QPainter painter(this);
    
    // 启用抗锯齿(如果不需要可以关闭以提高性能)
    painter.setRenderHint(QPainter::Antialiasing, false);
    
    // 使用更快的绘制方法
    painter.fillRect(rect(), Qt::blue);  // 比drawRect快
    
    // 批量绘制
    QVector<QRect> rects = getRectsToDraw();
    for (const QRect &rect : rects) {
        painter.fillRect(rect, Qt::red);
    }
}

9.3 样式资源优化

图片资源的优化

图片格式选择

cpp 复制代码
// PNG:适合图标、透明图片
QPixmap icon = QPixmap("icon.png");

// JPEG:适合照片(文件小,但无透明)
QPixmap photo = QPixmap("photo.jpg");

// SVG:矢量图,可无损缩放(适合高DPI)
QSvgRenderer renderer("icon.svg");

图片压缩

cpp 复制代码
QPixmap compressPixmap(const QPixmap &pixmap, int quality) {
    QByteArray data;
    QBuffer buffer(&data);
    buffer.open(QIODevice::WriteOnly);
    
    // 压缩为PNG
    pixmap.save(&buffer, "PNG", quality);
    
    // 重新加载
    QPixmap compressed;
    compressed.loadFromData(data);
    return compressed;
}

图片缓存

cpp 复制代码
class ImageCache {
public:
    static QPixmap getPixmap(const QString &path, const QSize &size) {
        QString key = path + "_" + QString::number(size.width()) + "x" + QString::number(size.height());
        
        if (!m_cache.contains(key)) {
            QPixmap pixmap(path);
            if (!size.isNull()) {
                pixmap = pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
            }
            m_cache[key] = pixmap;
        }
        
        return m_cache.value(key);
    }
    
    static void clearCache() {
        m_cache.clear();
    }
    
private:
    static QMap<QString, QPixmap> m_cache;
};

按需加载图片

cpp 复制代码
class LazyImageLoader {
public:
    QPixmap getPixmap(const QString &path) {
        if (!m_loadedPixmaps.contains(path)) {
            // 延迟加载
            QPixmap pixmap(path);
            m_loadedPixmaps[path] = pixmap;
        }
        return m_loadedPixmaps.value(path);
    }
    
    void preload(const QStringList &paths) {
        // 预加载常用图片
        for (const QString &path : paths) {
            getPixmap(path);
        }
    }
    
private:
    QMap<QString, QPixmap> m_loadedPixmaps;
};
字体资源的优化

字体子集化

cpp 复制代码
// 只加载需要的字符
int fontId = QFontDatabase::addApplicationFont("font.ttf");
QStringList families = QFontDatabase::applicationFontFamilies(fontId);
if (!families.isEmpty()) {
    QFont font(families.at(0));
    // 使用字体
}

字体缓存

cpp 复制代码
class FontCache {
public:
    static QFont getFont(const QString &family, int size) {
        QString key = family + "_" + QString::number(size);
        
        if (!m_cache.contains(key)) {
            QFont font(family, size);
            m_cache[key] = font;
        }
        
        return m_cache.value(key);
    }
    
private:
    static QMap<QString, QFont> m_cache;
};

字体预加载

cpp 复制代码
void preloadFonts() {
    // 预加载常用字体
    QStringList fonts = {
        ":/fonts/main.ttf",
        ":/fonts/bold.ttf",
        ":/fonts/italic.ttf"
    };
    
    for (const QString &fontPath : fonts) {
        QFontDatabase::addApplicationFont(fontPath);
    }
}
颜色查找表优化

颜色缓存

cpp 复制代码
class ColorCache {
public:
    static QColor getColor(const QString &name) {
        static QMap<QString, QColor> cache;
        
        if (!cache.contains(name)) {
            cache[name] = QColor(name);
        }
        
        return cache.value(name);
    }
    
    static QColor getColor(int r, int g, int b) {
        QString key = QString("%1,%2,%3").arg(r).arg(g).arg(b);
        static QMap<QString, QColor> cache;
        
        if (!cache.contains(key)) {
            cache[key] = QColor(r, g, b);
        }
        
        return cache.value(key);
    }
};

调色板缓存

cpp 复制代码
class PaletteCache {
public:
    static QPalette getPalette(const QString &theme) {
        static QMap<QString, QPalette> cache;
        
        if (!cache.contains(theme)) {
            QPalette palette = createPalette(theme);
            cache[theme] = palette;
        }
        
        return cache.value(theme);
    }
    
private:
    static QPalette createPalette(const QString &theme) {
        // 创建调色板
        // ...
        return QPalette();
    }
};
资源的延迟加载

按需加载资源

cpp 复制代码
class LazyResourceLoader {
public:
    static QPixmap getPixmap(const QString &path) {
        if (!m_loadedResources.contains(path)) {
            // 延迟加载
            QPixmap pixmap(path);
            m_loadedResources[path] = pixmap;
        }
        return m_loadedResources.value(path);
    }
    
    static void preload(const QStringList &paths) {
        // 在后台线程预加载
        QtConcurrent::run([paths]() {
            for (const QString &path : paths) {
                getPixmap(path);
            }
        });
    }
    
private:
    static QMap<QString, QPixmap> m_loadedResources;
};

资源预加载策略

cpp 复制代码
class ResourcePreloader {
public:
    void preloadCriticalResources() {
        // 预加载关键资源(主界面需要的)
        QStringList critical = {
            ":/icons/main.png",
            ":/images/background.png"
        };
        
        for (const QString &path : critical) {
            QPixmap::fromImage(QImage(path));
        }
    }
    
    void preloadBackgroundResources() {
        // 在后台预加载其他资源
        QtConcurrent::run([this]() {
            QStringList background = {
                ":/icons/icon1.png",
                ":/icons/icon2.png",
                // ...
            };
            
            for (const QString &path : background) {
                QPixmap::fromImage(QImage(path));
            }
        });
    }
};

9.4 样式调试与性能分析

样式调试工具

QSS调试器

cpp 复制代码
class QSSDebugger {
public:
    static void debugStyleSheet(const QString &qss) {
        // 解析QSS并显示规则
        QTextStream stream(qss.toUtf8());
        QString line;
        int lineNumber = 0;
        
        while (stream.readLineInto(&line)) {
            lineNumber++;
            if (line.contains("{")) {
                qDebug() << "规则开始,行" << lineNumber << ":" << line;
            }
            if (line.contains("}")) {
                qDebug() << "规则结束,行" << lineNumber;
            }
        }
    }
    
    static void debugWidgetStyle(QWidget *widget) {
        qDebug() << "控件:" << widget->metaObject()->className();
        qDebug() << "样式表:" << widget->styleSheet();
        qDebug() << "调色板:" << widget->palette();
        qDebug() << "字体:" << widget->font();
    }
};

样式继承查看器

cpp 复制代码
class StyleInheritanceViewer {
public:
    static void printInheritanceChain(QWidget *widget) {
        QWidget *current = widget;
        int level = 0;
        
        while (current) {
            QString indent = QString("  ").repeated(level);
            qDebug() << indent << "级别" << level << ":"
                     << current->metaObject()->className()
                     << "样式表:" << (current->styleSheet().isEmpty() ? "无" : "有");
            
            current = current->parentWidget();
            level++;
        }
    }
};

实时样式编辑器

cpp 复制代码
class LiveStyleEditor : public QDialog {
    Q_OBJECT
    
public:
    LiveStyleEditor(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("实时样式编辑器");
        resize(600, 400);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        
        // 样式表编辑器
        m_editor = new QTextEdit(this);
        m_editor->setPlainText(qApp->styleSheet());
        layout->addWidget(m_editor);
        
        // 应用按钮
        QPushButton *applyBtn = new QPushButton("应用", this);
        connect(applyBtn, &QPushButton::clicked, [this]() {
            qApp->setStyleSheet(m_editor->toPlainText());
        });
        layout->addWidget(applyBtn);
    }
    
private:
    QTextEdit *m_editor;
};
性能分析方法

QSS解析时间测量

cpp 复制代码
void measureQSSParseTime(const QString &qss) {
    QElapsedTimer timer;
    timer.start();
    
    qApp->setStyleSheet(qss);
    
    qint64 elapsed = timer.elapsed();
    qDebug() << "QSS解析耗时:" << elapsed << "毫秒";
}

绘制时间测量

cpp 复制代码
class PerformanceMeasuredWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QElapsedTimer timer;
        timer.start();
        
        // 绘制
        QPainter painter(this);
        // ... 绘制代码
        
        qint64 elapsed = timer.elapsed();
        if (elapsed > 16) {  // 超过一帧时间(60fps)
            qWarning() << "绘制耗时过长:" << elapsed << "毫秒";
        }
    }
};

内存使用分析

cpp 复制代码
void analyzeMemoryUsage() {
    // 分析QSS内存占用
    QString qss = qApp->styleSheet();
    qDebug() << "QSS大小:" << qss.size() << "字节";
    
    // 分析图片缓存内存
    // ...
}
常见性能瓶颈

瓶颈1:大型QSS文件

cpp 复制代码
// 问题:大型QSS文件解析慢
// 解决:拆分文件,按需加载
QStringList modules = {"base", "buttons", "inputs"};
QString combinedQSS;
for (const QString &module : modules) {
    combinedQSS += loadStyleModule(module);
}
qApp->setStyleSheet(combinedQSS);

瓶颈2:复杂选择器

cpp 复制代码
// 问题:复杂选择器匹配慢
// 解决:简化选择器,使用ID或类选择器
// ❌ 复杂
"QMainWindow QWidget QVBoxLayout QPushButton { ... }"
// ✅ 简单
"#myButton { ... }"

瓶颈3:频繁重绘

cpp 复制代码
// 问题:频繁调用update()
// 解决:批量更新,使用定时器合并
class BatchedUpdater {
public:
    void scheduleUpdate(QWidget *widget) {
        m_pendingUpdates.insert(widget);
        
        if (!m_timer.isActive()) {
            m_timer.singleShot(16, this, &BatchedUpdater::processUpdates);  // 一帧后更新
        }
    }
    
private slots:
    void processUpdates() {
        for (QWidget *widget : m_pendingUpdates) {
            widget->update();
        }
        m_pendingUpdates.clear();
    }
    
    QSet<QWidget*> m_pendingUpdates;
    QTimer m_timer;
};

瓶颈4:未缓存的资源

cpp 复制代码
// 问题:每次都加载图片
// 解决:使用缓存
QPixmap getCachedPixmap(const QString &path) {
    static QMap<QString, QPixmap> cache;
    if (!cache.contains(path)) {
        cache[path] = QPixmap(path);
    }
    return cache.value(path);
}
性能优化案例

案例1:优化大型界面样式切换

cpp 复制代码
// 优化前:直接切换,界面卡顿
void switchTheme(const QString &theme) {
    qApp->setStyleSheet(loadStyleSheet(theme));  // 卡顿
}

// 优化后:渐进式切换
void switchThemeOptimized(const QString &theme) {
    // 1. 暂停更新
    QList<QWidget*> widgets = qApp->allWidgets();
    for (QWidget *widget : widgets) {
        widget->setUpdatesEnabled(false);
    }
    
    // 2. 切换样式
    qApp->setStyleSheet(loadStyleSheet(theme));
    
    // 3. 恢复更新
    for (QWidget *widget : widgets) {
        widget->setUpdatesEnabled(true);
        widget->update();
    }
}

案例2:优化列表视图样式

cpp 复制代码
// 优化前:每个项都应用样式
void applyStyleToItems() {
    for (int i = 0; i < 1000; i++) {
        QListWidgetItem *item = listWidget->item(i);
        item->setBackground(QBrush(QColor(240, 240, 240)));
    }
}

// 优化后:使用代理样式
class OptimizedListStyle : public QProxyStyle {
    void drawControl(ControlElement element,
                     const QStyleOption *opt,
                     QPainter *p,
                     const QWidget *w) const override {
        if (element == CE_ItemViewItem) {
            // 统一绘制逻辑
            // ...
        } else {
            QProxyStyle::drawControl(element, opt, p, w);
        }
    }
};

案例3:优化动态样式更新

cpp 复制代码
// 优化前:每次属性变化都更新样式
void setProperty(const QString &name, const QVariant &value) {
    QWidget::setProperty(name.toUtf8().constData(), value);
    style()->unpolish(this);
    style()->polish(this);
    update();
}

// 优化后:批量更新
class BatchedStyleUpdater {
public:
    void setProperty(QWidget *widget, const QString &name, const QVariant &value) {
        widget->setProperty(name.toUtf8().constData(), value);
        m_pendingUpdates.insert(widget);
        
        if (!m_timer.isActive()) {
            m_timer.singleShot(0, this, &BatchedStyleUpdater::processUpdates);
        }
    }
    
private slots:
    void processUpdates() {
        for (QWidget *widget : m_pendingUpdates) {
            widget->style()->unpolish(widget);
            widget->style()->polish(widget);
            widget->update();
        }
        m_pendingUpdates.clear();
    }
    
    QSet<QWidget*> m_pendingUpdates;
    QTimer m_timer;
};


10. 实际应用场景与最佳实践

10.1 常见控件的样式定制

本节介绍常见控件的样式定制方法,提供实用的样式示例。

QPushButton样式定制

基础按钮样式

css 复制代码
QPushButton {
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 5px;
    font-size: 14px;
    font-weight: bold;
}

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

QPushButton:pressed {
    background-color: #3d8b40;
}

QPushButton:disabled {
    background-color: #cccccc;
    color: #999999;
}

扁平按钮

css 复制代码
QPushButton {
    background-color: transparent;
    color: #4CAF50;
    border: 2px solid #4CAF50;
    padding: 8px 16px;
    border-radius: 4px;
}

QPushButton:hover {
    background-color: #4CAF50;
    color: white;
}

图标按钮

css 复制代码
QPushButton {
    background-color: #2196F3;
    color: white;
    border: none;
    padding: 8px;
    border-radius: 4px;
    text-align: left;
    padding-left: 40px;  /* 为图标留空间 */
}

QPushButton::icon {
    left: 8px;  /* 图标位置 */
}

按钮组样式

css 复制代码
/* 主要按钮 */
QPushButton[class="primary"] {
    background-color: #4CAF50;
    color: white;
}

/* 次要按钮 */
QPushButton[class="secondary"] {
    background-color: #2196F3;
    color: white;
}

/* 危险按钮 */
QPushButton[class="danger"] {
    background-color: #f44336;
    color: white;
}
QLineEdit样式定制

基础输入框样式

css 复制代码
QLineEdit {
    background-color: white;
    color: #333333;
    border: 1px solid #cccccc;
    padding: 8px;
    border-radius: 4px;
    font-size: 14px;
}

QLineEdit:focus {
    border: 2px solid #2196F3;
    background-color: #f5f5f5;
}

QLineEdit:disabled {
    background-color: #f0f0f0;
    color: #999999;
}

搜索框样式

css 复制代码
QLineEdit[class="search"] {
    background-color: #f5f5f5;
    border: 1px solid #e0e0e0;
    border-radius: 20px;
    padding: 8px 40px 8px 16px;
}

QLineEdit[class="search"]:focus {
    background-color: white;
    border: 2px solid #2196F3;
}

带图标的输入框

css 复制代码
QLineEdit {
    padding-left: 30px;  /* 为图标留空间 */
    background-image: url(:/icons/search.png);
    background-repeat: no-repeat;
    background-position: left center;
    padding-left: 30px;
}
QComboBox样式定制

基础组合框样式

css 复制代码
QComboBox {
    background-color: white;
    color: #333333;
    border: 1px solid #cccccc;
    padding: 5px 30px 5px 10px;
    border-radius: 4px;
}

QComboBox:hover {
    border: 1px solid #2196F3;
}

QComboBox::drop-down {
    border: none;
    width: 30px;
    background-color: #f5f5f5;
    border-top-right-radius: 4px;
    border-bottom-right-radius: 4px;
}

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

QComboBox QAbstractItemView {
    background-color: white;
    border: 1px solid #cccccc;
    selection-background-color: #2196F3;
    selection-color: white;
}

下拉列表样式

css 复制代码
QComboBox::drop-down:hover {
    background-color: #e0e0e0;
}

QComboBox QAbstractItemView::item {
    padding: 5px;
    min-height: 25px;
}

QComboBox QAbstractItemView::item:hover {
    background-color: #f0f0f0;
}

QComboBox QAbstractItemView::item:selected {
    background-color: #2196F3;
    color: white;
}
QTableView/QTreeView样式定制

表格视图样式

css 复制代码
QTableView {
    background-color: white;
    alternate-background-color: #f5f5f5;
    gridline-color: #e0e0e0;
    border: 1px solid #cccccc;
}

QTableView::item {
    padding: 5px;
    border: none;
}

QTableView::item:selected {
    background-color: #2196F3;
    color: white;
}

QTableView::item:hover {
    background-color: #f0f0f0;
}

QHeaderView::section {
    background-color: #f5f5f5;
    color: #333333;
    padding: 8px;
    border: 1px solid #e0e0e0;
    font-weight: bold;
}

QHeaderView::section:hover {
    background-color: #e0e0e0;
}

树视图样式

css 复制代码
QTreeView {
    background-color: white;
    alternate-background-color: #f5f5f5;
    border: 1px solid #cccccc;
}

QTreeView::item {
    padding: 5px;
    border: none;
}

QTreeView::item:selected {
    background-color: #2196F3;
    color: white;
}

QTreeView::item:hover {
    background-color: #f0f0f0;
}

QTreeView::branch {
    background-color: white;
}

QTreeView::branch:has-siblings:!adjoins-item {
    border-image: url(:/icons/branch-line.png) 0;
}

QTreeView::branch:has-siblings:adjoins-item {
    border-image: url(:/icons/branch-more.png) 0;
}

QTreeView::branch:!has-children:!has-siblings:adjoins-item {
    border-image: url(:/icons/branch-end.png) 0;
}

QTreeView::branch:has-children:!closed:adjoins-item {
    border-image: url(:/icons/branch-closed.png) 0;
}

QTreeView::branch:closed:has-children:has-siblings {
    border-image: url(:/icons/branch-closed.png) 0;
}
QScrollBar样式定制

垂直滚动条

css 复制代码
QScrollBar:vertical {
    background-color: #f0f0f0;
    width: 12px;
    border: none;
}

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

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

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

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

QScrollBar::add-page:vertical,
QScrollBar::sub-page:vertical {
    background-color: transparent;
}

水平滚动条

css 复制代码
QScrollBar:horizontal {
    background-color: #f0f0f0;
    height: 12px;
    border: none;
}

QScrollBar::handle:horizontal {
    background-color: #cccccc;
    min-width: 20px;
    border-radius: 6px;
    margin: 2px;
}

QScrollBar::handle:horizontal:hover {
    background-color: #999999;
}
QTabWidget样式定制

标签页控件样式

css 复制代码
QTabWidget::pane {
    border: 1px solid #cccccc;
    background-color: white;
    top: -1px;
}

QTabBar::tab {
    background-color: #f5f5f5;
    color: #333333;
    padding: 8px 16px;
    border: 1px solid #cccccc;
    border-bottom: none;
    margin-right: 2px;
}

QTabBar::tab:selected {
    background-color: white;
    color: #2196F3;
    border-bottom: 2px solid #2196F3;
}

QTabBar::tab:hover {
    background-color: #e0e0e0;
}

QTabBar::tab:!selected {
    margin-top: 2px;
}

圆角标签页

css 复制代码
QTabBar::tab {
    background-color: #f5f5f5;
    color: #333333;
    padding: 8px 20px;
    border-top-left-radius: 8px;
    border-top-right-radius: 8px;
    border: 1px solid #cccccc;
    border-bottom: none;
}

QTabBar::tab:selected {
    background-color: white;
    color: #2196F3;
    border-bottom: 2px solid #2196F3;
}
QMenu/QMenuBar样式定制

菜单栏样式

css 复制代码
QMenuBar {
    background-color: #f5f5f5;
    color: #333333;
    border-bottom: 1px solid #e0e0e0;
    padding: 2px;
}

QMenuBar::item {
    padding: 8px 12px;
    border-radius: 4px;
}

QMenuBar::item:selected {
    background-color: #e0e0e0;
}

QMenuBar::item:pressed {
    background-color: #cccccc;
}

菜单样式

css 复制代码
QMenu {
    background-color: white;
    color: #333333;
    border: 1px solid #cccccc;
    padding: 4px;
}

QMenu::item {
    padding: 8px 30px 8px 20px;
    border-radius: 4px;
}

QMenu::item:selected {
    background-color: #2196F3;
    color: white;
}

QMenu::item:disabled {
    color: #999999;
}

QMenu::separator {
    height: 1px;
    background-color: #e0e0e0;
    margin: 4px 0px;
}

QMenu::indicator {
    width: 16px;
    height: 16px;
}

QMenu::indicator:checked {
    image: url(:/icons/check.png);
}

上下文菜单样式

css 复制代码
QMenu[class="context"] {
    background-color: #2b2b2b;
    color: #e0e0e0;
    border: 1px solid #555555;
}

QMenu[class="context"]::item:selected {
    background-color: #3c3c3c;
}
QDialog样式定制

对话框样式

css 复制代码
QDialog {
    background-color: white;
}

QDialog QLabel {
    color: #333333;
    font-size: 14px;
}

QDialog QPushButton {
    min-width: 80px;
    min-height: 30px;
    padding: 8px 16px;
}

QDialogButtonBox {
    border-top: 1px solid #e0e0e0;
    padding-top: 10px;
}

模态对话框样式

css 复制代码
QDialog[class="modal"] {
    background-color: white;
    border: 1px solid #cccccc;
    border-radius: 8px;
}

QDialog[class="modal"] QLabel#titleLabel {
    font-size: 18px;
    font-weight: bold;
    color: #333333;
    padding: 10px;
}

10.2 复杂界面的样式设计

现代扁平化设计

扁平化设计特点

  • 无阴影、无渐变、无3D效果
  • 简洁的配色方案
  • 清晰的层次结构
  • 大面积的留白

扁平化样式示例

css 复制代码
/* 扁平化主题 */
QWidget {
    background-color: #ffffff;
    color: #333333;
}

QPushButton {
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 12px 24px;
    border-radius: 0px;  /* 无圆角 */
    font-size: 14px;
    font-weight: 500;
}

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

QLineEdit {
    background-color: #f5f5f5;
    border: 2px solid transparent;
    padding: 10px;
    border-radius: 0px;
}

QLineEdit:focus {
    background-color: white;
    border: 2px solid #4CAF50;
}
Material Design风格

Material Design特点

  • 卡片式设计
  • 阴影效果(elevation)
  • 动画过渡
  • 明确的颜色系统

Material Design样式示例

css 复制代码
/* Material Design主题 */
QWidget {
    background-color: #fafafa;
    color: #212121;
}

QPushButton {
    background-color: #2196F3;
    color: white;
    border: none;
    padding: 12px 24px;
    border-radius: 4px;
    font-size: 14px;
    font-weight: 500;
    min-height: 36px;
}

QPushButton:hover {
    background-color: #1976D2;
}

QPushButton:pressed {
    background-color: #0D47A1;
}

/* 卡片样式 */
QWidget[class="card"] {
    background-color: white;
    border: none;
    border-radius: 4px;
    /* 阴影效果通过代码实现 */
}

QLineEdit {
    background-color: transparent;
    border: none;
    border-bottom: 2px solid #e0e0e0;
    padding: 8px 0px;
}

QLineEdit:focus {
    border-bottom: 2px solid #2196F3;
}
仿macOS风格

macOS风格特点

  • 毛玻璃效果(blur)
  • 圆角设计
  • 柔和的阴影
  • 简洁的配色

macOS风格样式示例

css 复制代码
/* macOS风格主题 */
QWidget {
    background-color: #f5f5f7;
    color: #1d1d1f;
}

QPushButton {
    background-color: #007aff;
    color: white;
    border: none;
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 13px;
}

QPushButton:hover {
    background-color: #0051d5;
}

QLineEdit {
    background-color: white;
    border: 1px solid #d2d2d7;
    padding: 8px 12px;
    border-radius: 6px;
}

QLineEdit:focus {
    border: 1px solid #007aff;
    box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.1);
}
仿Windows 11风格

Windows 11风格特点

  • Fluent Design设计语言
  • 圆角设计
  • 柔和的阴影和模糊
  • 现代化的配色

Windows 11风格样式示例

css 复制代码
/* Windows 11风格主题 */
QWidget {
    background-color: #f3f3f3;
    color: #202020;
}

QPushButton {
    background-color: #0078d4;
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    font-size: 14px;
}

QPushButton:hover {
    background-color: #106ebe;
}

QPushButton:pressed {
    background-color: #005a9e;
}

QLineEdit {
    background-color: white;
    border: 1px solid #d2d2d2;
    padding: 8px 12px;
    border-radius: 4px;
}

QLineEdit:focus {
    border: 2px solid #0078d4;
}
游戏UI风格

游戏UI特点

  • 炫酷的视觉效果
  • 渐变和发光效果
  • 动态元素
  • 高对比度

游戏UI样式示例

css 复制代码
/* 游戏UI主题 */
QWidget {
    background-color: #1a1a1a;
    color: #ffffff;
}

QPushButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 #ff6b6b, stop:1 #ee5a6f);
    color: white;
    border: 2px solid #ff4757;
    padding: 12px 24px;
    border-radius: 8px;
    font-size: 16px;
    font-weight: bold;
}

QPushButton:hover {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 #ff5252, stop:1 #ff1744);
    border: 2px solid #ff1744;
}

QPushButton:pressed {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
                                stop:0 #d32f2f, stop:1 #c62828);
}

QLineEdit {
    background-color: #2d2d2d;
    color: #ffffff;
    border: 2px solid #4a4a4a;
    padding: 10px;
    border-radius: 6px;
}

QLineEdit:focus {
    border: 2px solid #ff6b6b;
    background-color: #333333;
}

10.3 样式系统最佳实践

样式代码的组织

按功能组织

复制代码
styles/
├── base/
│   ├── colors.qss        # 颜色定义
│   ├── fonts.qss         # 字体定义
│   └── variables.qss     # 变量定义
├── components/
│   ├── buttons.qss       # 按钮样式
│   ├── inputs.qss        # 输入框样式
│   ├── menus.qss         # 菜单样式
│   └── dialogs.qss       # 对话框样式
├── layouts/
│   ├── sidebar.qss       # 侧边栏样式
│   ├── toolbar.qss       # 工具栏样式
│   └── statusbar.qss     # 状态栏样式
└── themes/
    ├── light.qss          # 亮色主题
    └── dark.qss           # 暗色主题

按模块组织

复制代码
styles/
├── module1/
│   ├── style.qss
│   └── resources/
├── module2/
│   ├── style.qss
│   └── resources/
└── shared/
    ├── common.qss
    └── variables.qss
样式的命名规范

选择器命名

css 复制代码
/* ✅ 好的命名 */
QPushButton[class="primary-button"] { ... }
QPushButton[class="secondary-button"] { ... }
QWidget[class="card-container"] { ... }

/* ❌ 不好的命名 */
QPushButton[class="btn1"] { ... }
QPushButton[class="button"] { ... }
QWidget[class="container"] { ... }

ID命名

cpp 复制代码
// ✅ 好的命名
button->setObjectName("loginButton");
label->setObjectName("titleLabel");
edit->setObjectName("usernameEdit");

// ❌ 不好的命名
button->setObjectName("btn1");
label->setObjectName("lbl1");
edit->setObjectName("edit1");

类命名

cpp 复制代码
// ✅ 好的命名
button->setProperty("class", "primary-button large-button");
widget->setProperty("class", "card-container");

// ❌ 不好的命名
button->setProperty("class", "btn primary");
widget->setProperty("class", "container");
样式的复用策略

使用样式类

css 复制代码
/* 定义可复用的样式类 */
.Button {
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
    font-size: 14px;
}

.Button-Primary {
    background-color: #4CAF50;
    color: white;
}

.Button-Secondary {
    background-color: #2196F3;
    color: white;
}

.Button-Large {
    padding: 15px 30px;
    font-size: 16px;
}

使用变量(通过属性)

cpp 复制代码
// 定义颜色变量
qApp->setProperty("primaryColor", "#4CAF50");
qApp->setProperty("secondaryColor", "#2196F3");

// 在QSS中使用(需要自定义处理)
// 或使用配置文件

使用继承

css 复制代码
/* 基础样式 */
QPushButton {
    padding: 10px 20px;
    border-radius: 5px;
}

/* 继承并扩展 */
QPushButton[class="primary"] {
    background-color: #4CAF50;
    color: white;
}
样式的版本控制

版本标记

css 复制代码
/* style.qss v1.0.0 */
/* 作者:Developer */
/* 日期:2024-01-01 */

QPushButton {
    background-color: #4CAF50;
}

变更日志

markdown 复制代码
# 样式变更日志

## v1.0.0 (2024-01-01)
- 初始版本
- 基础按钮样式

## v1.1.0 (2024-02-01)
- 添加输入框样式
- 优化按钮悬停效果

## v1.2.0 (2024-03-01)
- 添加暗色主题支持
- 修复样式冲突问题

Git管理

bash 复制代码
# 样式文件版本控制
git add styles/
git commit -m "更新样式:添加暗色主题支持"
git tag -a v1.2.0 -m "样式版本1.2.0"
样式的文档化

样式文档模板

markdown 复制代码
# 样式文档

## 概述
本文档描述了应用程序的样式系统。

## 颜色方案
- 主色:#4CAF50
- 次色:#2196F3
- 背景色:#ffffff
- 文本色:#333333

## 组件样式

### 按钮
- 主要按钮:绿色背景,白色文本
- 次要按钮:蓝色背景,白色文本

### 输入框
- 默认:白色背景,灰色边框
- 焦点:蓝色边框

## 使用示例
[代码示例]

内联注释

css 复制代码
/* ============================================
   按钮样式
   ============================================ */

/* 主要按钮:用于主要操作 */
QPushButton[class="primary"] {
    background-color: #4CAF50;  /* 主色 */
    color: white;
    padding: 10px 20px;         /* 内边距 */
    border-radius: 5px;          /* 圆角 */
}

/* 次要按钮:用于次要操作 */
QPushButton[class="secondary"] {
    background-color: #2196F3;  /* 次色 */
    color: white;
}

10.4 常见问题与解决方案

QSS不生效的原因

原因1:选择器不匹配

cpp 复制代码
// 问题:选择器不匹配
qApp->setStyleSheet("QPushButton { background-color: blue; }");
QPushButton *btn = new QPushButton();
// 如果btn的类型不是QPushButton,样式不会生效

// 解决:检查控件类型
qDebug() << "控件类型:" << btn->metaObject()->className();

原因2:样式被覆盖

cpp 复制代码
// 问题:样式被更高优先级的样式覆盖
qApp->setStyleSheet("QPushButton { background-color: blue; }");
button->setStyleSheet("background-color: red;");  // 覆盖应用级样式

// 解决:检查样式优先级
qDebug() << "控件样式表:" << button->styleSheet();
qDebug() << "应用样式表:" << qApp->styleSheet();

原因3:控件未正确初始化

cpp 复制代码
// 问题:在控件创建前设置样式
qApp->setStyleSheet("QPushButton { background-color: blue; }");
QPushButton *btn = new QPushButton();  // 样式可能未应用

// 解决:在控件创建后设置样式,或使用update()
btn->style()->unpolish(btn);
btn->style()->polish(btn);
btn->update();

原因4:QSS语法错误

cpp 复制代码
// 问题:QSS语法错误
qApp->setStyleSheet("QPushButton { background-color: blue }");  // 缺少分号

// 解决:检查QSS语法
QString qss = "QPushButton { background-color: blue; }";
// 验证语法...
qApp->setStyleSheet(qss);
样式覆盖失败

问题:样式优先级不够

cpp 复制代码
// 问题:类型选择器优先级低
qApp->setStyleSheet("QPushButton { background-color: blue; }");
// 被ID选择器覆盖
button->setObjectName("myButton");
qApp->setStyleSheet("#myButton { background-color: red; }");

// 解决:使用更高优先级的选择器
qApp->setStyleSheet("#myButton { background-color: red; }");

问题:样式被父控件覆盖

cpp 复制代码
// 问题:父控件样式覆盖子控件
parent->setStyleSheet("QPushButton { background-color: blue; }");
button->setStyleSheet("background-color: red;");  // 可能被覆盖

// 解决:使用更具体的选择器
button->setStyleSheet("#myButton { background-color: red; }");
样式在不同平台的差异

问题:不同平台渲染不同

cpp 复制代码
// 问题:Windows和macOS渲染不同
qApp->setStyleSheet("QPushButton { border-radius: 5px; }");

// 解决:使用平台特定样式
#ifdef Q_OS_WIN
    qApp->setStyleSheet("QPushButton { border-radius: 5px; }");
#elif defined(Q_OS_MAC)
    qApp->setStyleSheet("QPushButton { border-radius: 6px; }");
#else
    qApp->setStyleSheet("QPushButton { border-radius: 4px; }");
#endif

问题:字体渲染不同

cpp 复制代码
// 解决:使用平台特定字体
QFont font;
#ifdef Q_OS_WIN
    font.setFamily("Microsoft YaHei");
#elif defined(Q_OS_MAC)
    font.setFamily("PingFang SC");
#else
    font.setFamily("Noto Sans CJK SC");
#endif
qApp->setFont(font);
自定义控件样式不生效

问题:自定义控件未实现样式支持

cpp 复制代码
// 问题:自定义控件未实现paintEvent()
class CustomWidget : public QWidget {
    // 缺少paintEvent()实现
};

// 解决:实现paintEvent()和initStyleOption()
class CustomWidget : public QWidget {
protected:
    void paintEvent(QPaintEvent *event) override {
        QStylePainter painter(this);
        QStyleOption opt;
        opt.initFrom(this);
        style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
    }
};

问题:未设置Q_OBJECT宏

cpp 复制代码
// 问题:缺少Q_OBJECT宏
class CustomWidget : public QWidget {
    // 缺少Q_OBJECT
};

// 解决:添加Q_OBJECT宏
class CustomWidget : public QWidget {
    Q_OBJECT
    // ...
};
样式导致的布局问题

问题:样式影响布局计算

cpp 复制代码
// 问题:padding和margin影响布局
qApp->setStyleSheet("QPushButton { padding: 20px; margin: 10px; }");

// 解决:考虑样式对布局的影响
// 使用sizeHint()获取包含样式的尺寸
QSize hint = button->sizeHint();

问题:边框影响尺寸

cpp 复制代码
// 问题:边框增加控件尺寸
qApp->setStyleSheet("QPushButton { border: 2px solid; }");

// 解决:使用box-sizing(QSS不支持,需要代码处理)
// 或调整布局间距
样式冲突的解决

问题:多个样式规则冲突

cpp 复制代码
// 问题:样式冲突
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"
    "QPushButton { background-color: red; }"  // 冲突
);

// 解决:使用更具体的选择器
qApp->setStyleSheet(
    "QPushButton { background-color: blue; }"
    "#myButton { background-color: red; }"  // 更具体
);

问题:QSS和QPalette冲突

cpp 复制代码
// 问题:QSS和QPalette冲突
QPalette palette;
palette.setColor(QPalette::Button, Qt::blue);
qApp->setPalette(palette);
qApp->setStyleSheet("QPushButton { background-color: red; }");  // QSS优先级更高

// 解决:统一使用QSS或QPalette
// 或使用QSS引用QPalette
qApp->setStyleSheet("QPushButton { background-color: palette(button); }");

10.5 样式系统调试技巧

使用Qt样式表调试器

自定义调试工具

cpp 复制代码
class QSSDebugger : public QDialog {
    Q_OBJECT
    
public:
    QSSDebugger(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("QSS调试器");
        resize(800, 600);
        
        QVBoxLayout *layout = new QVBoxLayout(this);
        
        // 样式表编辑器
        m_editor = new QTextEdit(this);
        m_editor->setPlainText(qApp->styleSheet());
        layout->addWidget(m_editor);
        
        // 应用按钮
        QPushButton *applyBtn = new QPushButton("应用", this);
        connect(applyBtn, &QPushButton::clicked, [this]() {
            qApp->setStyleSheet(m_editor->toPlainText());
        });
        layout->addWidget(applyBtn);
        
        // 重置按钮
        QPushButton *resetBtn = new QPushButton("重置", this);
        connect(resetBtn, &QPushButton::clicked, [this]() {
            m_editor->setPlainText(qApp->styleSheet());
        });
        layout->addWidget(resetBtn);
    }
    
private:
    QTextEdit *m_editor;
};
实时修改样式

热重载样式

cpp 复制代码
class HotReloadStyleWatcher : public QObject {
    Q_OBJECT
    
public:
    HotReloadStyleWatcher(const QString &stylePath, QObject *parent = nullptr)
        : QObject(parent), m_stylePath(stylePath)
    {
        m_watcher = new QFileSystemWatcher(this);
        m_watcher->addPath(stylePath);
        
        connect(m_watcher, &QFileSystemWatcher::fileChanged,
                this, &HotReloadStyleWatcher::onFileChanged);
    }
    
private slots:
    void onFileChanged(const QString &path) {
        QFile file(path);
        if (file.open(QFile::ReadOnly)) {
            QString styleSheet = QLatin1String(file.readAll());
            qApp->setStyleSheet(styleSheet);
            qDebug() << "样式已重新加载:" << path;
        }
    }
    
    QString m_stylePath;
    QFileSystemWatcher *m_watcher;
};

// 使用(仅开发模式)
#ifdef QT_DEBUG
    HotReloadStyleWatcher *watcher = new HotReloadStyleWatcher("style.qss");
#endif
样式继承链查看

打印样式继承链

cpp 复制代码
void printStyleInheritanceChain(QWidget *widget) {
    QWidget *current = widget;
    int level = 0;
    
    qDebug() << "=== 样式继承链 ===";
    
    while (current) {
        QString indent = QString("  ").repeated(level);
        qDebug() << indent << "级别" << level << ":"
                 << current->metaObject()->className()
                 << "(" << current->objectName() << ")";
        qDebug() << indent << "  样式表:" 
                 << (current->styleSheet().isEmpty() ? "无" : "有");
        qDebug() << indent << "  调色板:" 
                 << (current->palette().isCopyOf(qApp->palette()) ? "继承" : "自定义");
        
        current = current->parentWidget();
        level++;
    }
}
样式属性检查

检查控件样式属性

cpp 复制代码
void inspectWidgetStyle(QWidget *widget) {
    qDebug() << "=== 控件样式检查 ===";
    qDebug() << "控件类型:" << widget->metaObject()->className();
    qDebug() << "对象名:" << widget->objectName();
    qDebug() << "样式表:" << widget->styleSheet();
    qDebug() << "调色板:" << widget->palette();
    qDebug() << "字体:" << widget->font();
    qDebug() << "尺寸:" << widget->size();
    qDebug() << "位置:" << widget->pos();
    
    // 检查所有属性
    const QMetaObject *meta = widget->metaObject();
    for (int i = 0; i < meta->propertyCount(); i++) {
        QMetaProperty prop = meta->property(i);
        if (prop.isReadable()) {
            qDebug() << "属性:" << prop.name() 
                     << "=" << prop.read(widget);
        }
    }
}

10.6 跨平台样式一致性

不同平台的样式差异

Windows平台

  • 默认字体:Segoe UI
  • 默认DPI:96
  • 样式特点:经典Windows风格

macOS平台

  • 默认字体:San Francisco
  • 默认DPI:72
  • 样式特点:圆角、毛玻璃效果

Linux平台

  • 默认字体:取决于桌面环境
  • 默认DPI:96
  • 样式特点:多样化
统一样式的实现策略

使用Fusion样式

cpp 复制代码
// Fusion样式在所有平台上外观一致
qApp->setStyle("fusion");

统一QSS样式

cpp 复制代码
// 使用统一的QSS,不依赖平台
qApp->setStyleSheet(loadStyleSheet("unified-style.qss"));

平台适配

cpp 复制代码
void applyUnifiedStyle() {
    QString baseStyle = loadStyleSheet("base-style.qss");
    
    // 平台特定调整
#ifdef Q_OS_WIN
    baseStyle += loadStyleSheet("windows-adjustments.qss");
#elif defined(Q_OS_MAC)
    baseStyle += loadStyleSheet("macos-adjustments.qss");
#else
    baseStyle += loadStyleSheet("linux-adjustments.qss");
#endif
    
    qApp->setStyleSheet(baseStyle);
}
平台特定样式的处理

条件样式加载

cpp 复制代码
void loadPlatformSpecificStyle() {
    QString style;
    
#ifdef Q_OS_WIN
    style = loadStyleSheet("windows-style.qss");
#elif defined(Q_OS_MAC)
    style = loadStyleSheet("macos-style.qss");
#else
    style = loadStyleSheet("linux-style.qss");
#endif
    
    qApp->setStyleSheet(style);
}

平台检测

cpp 复制代码
QString getPlatformName() {
#ifdef Q_OS_WIN
    return "windows";
#elif defined(Q_OS_MAC)
    return "macos";
#elif defined(Q_OS_LINUX)
    return "linux";
#else
    return "unknown";
#endif
}
样式的兼容性测试

自动化测试

cpp 复制代码
class StyleCompatibilityTest {
public:
    static void testAllPlatforms() {
        QStringList platforms = {"windows", "macos", "linux"};
        
        for (const QString &platform : platforms) {
            qDebug() << "测试平台:" << platform;
            testStyleOnPlatform(platform);
        }
    }
    
private:
    static void testStyleOnPlatform(const QString &platform) {
        // 加载样式
        QString style = loadStyleSheet(platform + "-style.qss");
        qApp->setStyleSheet(style);
        
        // 测试各种控件
        testButton();
        testInput();
        testMenu();
        // ...
    }
    
    static void testButton() {
        QPushButton *btn = new QPushButton("测试");
        btn->show();
        // 检查样式是否正确应用
        // ...
    }
};

10.7 样式系统与其他技术的集成

QML与QSS的集成

QML使用QSS样式

qml 复制代码
// QML中可以使用Qt的样式系统
import QtQuick.Controls 2.15

Button {
    text: "按钮"
    // QML有自己的样式系统,但可以引用Qt样式
}

共享样式定义

cpp 复制代码
// 在C++中定义样式,QML和Widgets共享
QString sharedStyle = R"(
    :root {
        --primary-color: #4CAF50;
        --secondary-color: #2196F3;
    }
)";

// QML和Widgets都可以使用这些颜色定义
Web技术(CSS)的借鉴

CSS变量(通过QSS属性模拟)

cpp 复制代码
// 定义CSS变量(通过属性)
qApp->setProperty("--primary-color", "#4CAF50");
qApp->setProperty("--secondary-color", "#2196F3");

// 在QSS中使用(需要自定义处理)
// 或使用配置文件

CSS Grid布局(通过Qt布局模拟)

cpp 复制代码
// CSS Grid在Qt中通过布局管理器实现
QGridLayout *grid = new QGridLayout();
// 实现类似CSS Grid的布局
样式与动画系统的结合

使用QPropertyAnimation

cpp 复制代码
class AnimatedStyleButton : public QPushButton {
    Q_OBJECT
    Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
    
public:
    AnimatedStyleButton(const QString &text, QWidget *parent = nullptr)
        : QPushButton(text, parent)
    {
        m_backgroundColor = palette().color(QPalette::Button);
    }
    
    QColor backgroundColor() const { return m_backgroundColor; }
    void setBackgroundColor(const QColor &color) {
        m_backgroundColor = color;
        setStyleSheet(QString("background-color: %1;").arg(color.name()));
    }
    
    void animateColor(const QColor &targetColor) {
        QPropertyAnimation *animation = new QPropertyAnimation(this, "backgroundColor");
        animation->setDuration(300);
        animation->setStartValue(m_backgroundColor);
        animation->setEndValue(targetColor);
        animation->start(QAbstractAnimation::DeleteWhenStopped);
    }
    
private:
    QColor m_backgroundColor;
};

使用QGraphicsEffect

cpp 复制代码
// 使用阴影效果
QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
shadow->setBlurRadius(10);
shadow->setColor(QColor(0, 0, 0, 100));
shadow->setOffset(0, 2);
button->setGraphicsEffect(shadow);
样式与状态机的集成

使用QStateMachine

cpp 复制代码
class StyledStateMachine : public QStateMachine {
    Q_OBJECT
    
public:
    StyledStateMachine(QWidget *widget) : m_widget(widget) {
        // 正常状态
        QState *normalState = new QState();
        normalState->assignProperty(m_widget, "styleSheet", 
                                   "QWidget { background-color: white; }");
        
        // 悬停状态
        QState *hoverState = new QState();
        hoverState->assignProperty(m_widget, "styleSheet",
                                  "QWidget { background-color: #f0f0f0; }");
        
        // 添加状态
        addState(normalState);
        addState(hoverState);
        setInitialState(normalState);
        
        // 添加转换
        normalState->addTransition(m_widget, SIGNAL(entered()), hoverState);
        hoverState->addTransition(m_widget, SIGNAL(left()), normalState);
    }
    
private:
    QWidget *m_widget;
};


总结

Qt样式系统的核心思想

样式系统的选择建议

学习路径和进阶方向

推荐资源和进一步阅读


相关推荐
hqwest16 小时前
码上通QT实战10--监控页面02-绘制温度盘
开发语言·qt·自定义控件·qwidget·提升部件·qt绘图
cn_mengbei17 小时前
鸿蒙PC开发实战:Qt环境搭建保姆级教程与常见问题避坑指南(HarmonyOS 4.0+DevEco Studio 3.1最新版)
qt·华为·harmonyos
非凡ghost17 小时前
MPC-QT视频播放器(基于Qt框架播放器)
开发语言·windows·qt·音视频·软件需求
cn_mengbei17 小时前
从零到一:基于Qt on HarmonyOS的鸿蒙PC原生应用开发实战与性能优化指南
qt·性能优化·harmonyos
IOT-Power19 小时前
QT 对话框(QDialog)中 accept、reject、exec、open的使用
开发语言·qt
_OP_CHEN20 小时前
【从零开始的Qt开发指南】(十九)Qt 文件操作:从 I/O 设备到文件信息,一站式掌握跨平台文件处理
开发语言·c++·qt·前端开发·文件操作·gui开发·qt文件
cn_mengbei21 小时前
鸿蒙PC开发指南:从零配置Qt环境到实战部署完整流程
qt·华为·harmonyos
GREGGXU21 小时前
Could not load the Qt platform plugin “xcb“ in ““ even though it was found.
linux·qt
Summer_Uncle1 天前
【QT学习】qt项目使用MySQL数据库
数据库·qt·学习