import queue import logging import pystray from PIL import Image, ImageDraw logger = logging.getLogger(__name__) _COLORS = {"dp": "#1565C0", "hdmi": "#B71C1C", "default": "#455A64"} def _make_icon(label: str, color: str) -> Image.Image: img = Image.new("RGBA", (64, 64), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) draw.ellipse([2, 2, 62, 62], fill=color) text = label[:2].upper() bbox = draw.textbbox((0, 0), text) tw = bbox[2] - bbox[0] th = bbox[3] - bbox[1] draw.text(((64 - tw) // 2, (64 - th) // 2), text, fill="white") return img def _color_for(profile_id: str) -> str: if "dp" in profile_id: return _COLORS["dp"] if "hdmi" in profile_id: return _COLORS["hdmi"] return _COLORS["default"] class TrayApp: def __init__(self, config_manager, switcher, settings_queue: queue.Queue): self._config_manager = config_manager self._switcher = switcher self._settings_queue = settings_queue self._icon: pystray.Icon | None = None switcher.on_switch(self._on_profile_switched) def _build_menu(self) -> pystray.Menu: config = self._config_manager.get() items = [] for profile in config.get("profiles", []): pid = profile["id"] name = profile["name"] items.append( pystray.MenuItem( name, lambda _, p=pid: self._switcher.apply_profile(p), checked=lambda item, p=pid: ( self._config_manager.get_active_profile_id() == p ), radio=True, ) ) items.append(pystray.Menu.SEPARATOR) items.append(pystray.MenuItem("Ustawienia", self._request_settings)) items.append(pystray.MenuItem("Wyjscie", self._exit)) return pystray.Menu(*items) def _on_profile_switched(self, profile_id: str) -> None: if self._icon is None: return profile = self._config_manager.get_profile(profile_id) label = profile["name"][:2] if profile else "??" title = profile["name"] if profile else profile_id self._icon.icon = _make_icon(label, _color_for(profile_id)) self._icon.title = f"MonitorSwitcher \u2014 {title}" self._icon.update_menu() def _request_settings(self, icon=None, item=None) -> None: self._settings_queue.put("open_settings") def _exit(self, icon=None, item=None) -> None: self._settings_queue.put("exit") if self._icon: self._icon.stop() def run_tray(self) -> None: active_id = self._config_manager.get_active_profile_id() profile = self._config_manager.get_profile(active_id) label = profile["name"][:2] if profile else "MS" title = f"MonitorSwitcher \u2014 {profile['name']}" if profile else "MonitorSwitcher" self._icon = pystray.Icon( "MonitorSwitcher", _make_icon(label, _color_for(active_id)), title=title, menu=self._build_menu(), ) self._icon.run()