PySide6 实现win10 手动与自动切换主题 借助系统托盘
文章目录
- 实现win10 手动与自动切换主题
- 为什么写这篇文章?
- 一些需要知道的知识
- 系统托盘的实现
- 效果演示
- 整体代码
- auto_choose_them.py
- one_day.py
- windows_regedit.py
- 展望
实现win10 手动与自动切换主题
为什么写这篇文章?
笔者以前使用代码实现过win10自动与手动切换主题,但是没有实现过带界面的,这次是弥补以前的不足。
常见的后台应用(如:搜狗输入法)都会设置有托盘(QSystemTrayIcon)这里就借助托盘,让程序更像在后台一直运行一样!
一些需要知道的知识
- 怎么通过代码修改
win10的主题?
答:其实是借用winreg(Python内置库)来修改注册表文件 - 怎么实现自动切换主题?
答: 获取当地的日出日落时间,在日出之后日落之前亮色;其他暗色 - 怎么实现系统托盘?下面详解
系统托盘的实现
见相关代码:
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
# 这是一个 win10的自动切换 主题的工具
# 根据当地 日升 日落 时间来设置 对应的主题
# 需要主界面与应用窗口
from PySide6.QtWidgets import (QWidget,QVBoxLayout,QCheckBox,QSystemTrayIcon,QMenu,QApplication)
from PySide6.QtGui import QIcon
import sysfrom windows_regedit import WinSystemThem
from one_day import OneDayclass 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
import time
import requests
from datetime import datetimeclass OneDay:def __init__(self):self.lat,self.lon,self.city = None,None,Noneself.sunrise_hour,self.sunrise_minute = None,Noneself.sunset_hour,self.sunset_minute = None,Nonedef __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, Noneexcept:return None, None, Nonedef __get_sunrise_sunset(self):if self.lat:lat,lon = self.lat,self.lonelse: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 UTCsunset_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.hoursunrise_minute = sunrise_local.minutesunset_hour = sunset_local.hoursunset_minute = sunset_local.minuteself.sunrise_hour,self.sunrise_minute,self.sunset_hour,self.sunset_minute = sunrise_hour, sunrise_minute, sunset_hour, sunset_minutereturn sunrise_hour, sunrise_minute, sunset_hour, sunset_minutereturn None, None,None,Nonedef 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_hourcurrent_minute = local_time.tm_minreturn (current_hour,current_minute)
windows_regedit.py
import winregclass 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 0else:return 1def 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方法等待优化!
