在PySide6/PyQt6的项目中实现样式切换处理

在桌面应用开发中,“主题切换(深色 / 浅色模式)”已经不再是锦上添花的功能,而是逐渐成为一种用户刚需。尤其对于长时间使用的 ERP 工具类应用来说,良好的视觉舒适度对用户体验影响巨大。本篇随笔针对PySide6/PyQt6的项目的实现案例,介绍如何实现样式切换处理。

 

Qt 官方本身提供了 QStyle 体系,但如果想做到:

 

可自定义配色

 

可作为品牌皮肤

 

可热切换(无需重启)

 

多主题共存

 

可持久化

 

那么,采用 QSS (Qt Style Sheet) + 统一主题管理器的方案无疑是目前最成熟、最实用、性价比最高的选择。

 

本文将从 原理 → 架构设计 → 实现方式 → 最佳实践 四个维度,完整介绍在 PySide6 / PyQt6 项目中如何构建一套“企业级主题切换系统”。

 

一、为什么不能简单地用 setStyle?

很多初学者在做样式适配时,都会走入一个误区:

 

btn.setStyleSheet("background:red")

table.setStyleSheet("color:white")

...

这种写法短期“能用”,长期会带来三个严重问题:

 

1. 样式分散,难维护

 

不同窗口、不同控件中的样式写法彼此独立,一个主题改动需要满项目搜索替换,很容易遗漏。

 

2. 逻辑和表现混杂

 

UI 逻辑代码被大量 CSS 淹没,维护成本剧增,违反基本的架构解耦原则。

 

3. 无法进行主题切换

 

没有分主题文件结构,一旦需要切换风格就必须“重写所有 setStyle”,几乎不可实现。

 

✅ 因此,如果项目规模超过 3 个界面,我们就必须放弃零散 setStyle 写法,转而使用 QSS 主题体系。

 

二、什么是 QSS?

Qt 中的 QSS (Qt Style Sheet) 本质和 CSS 非常类似,几乎可以视为 CSS 在 Qt 世界中的实现。

 

对 Qt 控件来说:

 

QWidget → HTML 标签

 

QPushButton、QTableView → CSS 选择器

 

background / border / padding / color → 样式属性

 

例如:

 

QPushButton {

    background: #3a3a3a;

    border-radius: 6px;

    color: white;

}

QSS 能做到:

 

全局覆盖

 

继承 & 层级作用

 

精准控件控制

 

即时加载刷新

 

我们希望做到:

 

业务代码永远不接触 CSS

主题由独立模块统一调度

 

复制代码

project/

├── main.py

├── core/

│ └── theme_manager.py

└── themes/

    ├── light.qss

    └── dark.qss

复制代码

通过 ThemeManager 将 UI 层与样式层完全隔离,可读性和可维护性大幅提升。

 

我们需要一个负责三件事的主题管理器统一类:

 

读取主题文件

 

调用 Qt API 应用主题

 

记录并恢复用户选择

 

QSS 主题文件设计示例:以下是一个简化版本:

 

浅色主题

 

复制代码

QWidget {

    background: #ffffff;

    color: #202020;

}

 

QPushButton {

    background: #e8e8e8;

    border-radius: 4px;

}

复制代码

深色主题

 

复制代码

QWidget {

    background: #2b2b2b;

    color: #f0f0f0;

}

 

QPushButton {

    background: #3a3a3a;

    color: #ffffff;

}

复制代码

三、UI 中的使用方式

我们通过创建系统相关的菜单,如定义样式切换的菜单,然后绑定对应的菜单信号处理。

 

action_dark.triggered.connect(lambda: ThemeManager.apply("dark"))

action_light.triggered.connect(lambda: ThemeManager.apply("light"))

这样切换的时候,无需重启——Qt 会自动刷新全部控件。

 

通过样式管理器,实现不同主题演示的即时切换。

 

主题系统并不是简单的“换几个颜色”。

 

它是 UI 架构中不可忽视的一环:

 

影响可维护性

 

决定代码整洁度

 

直接关系用户体验

 

在 PySide6 / PyQt6 中,

 

QSS + ThemeManager + 动态切换 + 持久化

 

这一套组合,几乎是目前最成熟可靠的主题实现方案,没有之一。

 

真正的统一UI的样式,包含 5 个维度:

 

维度 说明

色板统一 背景色、主色、强调色、警示色

字体统一 字体家族、字号梯度

控件形态 圆角、border、padding

交互动效 hover、press、disable

布局密度 表格行距、控件间距

 

 

四、具体在PySide6/PyQt6的项目中的实践过程

我们根据前面的介绍,在项目目录中创建两个不同主题样式的文件,如下所示。

 

image

 

然后根据样式的需要定义对应的相关内容,如下是浅色的主题定义,通过定义对应控件的颜色、字体、背景色等相关属性,实现统一的效果。

 

image

 

通过辅助类,我们创建几个菜单来实现不同样式的切换。

 

复制代码

        theme_menu = menu_bar.addMenu("界面主题")

        light_action = ControlUtil.create_menu(

            self, theme_menu, "浅色主题", "info"

        )

        light_action.triggered.connect(lambda: self.set_theme(1))

        dark_action = ControlUtil.create_menu(

            self, theme_menu, "深色主题", "info"

        )

        dark_action.triggered.connect(lambda: self.set_theme(2))

        system_action = ControlUtil.create_menu(

            self, theme_menu, "初始主题", "info"

        )

        system_action.triggered.connect(lambda: self.set_theme(0))

复制代码

菜单界面效果如下所示。

 

image

 

其中样式的信号处理,我们通过一个单件的样式总线对象来处理。

 

    def set_theme(self, theme_type):

        """设置主题颜色"""

        self.theme_type = theme_type

        # 切换主题颜色

        ThemeBus().set_theme_type(self.theme_type)

其中,我们对样式总线对象的变化进行信号绑定处理。

 

        #监听主题变化的事件

        ThemeBus().theme_type_changed.connect(self.on_theme_changed)

对主题样式的变化进行处理。

 

复制代码

    def on_theme_changed(self, theme_type):

        """主题变化的事件处理"""

        

        theme_map = {

            1: "light",

            2: "dark",

            0: "" #不设置主题,默认使用系统主题

        } 

        theme_name = theme_map[self.theme_type]

 

        self.log.info(f"主题变化: {theme_type}:{theme_name}")

        # 切换主题颜色

        ThemedHelper.apply(theme_type)

复制代码

这个主题的辅助类,主要就是根据当前总线的样式值,加载对应的样式文件进行设置,如果为空,这还原为最初的默认样式。

 

复制代码

class ThemedHelper:

    """控件主题辅助类"""

 

    @staticmethod

    def apply(theme_type: int = 0):

        """应用主题样式"""

        theme_name = ""

        if theme_type == 1:

            theme_name= "light"

        elif theme_type == 2:

            theme_name= "dark" 

        else:

            theme_name = ""

            

        if not theme_name:

            qss = "" # 自动主题, 无需加载 qss 文件

        else:

            with open(f"app/themes/{theme_name}.qss", encoding="utf8") as f:

                qss = f.read()

        

        app : QApplication = QApplication.instance()

        if app:

            app.setStyle("Fusion")

            app.setStyleSheet(qss)

复制代码

暗色主题的效果如下所示。

 

image

 

 如果不喜欢厚重的主题,我们也可以切换会原来的默认主题。

 

image

 

以上就是我们在定义不同主题,实现主题切换的过程,我们可以根据需要,定义更多有特色的主题样式,而具有统一效果的主题样式,我们可以通过AI的询问方式,获得完整的样式代码,从而构建个性化的效果。

 

 

 

 专注于代码生成工具、.Net/Python 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架、Python开发框架等框架产品。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值