PySide6 实现win10 手动与自动切换主题 借助系统托盘

文章目录

实现win10 手动与自动切换主题

为什么写这篇文章?

笔者以前使用代码实现过win10自动与手动切换主题,但是没有实现过带界面的,这次是弥补以前的不足。

常见的后台应用(如:搜狗输入法 )都会设置有托盘(QSystemTrayIcon)这里就借助托盘,让程序更像在后台一直运行一样!

一些需要知道的知识

  1. 怎么通过代码修改win10的主题?
    答:其实是借用winregPython内置库)来修改注册表文件
  2. 怎么实现自动切换主题?
    答: 获取当地的日出日落时间,在日出之后日落之前亮色 ;其他暗色
  3. 怎么实现系统托盘?下面详解

系统托盘的实现

见相关代码:

py 复制代码
    def setup_system_tray(self):
        """设置系统托盘
        """
        # 
        system_tray = QSystemTrayIcon(self)
        system_tray.setIcon(QIcon("./images/dicos.ico"))
        system_tray.setToolTip("自动切换主题")

        tray_menu = QMenu()
        
        self.__change = tray_menu.addAction("切换主题")
        self.__exit  = tray_menu.addAction("退出")

        system_tray.setContextMenu(tray_menu)
        system_tray.show()

注意win10图标没有正确配置则不会显示托盘,linux 会显示三个点的图标,macOS条件有限待更新

按照如上代码就可以实现一个系统托盘

小知识 :在 Windows 上,系统托盘图标大小为 16x16;在 X11 上,首选尺寸为 22x22

效果演示

PySide6 win10 手动与自动切换主题 借助系统托盘

整体代码

│- auto_choose_them.py PySide6窗口QWidget与系统托盘QSystemTrayIcon

│ - one_day.py 一天的时间相关,当前时间、日出日落时间

│ - windows_regedit.py 用于操作windows注册表

└─images

dicos.ico 托盘图标

auto_choose_them.py

py 复制代码
# 这是一个 win10的自动切换 主题的工具  
# 根据当地 日升 日落 时间来设置 对应的主题
# 需要主界面与应用窗口
from PySide6.QtWidgets import (QWidget,QVBoxLayout,QCheckBox,QSystemTrayIcon,QMenu,QApplication)
from PySide6.QtGui import QIcon
import sys

from windows_regedit import WinSystemThem
from one_day import OneDay


class AutoThem(QWidget):
    """自动切换主题
    """
    def __init__(self):
        """初始化
        """
        super().__init__()
        self.setup_ui()
        self.setup_system_tray()
        self.set_event_bind()
    
    def setup_ui(self):
        """设置界面
        """
        # 窗口使用垂直布局
        vbox = QVBoxLayout(self)

        # 创建勾选框
        self.__enable_checkbox = QCheckBox("开机自启动")
        self.__auto_checkbox = QCheckBox("自动选择")
        
        # 添加到垂直布局中
        vbox.addWidget(self.__enable_checkbox)
        vbox.addWidget(self.__auto_checkbox)

    def setup_system_tray(self):
        """设置系统托盘
        """
        # 
        system_tray = QSystemTrayIcon(self)
        system_tray.setIcon(QIcon("./images/dicos.ico"))
        system_tray.setToolTip("自动切换主题")

        tray_menu = QMenu()
        
        self.__change = tray_menu.addAction("切换主题")
        self.__exit  = tray_menu.addAction("退出")

        system_tray.setContextMenu(tray_menu)
        system_tray.show()
        
    def set_event_bind(self):
        """设置事件绑定
        """
        # 主界面
        self.__auto_checkbox.checkStateChanged.connect(self.auto_choose_theme)

        # 托盘 上下文菜单
        self.__change.triggered.connect(lambda: WinSystemThem().reset_windows_theme())
        self.__exit.triggered.connect(self.close)

    def auto_choose_theme(self):
        """自动选择主题
        """
        one_day = OneDay()
        hours,minutes  = one_day.get_current_time()
        sunrise_hour,sunrise_minute = one_day.get_sunrise()
        sunset_hour,sunset_minute = one_day.get_sunset()
        # 日升之后 落入之前 亮色 二分法 先比小时 再比分钟
        if hours >= sunrise_hour and hours <= sunset_hour:
            if sunrise_minute >= minutes or minutes <= sunset_minute:
                WinSystemThem().set_system_theme(1)
        else:
              WinSystemThem().set_system_theme(0)      
        
if __name__ == "__main__":
    app = QApplication(sys.argv)
    auto_them = AutoThem()
    auto_them.show()
    sys.exit(app.exec())

one_day.py

py 复制代码
import time
import requests
from datetime import datetime


class OneDay:
    def __init__(self):
        self.lat,self.lon,self.city =  None,None,None
        self.sunrise_hour,self.sunrise_minute = None,None
        self.sunset_hour,self.sunset_minute = None,None

    def __get_location_by_ip(self) -> tuple[float,float,str]:
        """获取当前位置
        """
        try:
            response = requests.get("http://ip-api.com/json/", timeout=5)
            data = response.json()
            if data["status"] == "success":
                self.lat,self.lon,self.city = data["lat"], data["lon"], data["city"]
                return (data["lat"], data["lon"], data["city"])
            else:
                return None, None, None
        except:
            return None, None, None

    def __get_sunrise_sunset(self):
        if self.lat:
            lat,lon = self.lat,self.lon
        else:
            lat,lon,city = self.__get_location_by_ip()
        url = f"https://api.sunrise-sunset.org/json?lat={lat}&lng={lon}&date=today&formatted=0"
        response = requests.get(url, timeout=5)
        data = response.json()
        if data["status"] == "OK":
            sunrise_str = data["results"]["sunrise"]  # ISO 8601 UTC
            sunset_str = data["results"]["sunset"]
            
                        # 解析 ISO 时间字符串
            sunrise_utc = datetime.fromisoformat(sunrise_str.replace("Z", "+00:00"))
            sunset_utc = datetime.fromisoformat(sunset_str.replace("Z", "+00:00"))

            # 转换为本地时区
            sunrise_local = sunrise_utc.astimezone()
            sunset_local = sunset_utc.astimezone()

            # 提取小时和分钟为整数
            sunrise_hour = sunrise_local.hour
            sunrise_minute = sunrise_local.minute
            sunset_hour = sunset_local.hour
            sunset_minute = sunset_local.minute
            self.sunrise_hour,self.sunrise_minute,self.sunset_hour,self.sunset_minute = sunrise_hour, sunrise_minute, sunset_hour, sunset_minute
            return sunrise_hour, sunrise_minute, sunset_hour, sunset_minute
        return None, None,None,None

    def get_sunrise(self):
        """获取日出事件
        """
        if self.sunrise_hour:
            return (self.sunrise_hour,self.sunrise_minute) 
        else:
            return (self.__get_sunrise_sunset()[0],self.__get_sunrise_sunset()[1])

    def get_sunset(self):
        """获取日落事件
        """
        if self.sunrise_hour:
            return (self.sunset_hour,self.sunset_minute)
        else:
            return (self.__get_sunrise_sunset()[2],self.__get_sunrise_sunset()[3])

    def get_current_time(self) -> tuple[int,int] :
        """获取当前时间
        """
        # 获取当前时间结构
        local_time = time.localtime()

        # 获取当前小时和分钟
        current_hour = local_time.tm_hour
        current_minute = local_time.tm_min
        return (current_hour,current_minute)

windows_regedit.py

py 复制代码
import winreg

class WinSystemThem:
    def __init__(self):
        self.__key =  winreg.OpenKey(
            winreg.HKEY_CURRENT_USER, 
            r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
            0, 
            winreg.KEY_READ | winreg.KEY_WRITE
        )

    def __del__(self):
        """对象销毁时关闭注册表键
        """
        if hasattr(self, '__key') and self.__key:
            winreg.CloseKey(self.__key)

    def get_system_theme(self):
        """获取系统主题

        :return: 0 dark ; 1 light
        """
        apps_use_light_theme = winreg.QueryValueEx(self.__key , "AppsUseLightTheme")[0]
        system_use_light_theme = winreg.QueryValueEx(self.__key , "SystemUsesLightTheme")[0]
        # 如果两个值都是0,表示暗色模式
        if apps_use_light_theme == 0 and system_use_light_theme == 0:
            return 0
        else:
            return 1
        
    def set_system_theme(self,module:bool):
        """设置系统主题

        :param module: 0 dark; 1 light
        """
        winreg.SetValueEx(self.__key, "AppsUseLightTheme", 0, winreg.REG_DWORD, module)
        winreg.SetValueEx(self.__key, "SystemUsesLightTheme", 0, winreg.REG_DWORD, module)

    def reset_windows_theme(self):
        """
        重新设置主题为亮色或者按钮
        """
        
        module = not self.get_system_theme()
        self.set_system_theme(module)
        winreg.CloseKey(self.__key)

展望

  • 开机自启动待实现
  • auto_choose_theme方法等待优化!
相关推荐
深蓝海拓19 小时前
PySide6从0开始学习的笔记(二十七) 日志管理
笔记·python·学习·pyqt
极客小云2 天前
【基于AI的自动商品试用系统:不仅仅是虚拟试衣!】
javascript·python·django·flask·github·pyqt·fastapi
进击切图仔3 天前
集成类 pyqt 项目构造流程
pyqt
SNAKEpc121383 天前
PyQtGraph应用(五):k线回放复盘功能实现
python·qt·pyqt
开开心心就好4 天前
开源免费高速看图工具,支持漫画大图秒开
linux·运维·服务器·安全·ruby·symfony·1024程序员节
Warren984 天前
Pytest Fixture 到底该用 return 还是 yield?
数据库·oracle·面试·职场和发展·单元测试·pytest·pyqt
Ulyanov5 天前
顶层设计——单脉冲雷达仿真器的灵魂蓝图
python·算法·pyside·仿真系统·单脉冲
深蓝海拓6 天前
PySide6从0开始学习的笔记(二十六) 重写Qt窗口对象的事件(QEvent)处理方法
笔记·python·qt·学习·pyqt
深蓝海拓6 天前
PySide6从0开始学习的笔记(二十五) Qt窗口对象的生命周期和及时销毁
笔记·python·qt·学习·pyqt
unable code7 天前
磁盘取证-Flying_High
网络安全·ctf·misc·1024程序员节·磁盘取证