from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QApplication from PyQt6.QtGui import QAction from PyQt6.QtCore import QTimer from app.core.config import Config from app.core import wireguard as wg_core from app.ui import icons class SystemTray(QSystemTrayIcon): def __init__(self, config: Config, main_window, parent=None): super().__init__(icons.icon_disconnected(), parent) self._cfg = config self._win = main_window self._connected = False self._build_menu() self.setToolTip("WGSecure — Déconnecté") self.activated.connect(self._on_activated) self._poll = QTimer(parent) self._poll.timeout.connect(self._update_status) self._poll.start(5000) self._update_status() def _build_menu(self): menu = QMenu() # Ligne de statut (désactivée) self._action_status = QAction("● Déconnecté", self) self._action_status.setEnabled(False) menu.addAction(self._action_status) menu.addSeparator() # Afficher la fenêtre self._action_show = QAction(icons.icon_app(), "Afficher la fenêtre", self) self._action_show.triggered.connect(self._show_window) menu.addAction(self._action_show) menu.addSeparator() # Connexion / déconnexion self._action_toggle = QAction(icons.icon_disconnected(), "Se connecter", self) self._action_toggle.triggered.connect(self._toggle_connection) menu.addAction(self._action_toggle) menu.addSeparator() # Panneau de configuration self._action_config = QAction(icons.icon_admin(), "Configuration", self) self._action_config.triggered.connect(self._win._open_admin) menu.addAction(self._action_config) menu.addSeparator() # Quitter self._action_quit = QAction("Quitter", self) self._action_quit.triggered.connect(self._quit) menu.addAction(self._action_quit) self.setContextMenu(menu) def _on_activated(self, reason: QSystemTrayIcon.ActivationReason): if reason == QSystemTrayIcon.ActivationReason.Trigger: self._show_window() def _show_window(self): self._win.show() self._win.raise_() self._win.activateWindow() def _update_status(self): prev = self._connected try: self._connected = wg_core.is_connected(self._cfg) except Exception: self._connected = False if self._connected: self.setIcon(icons.icon_connected()) self.setToolTip("WGSecure — Connecté") self._action_status.setText("● Connecté") self._action_toggle.setIcon(icons.icon_connected()) self._action_toggle.setText("Se déconnecter") if not prev: srv = self._cfg.wg.get("server_endpoint", "") self.showMessage( "WGSecure — Connecté ✓", f"Tunnel WireGuard actif{(' → ' + srv) if srv else ''}", QSystemTrayIcon.MessageIcon.Information, 3000, ) else: self.setIcon(icons.icon_disconnected()) self.setToolTip("WGSecure — Déconnecté") self._action_status.setText("● Déconnecté") self._action_toggle.setIcon(icons.icon_disconnected()) self._action_toggle.setText("Se connecter") if prev: self.showMessage( "WGSecure — Déconnecté", "Le tunnel WireGuard a été désactivé.", QSystemTrayIcon.MessageIcon.Warning, 3000, ) def _toggle_connection(self): self._win._on_connect() self._update_status() def _quit(self): self._poll.stop() QApplication.quit()