From e3d2229f7f2f259c669002f2998d283e2ac002cd Mon Sep 17 00:00:00 2001 From: Konukhov Yaroslav Date: Fri, 8 May 2026 08:54:30 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=B5=D0=BB=D0=BA=D0=B8=D0=B5=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D1=8B=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=20=D0=B8=20=D0=BF=D0=B0=D1=80=D0=B0=20=D0=B1=D0=B0?= =?UTF-8?q?=D0=B3=D0=BE=D0=B2=20(#805)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- linux.py | 23 +++++++++++++++++++++-- ui/ctk_tray_ui.py | 27 +++++++++++++++++++-------- utils/update_check.py | 2 +- windows.py | 31 +++++++++++++++++++++++++++---- 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/linux.py b/linux.py index 45b6eda..8c2843e 100644 --- a/linux.py +++ b/linux.py @@ -156,21 +156,40 @@ def _edit_config_dialog() -> None: scroll, footer = tray_settings_scroll_and_footer(ctk, frame, theme) widgets = install_tray_config_form(ctk, scroll, theme, cfg, DEFAULT_CONFIG, show_autostart=False) + _original_appearance = ctk.get_appearance_mode() + def _finish() -> None: root.destroy() done.set() + def _cancel() -> None: + ctk.set_appearance_mode(_original_appearance) + _finish() + def on_save() -> None: from tkinter import messagebox merged = validate_config_form(widgets, DEFAULT_CONFIG, include_autostart=False) if isinstance(merged, str): messagebox.showerror("TG WS Proxy — Ошибка", merged, parent=root) return + + _ui_only_keys = {"appearance", "check_updates"} + config_changed = any(merged.get(k) != cfg.get(k) for k in merged) + proxy_changed = any(merged.get(k) != cfg.get(k) for k in merged if k not in _ui_only_keys) + + if not config_changed: + _finish() + return + save_config(merged) _config.update(merged) log.info("Config saved: %s", merged) _tray_icon.menu = _build_menu() + if not proxy_changed: + _finish() + return + do_restart = messagebox.askyesno( "Перезапустить?", "Настройки сохранены.\n\nПерезапустить прокси сейчас?", @@ -180,8 +199,8 @@ def _edit_config_dialog() -> None: if do_restart: threading.Thread(target=lambda: restart_proxy(_config, _show_error), daemon=True).start() - root.protocol("WM_DELETE_WINDOW", _finish) - install_tray_config_buttons(ctk, footer, theme, on_save=on_save, on_cancel=_finish) + root.protocol("WM_DELETE_WINDOW", _cancel) + install_tray_config_buttons(ctk, footer, theme, on_save=on_save, on_cancel=_cancel) ctk_run_dialog(_build) diff --git a/ui/ctk_tray_ui.py b/ui/ctk_tray_ui.py index eb80822..6026c45 100644 --- a/ui/ctk_tray_ui.py +++ b/ui/ctk_tray_ui.py @@ -330,6 +330,7 @@ def install_tray_config_form( def _on_appearance_change(choice: str) -> None: cfg_val = _APPEARANCE_TO_CFG.get(choice, "auto") ctk.set_appearance_mode(_APPEARANCE_TO_CTK[cfg_val]) + cfg["appearance"] = cfg_val ctk.CTkComboBox( header, @@ -444,17 +445,27 @@ def install_tray_config_form( import threading as _threading if user_domain: def _worker(): - res = _run_cfproxy_connectivity_test(user_domain) - if btn: - btn.after(0, lambda: btn.configure(text="Тест", state="normal")) - btn.after(0, lambda: _cfproxy_show_test_results(user_domain, res)) + try: + res = _run_cfproxy_connectivity_test(user_domain) + if btn: + btn.after(0, lambda: _cfproxy_show_test_results(user_domain, res)) + except Exception as exc: + log.error("CF proxy test failed: %s", exc) + finally: + if btn: + btn.after(0, lambda: btn.configure(text="Тест", state="normal")) _threading.Thread(target=_worker, daemon=True).start() else: def _worker_auto(): - ok_domain, res = _run_cfproxy_auto_test(balancer.domains) - if btn: - btn.after(0, lambda: btn.configure(text="Тест", state="normal")) - btn.after(0, lambda: _cfproxy_show_auto_test_results(ok_domain, res)) + try: + ok_domain, res = _run_cfproxy_auto_test(balancer.domains) + if btn: + btn.after(0, lambda: _cfproxy_show_auto_test_results(ok_domain, res)) + except Exception as exc: + log.error("CF proxy auto-test failed: %s", exc) + finally: + if btn: + btn.after(0, lambda: btn.configure(text="Тест", state="normal")) _threading.Thread(target=_worker_auto, daemon=True).start() _cf_test_widget = ctk.CTkButton( diff --git a/utils/update_check.py b/utils/update_check.py index ac8c739..97f8062 100644 --- a/utils/update_check.py +++ b/utils/update_check.py @@ -73,7 +73,7 @@ def _parse_version_tuple(s: str) -> tuple: return (0,) parts = [] for seg in s.split("."): - digits = "".join(c for c in seg if c.isdigit()) + digits = next((seg[:i] for i, c in enumerate(seg) if not c.isdigit()), seg) if digits: try: parts.append(int(digits)) diff --git a/windows.py b/windows.py index 0430781..4ccc843 100644 --- a/windows.py +++ b/windows.py @@ -436,7 +436,11 @@ def _on_edit_config(icon=None, item=None) -> None: def _on_open_logs(icon=None, item=None) -> None: log.info("Opening log file: %s", LOG_FILE) if LOG_FILE.exists(): - os.startfile(str(LOG_FILE)) + try: + os.startfile(str(LOG_FILE)) + except Exception as exc: + log.error("Failed to open log file: %s", exc) + _show_error(f"Не удалось открыть файл логов:\n{exc}") else: _show_info("Файл логов ещё не создан.") @@ -485,16 +489,31 @@ def _edit_config_dialog() -> None: autostart_value=cfg.get("autostart", False), ) + _original_appearance = ctk.get_appearance_mode() + def _finish() -> None: root.destroy() done.set() + def _cancel() -> None: + ctk.set_appearance_mode(_original_appearance) + _finish() + def on_save() -> None: from tkinter import messagebox merged = validate_config_form(widgets, DEFAULT_CONFIG, include_autostart=_supports_autostart()) if isinstance(merged, str): messagebox.showerror("TG WS Proxy — Ошибка", merged, parent=root) return + + _ui_only_keys = {"appearance", "autostart", "check_updates"} + config_changed = any(merged.get(k) != cfg.get(k) for k in merged) + proxy_changed = any(merged.get(k) != cfg.get(k) for k in merged if k not in _ui_only_keys) + + if not config_changed: + _finish() + return + save_config(merged) _config.update(merged) log.info("Config saved: %s", merged) @@ -502,6 +521,10 @@ def _edit_config_dialog() -> None: set_autostart_enabled(bool(merged.get("autostart", False))) _tray_icon.menu = _build_menu() + if not proxy_changed: + _finish() + return + do_restart = messagebox.askyesno( "Перезапустить?", "Настройки сохранены.\n\nПерезапустить прокси сейчас?", @@ -511,8 +534,8 @@ def _edit_config_dialog() -> None: if do_restart: threading.Thread(target=lambda: restart_proxy(_config, _show_error), daemon=True).start() - root.protocol("WM_DELETE_WINDOW", _finish) - install_tray_config_buttons(ctk, footer, theme, on_save=on_save, on_cancel=_finish) + root.protocol("WM_DELETE_WINDOW", _cancel) + install_tray_config_buttons(ctk, footer, theme, on_save=on_save, on_cancel=_cancel) ctk_run_dialog(_build) @@ -578,7 +601,7 @@ def run_tray() -> None: _config = load_config() - if is_windows_dark_theme: + if is_windows_dark_theme(): apply_windows_dark_theme() bootstrap(_config)