Qt 中实现系统主题感知
【写在前面】
在现代桌面应用程序开发中,系统主题感知是一项重要的功能,它使得应用程序能够根据用户的系统主题设置(如深色模式或浅色模式)自动调整其外观。
Qt 作为一个跨平台的C++图形用户界面应用程序开发框架,提供了丰富的工具和类来实现这一功能。
【正文开始】
一、使用效果

二、系统主题感知助手类(SystemThemeHelper)
SystemThemeHelper类是一个封装了系统主题感知功能的Qt对象。它主要通过读取系统设置和监听系统主题变化来更新应用程序的主题颜色和颜色方案。
类定义与属性
在
systemthemehelper.h中,SystemThemeHelper类继承自QObject,并定义了两个属性:themeColor和colorScheme。这两个属性分别表示当前的主题颜色和颜色方案(深色、浅色或无)。class SystemThemeHelper : public QObject
{
Q_OBJECT
Q_PROPERTY(QColor themeColor READ themeColor NOTIFY themeColorChanged)
Q_PROPERTY(SystemThemeHelper::ColorScheme colorScheme READ colorScheme NOTIFY colorSchemeChanged)
// ...
};
ColorScheme是一个枚举类,定义了三种颜色方案:None、Dark和Light。构造函数与析构函数
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()
{
// 清理资源
}
获取主题颜色和颜色方案
getThemeColor和getColorScheme是两个不可用于绑定的方法,它们立即返回当前的主题颜色和颜色方案,但不会触发任何更新通知。这两个方法主要用于快速获取当前设置,而不关心后续的变化。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
}
更新主题颜色和颜色方案
themeColor和colorScheme是两个可用于绑定的方法,它们返回当前的主题颜色和颜色方案,并在值发生变化时发出通知。这两个方法内部调用了私有成员函数的更新逻辑。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;
}
设置窗口标题栏模式
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
}
定时器事件处理
timerEvent方法是一个虚函数,用于处理定时器事件。它定期调用更新函数来检查主题颜色和颜色方案是否发生变化,并在变化时发出通知。void SystemThemeHelper::timerEvent(QTimerEvent *)
{
Q_D(SystemThemeHelper);
d->_updateThemeColor();
d->_updateColorScheme();
}
三、实现细节
SystemThemeHelperPrivate是SystemThemeHelper的私有实现类,它封装了所有的实现细节和状态变量。这个类主要负责读取系统设置、更新主题颜色和颜色方案,并发出通知。
构造函数与成员变量
SystemThemeHelperPrivate的构造函数接收一个指向SystemThemeHelper的指针,并初始化成员变量。成员变量包括主题颜色、颜色方案、定时器和一些平台特定的设置对象。SystemThemeHelperPrivate::SystemThemeHelperPrivate(SystemThemeHelper *q)
: q_ptr(q)
{
// 初始化成员变量
}
更新函数
_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();
}
}
平台特定的实现
在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 中实现系统主题感知的更多相关文章
- QT中自定义系统托盘的实现—c++语言为例
将要介绍的是:QT中自定义系统托盘(systemtray)的一个Demo,希望能帮需要的读者快速上手. 前提假设是诸位已经知道QT中的signals .slot以及资源文件,所以关于这些不会再累述. ...
- Qt 框架的图形性能高(OpenGL上的系统效率高),网络性能低,开发效率高,Quick是可以走硬件加速——Qt中分为好几套图形系统,差不多代表了2D描画的发展史。最经典的软描画系统
-----图形性能部分-----Qt的widgets部分,运行时的图像渲染性能是一般的,因为大部分的界面内容都是Qt自绘,没有走硬件加速,也就是说很多图形内容都是CPU算出来的.但是widgets底层 ...
- Qt 中的属性系统(Property System)
21 人赞同了该文章 本节内容主要讲解我对 Qt 属性系统的理解.官方文档参考 The Property System. 如何理解"属性系统"这个概念? 一般我们说一个类有什么属性 ...
- Qt 中的事件处理(一)
1.图形界面应用程序的消息处理模型 特点: 基于操作系统才能运行 GUI应用程序提供的功能必须由用户触发 用户操作界面时操作系统是第一个感知的 系统内核的消息通过事件处理转变成QT的信号 2. Qt中 ...
- QT 中 关键字讲解(emit,signal,slot)
Qt中的类库有接近一半是从基类QObject上继承下来,信号与反应槽(signals/slot)机制就是用来在QObject类或其子类间通讯的方法.作为一种通用的处理机制,信号与反应槽非常灵活,可以携 ...
- 第39课 Qt中的事件处理(下)
1. 事件的传递过程 (1)操作系统检测到用户动作时,会产生一条系统消息,该消息被发送到Qt应用程序 (2)Qt应用程序收到系统消息后,将其转化为一个对应的QEvent事件对象,并调用QObject: ...
- 第38课 Qt中的事件处理(上)
1. GUI程序原理回顾 (1)图形界面应用程序的消息处理模型 (2)思考:操作系统发送的消息如何转变为Qt信号 2. Qt中的事件处理 (1)Qt平台将系统产生的消息转换为Qt事件 ①Qt事件是一个 ...
- 第32课 Qt中的文件操作
1. Qt的中IO操作 (1)Qt中IO操作的处理方式 ①Qt通过统一的接口简化了文件和外部设备的操作方式 ②Qt中的文件被看作一种特殊的外部设备 ③Qt中的文件操作与外部设备的操作相同 (2)IO操 ...
- QT中静态库的生成与使用
一. 静态库的生成 1. 测试目录: lib 2. 源码文件名: mywindow.h, mywindow.cpp, 类MyWindow继承于QPushButton, 并将文字设置为&qu ...
- Qt之资源系统
简述 Qt 的资源系统用于存储应用程序的可执行二进制文件,它采用平台无关的机制.当你的程序总需要这样的一系列文件(图标.翻译文件等)并且不想冒丢失某些文件的风险时,这就显得十分有用. 资源系统基于 q ...
随机推荐
- C#轻松实现Modbus通信
1.前言 大家好!我是付工.前面给大家介绍了一系列关于RS485与Modbus的知识. 终于有人把RS485说清楚了终于有人把Modbus说明白了通透!终于把ModbusRTU弄明白了这样看来,Mod ...
- Win11使用Translucent TB设置Windows导航栏透明失败解决方案
Win11使用Translucent TB设置Windows导航栏透明失败解决方案 Translucent TB下载方式:直接在Windows自带的Microsoft应用商店里面搜索下载就可以了 1. ...
- 从0到1实现项目Docker编排部署
在深入讨论 Docker 编排之前,首先让我们了解一下 Docker 技术本身.Docker 是一个开源平台,旨在帮助开发者自动化应用程序的部署.扩展和管理.自 2013 年推出以来,Docker 迅 ...
- A星、Floyod、Bellman-Ford
A 星算法 A 星和 Dijkstra 算法唯一区别在于堆中排序的依据.distance 数组仍然保存实际代价,预估代价只影响堆的弹出顺序. Dijkstra 根据源点到当前点的实际代价进行排序. A ...
- Python移除重复元素
第一种写法:通过set特性去重,但是不保证顺序,无序的 a = ["1", 1, "1", 2] a = list(set(a)) print(a) 结果: [ ...
- Abp源码分析之Abp本地化
aspnetcore mvc 实现本地化 新建mvc项目 修改Program.cs using Microsoft.AspNetCore.Localization; using Microsoft.A ...
- 合宙低功耗4G模组HTTP网络协议应用
一.HTTP概述 1.1 简介 HTTP是HyperTextTransferProtocol(超文本传输协议)的缩写.HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型.HT ...
- 记录一次CTF经典PHP反序列化
PHP反序列化 序列化通俗来讲就是将对象转化为可以存储.传输的字符串,反序列化就是把字符串再变回对象的过程. 例如: <?php class chybate { var $test = '123 ...
- 深入JVM——栈和局部变量
java栈概述 记得当初我学习java时,常常听见身边的朋友说:"你要记住,当new一个对象时,对象的引用存放在栈里,而对象是存放在堆里的".当时,听到这句教导时,脑海里立即出现栈 ...
- JVM性能优化, Part 5:Java的伸缩性
本文由 ImportNew - ImportNew读者 翻译自 Javaworld.如需转载本文,请先参见文章末尾处的转载要求. ImportNew注: JVM性能优化系列文章前4篇由ImportNe ...