【写在前面】

在现代桌面应用程序开发中,系统主题感知是一项重要的功能,它使得应用程序能够根据用户的系统主题设置(如深色模式或浅色模式)自动调整其外观。

Qt 作为一个跨平台的C++图形用户界面应用程序开发框架,提供了丰富的工具和类来实现这一功能。


【正文开始】

一、使用效果

二、系统主题感知助手类(SystemThemeHelper)

SystemThemeHelper类是一个封装了系统主题感知功能的Qt对象。它主要通过读取系统设置和监听系统主题变化来更新应用程序的主题颜色和颜色方案。

  1. 类定义与属性

    systemthemehelper.h中,SystemThemeHelper类继承自QObject,并定义了两个属性:themeColorcolorScheme。这两个属性分别表示当前的主题颜色和颜色方案(深色、浅色或无)。

    class SystemThemeHelper : public QObject
    {
    Q_OBJECT
    Q_PROPERTY(QColor themeColor READ themeColor NOTIFY themeColorChanged)
    Q_PROPERTY(SystemThemeHelper::ColorScheme colorScheme READ colorScheme NOTIFY colorSchemeChanged)
    // ...
    };

    ColorScheme是一个枚举类,定义了三种颜色方案:NoneDarkLight

  2. 构造函数与析构函数

    SystemThemeHelper的构造函数初始化了一些私有成员变量,并启动了一个定时器,用于定期更新主题颜色和颜色方案。析构函数则负责清理资源。

    SystemThemeHelper::SystemThemeHelper(QObject *parent)
    : QObject{parent}, d_ptr(new SystemThemeHelperPrivate(this))
    {
    Q_D(SystemThemeHelper);
    d->m_themeColor = getThemeColor();
    d->m_colorScheme = getColorScheme();
    d->m_timer.start(200, this);
    #ifdef Q_OS_WIN
    initializeFunctionPointers();
    #endif
    } SystemThemeHelper::~SystemThemeHelper()
    {
    // 清理资源
    }
  3. 获取主题颜色和颜色方案

    getThemeColorgetColorScheme是两个不可用于绑定的方法,它们立即返回当前的主题颜色和颜色方案,但不会触发任何更新通知。这两个方法主要用于快速获取当前设置,而不关心后续的变化。

    QColor SystemThemeHelper::getThemeColor() const
    {
    Q_D(const SystemThemeHelper);
    #ifdef Q_OS_WIN
    return QColor::fromRgb(d->m_themeColorSettings.value("ColorizationColor").toUInt());
    #endif
    } SystemThemeHelper::ColorScheme SystemThemeHelper::getColorScheme() const
    {
    Q_D(const SystemThemeHelper);
    #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
    const auto scheme = QGuiApplication::styleHints()->colorScheme();
    return scheme == Qt::ColorScheme::Dark ? ColorScheme::Dark : ColorScheme::Light;
    #else
    #ifdef Q_OS_WIN
    return !d->m_colorSchemeSettings.value("AppsUseLightTheme").toBool() ? ColorScheme::Dark : ColorScheme::Light;
    #else //linux
    const QPalette defaultPalette;
    const auto text = defaultPalette.color(QPalette::WindowText);
    const auto window = defaultPalette.color(QPalette::Window);
    return text.lightness() > window.lightness() ? ColorScheme::Dark : ColorScheme::Light;
    #endif // Q_OS_WIN
    #endif // QT_VERSION
    }
  4. 更新主题颜色和颜色方案

    themeColorcolorScheme是两个可用于绑定的方法,它们返回当前的主题颜色和颜色方案,并在值发生变化时发出通知。这两个方法内部调用了私有成员函数的更新逻辑。

    QColor SystemThemeHelper::themeColor()
    {
    Q_D(SystemThemeHelper);
    d->_updateThemeColor();
    return d->m_themeColor;
    } SystemThemeHelper::ColorScheme SystemThemeHelper::colorScheme()
    {
    Q_D(SystemThemeHelper);
    d->_updateColorScheme();
    return d->m_colorScheme;
    }
  5. 设置窗口标题栏模式

    setWindowTitleBarMode方法允许设置窗口标题栏的模式(深色或浅色)。这个方法在Windows平台上通过调用 DWM API 实现,而在其他平台上则不支持。

    bool SystemThemeHelper::setWindowTitleBarMode(QWindow *window, bool isDark)
    {
    #ifdef Q_OS_WIN
    return bool(pDwmSetWindowAttribute ? !pDwmSetWindowAttribute(HWND(window->winId()), 20, &isDark, sizeof(BOOL)) : false);
    #else
    return false;
    #endif //Q_OS_WIN
    }
  6. 定时器事件处理

    timerEvent方法是一个虚函数,用于处理定时器事件。它定期调用更新函数来检查主题颜色和颜色方案是否发生变化,并在变化时发出通知。

    void SystemThemeHelper::timerEvent(QTimerEvent *)
    {
    Q_D(SystemThemeHelper);
    d->_updateThemeColor();
    d->_updateColorScheme();
    }

三、实现细节

SystemThemeHelperPrivateSystemThemeHelper的私有实现类,它封装了所有的实现细节和状态变量。这个类主要负责读取系统设置、更新主题颜色和颜色方案,并发出通知。

  1. 构造函数与成员变量

    SystemThemeHelperPrivate的构造函数接收一个指向SystemThemeHelper的指针,并初始化成员变量。成员变量包括主题颜色、颜色方案、定时器和一些平台特定的设置对象。

    SystemThemeHelperPrivate::SystemThemeHelperPrivate(SystemThemeHelper *q)
    : q_ptr(q)
    {
    // 初始化成员变量
    }
  2. 更新函数

    _updateThemeColor_updateColorScheme是两个更新函数,它们检查当前的主题颜色和颜色方案是否发生变化,并在变化时更新成员变量并发出通知。

    void SystemThemeHelperPrivate::_updateThemeColor()
    {
    Q_Q(SystemThemeHelper);
    auto nowThemeColor = q->getThemeColor();
    if (nowThemeColor != m_themeColor) {
    m_themeColor = nowThemeColor;
    emit q->themeColorChanged();
    }
    } void SystemThemeHelperPrivate::_updateColorScheme()
    {
    Q_Q(SystemThemeHelper);
    auto nowColorScheme = q->getColorScheme();
    if (nowColorScheme != m_colorScheme) {
    m_colorScheme = nowColorScheme;
    emit q->colorSchemeChanged();
    }
    }
  3. 平台特定的实现

    在Windows平台上,SystemThemeHelperPrivate使用QSettings来读取系统主题设置,并使用DWM API来设置窗口标题栏的模式。这些实现细节被封装在条件编译块中,以确保跨平台的兼容性。

    #ifdef Q_OS_WIN
    QSettings m_themeColorSettings{QSettings::UserScope, "Microsoft", "Windows\\DWM"};
    QSettings m_colorSchemeSettings{QSettings::UserScope, "Microsoft", "Windows\\CurrentVersion\\Themes\\Personalize"};
    static DwmSetWindowAttributeFunc pDwmSetWindowAttribute = nullptr;
    // ...
    static inline bool initializeFunctionPointers()
    {
    // 初始化DWM API函数指针
    }
    #endif //Q_OS_WIN

四、如何使用

C++:

	SystemThemeHelper *helper = new SystemThemeHelper;
QObject::connect(helper, &SystemThemeHelper::themeColorChanged, [helper]{
qDebug() << helper->getThemeColor();
});
QObject::connect(helper, &SystemThemeHelper::colorSchemeChanged, [helper]{
qDebug() << helper->getColorScheme();
});

Qml:

import QtQuick 2.15
import QtQuick.Window 2.15 import DelegateUI.Utils 1.0 Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("SystemThemeHelper Test - ") + (themeHelper.colorScheme == SystemThemeHelper.Dark ? "Dark" : "Light")
color: themeHelper.colorScheme == SystemThemeHelper.Dark ? "black" : "white" Behavior on color { ColorAnimation { } } SystemThemeHelper {
id: themeHelper
onThemeColorChanged: {
console.log("onThemeColorChanged:", themeColor);
}
onColorSchemeChanged: {
setWindowTitleBarMode(window, themeHelper.colorScheme == SystemThemeHelper.Dark)
console.log("onColorSchemeChanged:", colorScheme);
}
Component.onCompleted: {
console.log("onColorSchemeChanged:", colorScheme);
setWindowTitleBarMode(window, themeHelper.colorScheme == SystemThemeHelper.Dark)
}
} Text {
anchors.centerIn: parent
text: qsTr("主题颜色")
font.family: "微软雅黑"
font.pointSize: 32
color: themeHelper.themeColor
}
}

【结语】

通过SystemThemeHelper类,我们可以在 Qt 应用程序中实现系统主题感知功能。

这个类封装了读取系统设置、更新主题颜色和颜色方案以及发出通知的逻辑,使得我们可以轻松地根据系统主题变化来调整应用程序的外观。

此外,通过条件编译和平台特定的实现,还确保了跨平台的兼容性。

最后:项目链接(多多star呀.._):

Github: https://github.com/mengps/QmlControls

Gitee: https://gitee.com/MenPenS/QmlControls

Qt 中实现系统主题感知的更多相关文章

  1. QT中自定义系统托盘的实现—c++语言为例

    将要介绍的是:QT中自定义系统托盘(systemtray)的一个Demo,希望能帮需要的读者快速上手. 前提假设是诸位已经知道QT中的signals .slot以及资源文件,所以关于这些不会再累述. ...

  2. Qt 框架的图形性能高(OpenGL上的系统效率高),网络性能低,开发效率高,Quick是可以走硬件加速——Qt中分为好几套图形系统,差不多代表了2D描画的发展史。最经典的软描画系统

    -----图形性能部分-----Qt的widgets部分,运行时的图像渲染性能是一般的,因为大部分的界面内容都是Qt自绘,没有走硬件加速,也就是说很多图形内容都是CPU算出来的.但是widgets底层 ...

  3. Qt 中的属性系统(Property System)

    21 人赞同了该文章 本节内容主要讲解我对 Qt 属性系统的理解.官方文档参考 The Property System. 如何理解"属性系统"这个概念? 一般我们说一个类有什么属性 ...

  4. Qt 中的事件处理(一)

    1.图形界面应用程序的消息处理模型 特点: 基于操作系统才能运行 GUI应用程序提供的功能必须由用户触发 用户操作界面时操作系统是第一个感知的 系统内核的消息通过事件处理转变成QT的信号 2. Qt中 ...

  5. QT 中 关键字讲解(emit,signal,slot)

    Qt中的类库有接近一半是从基类QObject上继承下来,信号与反应槽(signals/slot)机制就是用来在QObject类或其子类间通讯的方法.作为一种通用的处理机制,信号与反应槽非常灵活,可以携 ...

  6. 第39课 Qt中的事件处理(下)

    1. 事件的传递过程 (1)操作系统检测到用户动作时,会产生一条系统消息,该消息被发送到Qt应用程序 (2)Qt应用程序收到系统消息后,将其转化为一个对应的QEvent事件对象,并调用QObject: ...

  7. 第38课 Qt中的事件处理(上)

    1. GUI程序原理回顾 (1)图形界面应用程序的消息处理模型 (2)思考:操作系统发送的消息如何转变为Qt信号 2. Qt中的事件处理 (1)Qt平台将系统产生的消息转换为Qt事件 ①Qt事件是一个 ...

  8. 第32课 Qt中的文件操作

    1. Qt的中IO操作 (1)Qt中IO操作的处理方式 ①Qt通过统一的接口简化了文件和外部设备的操作方式 ②Qt中的文件被看作一种特殊的外部设备 ③Qt中的文件操作与外部设备的操作相同 (2)IO操 ...

  9. QT中静态库的生成与使用

    一. 静态库的生成    1. 测试目录: lib    2. 源码文件名: mywindow.h, mywindow.cpp, 类MyWindow继承于QPushButton, 并将文字设置为&qu ...

  10. Qt之资源系统

    简述 Qt 的资源系统用于存储应用程序的可执行二进制文件,它采用平台无关的机制.当你的程序总需要这样的一系列文件(图标.翻译文件等)并且不想冒丢失某些文件的风险时,这就显得十分有用. 资源系统基于 q ...

随机推荐

  1. 云原生爱好者周刊:Lens 5.0 发布,更炫、更快、更强!

    云原生一周动态要闻: Lens 5.0.0 发布 GitHub 推出 AI 编程工具 GitHub Copilot Kubernetes 发布 2020 年社区年度报告 Weaveworks 推出适用 ...

  2. docker实战: vue+java+uniapp部署到阿里云服务器详解(此文没有使用宝塔面板部署),附带各种坑处理

    一.部署前准备工具以及注意事项: 项目来源:https://gitee.com/ZhongBangKeJi/crmeb_java?_from=gitee_search 官方部署文档:https://d ...

  3. NIO和传统IO

    传统 IO 基于字节流或字符流(如 FileInputStream.BufferedReader 等)进行文件读写,以及使用 Socket 和 ServerSocket 进行网络传输. NIO 使用通 ...

  4. vue之JavaScript封装

    导入js文件有很多种方式,我喜欢使用这种,感觉跟框架导入很相似.没有多余的沉湎. 定义一个js文件,比如util.js import Vue from "vue"; //===== ...

  5. Computersystemsicherheit学习笔记

    1.Verlässliche System 1.1 Security和Safety的区别 1.2 Fault,Error和Failure的区别 举例如下: 1.3 Verfügbarkeit 例题: ...

  6. 聊聊jvm的内存结构, 以及各种结构的作用

    什么是JVM 定义:Java Virtual Machine,JAVA程序的运行环境(JAVA二进制字节码的运行环境) 内存结构 JVM 内存布局规定了 Java 在运行过程中内存申请.分配.管理的策 ...

  7. 别再被多线程搞晕了!一篇文章轻松搞懂 Linux 多线程同步!

    前言 大家有没有遇到过,代码跑着跑着,线程突然抢资源抢疯了?其实,这都是"多线程同步"在作怪.多线程同步是个老生常谈的话题,可每次真正要处理时还是让人头疼.这篇文章,带你从头到尾掌 ...

  8. 题解:CF1537E2 Erase and Extend (Hard Version)

    CF1537E2 Erase and Extend 题解 分析 通过观察题目,可以证明结果一定是由多次前缀复制得来的. 题目要求你进行删和复制的操作,与其交替着操作,不如直接先删到最优的前缀再进行复制 ...

  9. ServiceMesh 4:实现流量染色和分级发布

    ★ ServiceMesh系列 1 什么是流量染色 在复杂的生产场景中,经常会有同一个服务中,存在多个版本长期共存的需求.为了让不同的用户在不一样的版本中使用,就需要对用户的请求进行采样和染色,打上不 ...

  10. Docker私有仓库操作----命令行

    查看所有镜像 http://10.206.29.162:5000/v2/_catalog {"repositories":["dashboard"," ...