import os import threading import logging from PIL import Image import pystray import config import fetcher import wallpaper logger = logging.getLogger(__name__) STYLES = ["fill", "fit", "stretch", "tile", "center"] STYLE_LABELS = {"fill": "填充", "fit": "适应", "stretch": "拉伸", "tile": "平铺", "center": "居中"} def _get_icon_path(): return os.path.join(config._get_app_dir(), "assets", "icon.ico") class TrayApp: def __init__(self): self._icon = None self._auto_timer = None self._running = False def run(self): self._running = True image = Image.open(_get_icon_path()) self._icon = pystray.Icon( "WallpaperChanger", image, "壁纸更换器", menu=self._build_menu(), ) self._icon.title = "壁纸更换器" if config.get("auto_refresh.enabled"): self._start_auto_refresh() self._icon.run() def stop(self): self._running = False self._stop_auto_refresh() if self._icon: self._icon.stop() def _build_menu(self): show_notify = config.get("wallpaper.show_notification", True) notify_label = "弹出通知: 开" if show_notify else "弹出通知: 关" return pystray.Menu( pystray.MenuItem("刷新壁纸", self._on_refresh, default=True), pystray.MenuItem("自动刷新", self._auto_refresh_submenu()), pystray.MenuItem("填充方式", self._fill_style_submenu()), pystray.Menu.SEPARATOR, pystray.MenuItem(notify_label, self._on_toggle_notification), pystray.Menu.SEPARATOR, pystray.MenuItem("安装右键菜单", self._on_install_menu), pystray.MenuItem("卸载右键菜单", self._on_uninstall_menu), pystray.Menu.SEPARATOR, pystray.MenuItem("退出", self._on_exit), ) def _auto_refresh_submenu(self): enabled = config.get("auto_refresh.enabled", False) label = "状态: 开启" if enabled else "状态: 关闭" return pystray.Menu( pystray.MenuItem(label, None), pystray.Menu.SEPARATOR, pystray.MenuItem("开启", self._on_auto_on), pystray.MenuItem("关闭", self._on_auto_off), ) def _fill_style_submenu(self): items = [] for s in STYLES: items.append( pystray.MenuItem( STYLE_LABELS[s], self._on_set_fill_style, checked=lambda item, _s=s: _s == config.get("wallpaper.fill_style", "fill"), ) ) return pystray.Menu(*items) def _on_refresh(self, icon, item=None): threading.Thread(target=self._do_refresh, daemon=True).start() def _do_refresh(self): try: image_path = fetcher.fetch_wallpaper() fill_style = config.get("wallpaper.fill_style", "fill") ok = wallpaper.set_wallpaper(image_path, fill_style) if ok and self._icon: if config.get("wallpaper.show_notification", True): self._icon.notify("壁纸已更新") except Exception as e: logger.exception("Refresh failed") if self._icon and config.get("wallpaper.show_notification", True): self._icon.notify(f"刷新失败: {e}") def _on_auto_on(self, icon, item): config.set_("auto_refresh.enabled", True) self._start_auto_refresh() icon.update_menu() def _on_auto_off(self, icon, item): config.set_("auto_refresh.enabled", False) self._stop_auto_refresh() icon.update_menu() def _on_set_fill_style(self, icon, item): for s in STYLES: if STYLE_LABELS[s] == str(item): config.set_("wallpaper.fill_style", s) break icon.update_menu() def _on_toggle_notification(self, icon, item): current = config.get("wallpaper.show_notification", True) config.set_("wallpaper.show_notification", not current) icon.update_menu() def _on_install_menu(self, icon, item): threading.Thread(target=self._do_install_menu, daemon=True).start() def _do_install_menu(self): try: from install_menu import install install() if self._icon: self._icon.notify("右键菜单已安装") except Exception as e: logger.exception("Install menu failed") if self._icon: self._icon.notify(f"安装失败: {e}") def _on_uninstall_menu(self, icon, item): threading.Thread(target=self._do_uninstall_menu, daemon=True).start() def _do_uninstall_menu(self): try: from install_menu import uninstall uninstall() if self._icon: self._icon.notify("右键菜单已卸载") except Exception as e: logger.exception("Uninstall menu failed") if self._icon: self._icon.notify(f"卸载失败: {e}") def _on_exit(self, icon, item): self.stop() def _start_auto_refresh(self): self._stop_auto_refresh() interval = config.get("auto_refresh.interval_minutes", 10) * 60 self._auto_timer = threading.Timer(interval, self._auto_refresh_tick) self._auto_timer.daemon = True self._auto_timer.start() logger.info("Auto refresh started, interval=%ds", interval) def _stop_auto_refresh(self): if self._auto_timer: self._auto_timer.cancel() self._auto_timer = None def _auto_refresh_tick(self): if not self._running: return self._do_refresh() if config.get("auto_refresh.enabled"): interval = config.get("auto_refresh.interval_minutes", 10) * 60 self._auto_timer = threading.Timer(interval, self._auto_refresh_tick) self._auto_timer.daemon = True self._auto_timer.start()