From d0aed9e2537f31841dee281b1e11b71cab8915e8 Mon Sep 17 00:00:00 2001 From: punix Date: Mon, 5 May 2025 20:42:19 +0200 Subject: [PATCH] optimize performance 03-05-2025 --- .vscode/settings.json | 3 + __pycache__/cls_mth_fc.cpython-312.pyc | Bin 26419 -> 29714 bytes __pycache__/wp_app_config.cpython-312.pyc | Bin 8928 -> 9170 bytes cls_mth_fc.py | 133 +++++-- wirepy.py | 443 ++++++++++++++-------- wp_app_config.py | 6 + 6 files changed, 390 insertions(+), 195 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c64554..34dfa2f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { "workbench.settings.openDefaultSettings": true + "workbench.startupEditor": "none" + "update.showReleaseNotes": false + "terminal.integrated.fontSize": 22 } \ No newline at end of file diff --git a/__pycache__/cls_mth_fc.cpython-312.pyc b/__pycache__/cls_mth_fc.cpython-312.pyc index b250738e7093043a4d3def650f62d29f7bbcd3d5..d1ce12316f634408ba823cf598851ce560a9e523 100644 GIT binary patch delta 5460 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 WkvDSWojkbBe#n!|@=ZdB@B9a5{IjC~ diff --git a/cls_mth_fc.py b/cls_mth_fc.py index cb89a1d..ba9cb42 100755 --- a/cls_mth_fc.py +++ b/cls_mth_fc.py @@ -169,18 +169,6 @@ class LxTools(tk.Tk): if file is not None: Path.unlink(file) - @staticmethod - def if_tip(path: Path) -> bool: - """ - method that writes in file whether tooltip is displayed or not - """ - lines = Path(path).read_text(encoding="utf-8") - if "False\n" in lines: - tip = False - else: - tip = True - return tip - @staticmethod def msg_window(image_path: Path, image_path2: Path, w_title: str, w_txt: str, txt2: Optional[str] = None, com: Optional[str] = None) -> None: @@ -382,6 +370,82 @@ class Tunnel: pass +# ConfigManager with caching +class ConfigManager: + """ + Universal class for managing configuration files with caching. + Can be reused in different projects. + """ + _config = None + _config_file = None + + @classmethod + def init(cls, config_file): + """Initial the Configmanager with the given config file""" + cls._config_file = config_file + cls._config = None # Reset the cache + + @classmethod + def load(cls): + """Load the config file and return the config as dict""" + if not cls._config: + try: + lines = Path(cls._config_file).read_text(encoding="utf-8").splitlines() + cls._config = { + 'updates': lines[1].strip(), + 'theme': lines[3].strip(), + 'tooltips': lines[5].strip() == 'True', + 'autostart': lines[7].strip() if len(lines) > 7 else 'off' + } + except (IndexError, FileNotFoundError): + # DeDefault values in case of error + cls._config = { + 'updates': 'on', + 'theme': 'light', + 'tooltips': True, + 'autostart': 'off' + } + return cls._config + + @classmethod + def save(cls): + """Save the config to the config file""" + if cls._config: + lines = [ + '# Configuration\n', + f"{cls._config['updates']}\n", + '# Theme\n', + f"{cls._config['theme']}\n", + '# Tooltips\n', + f"{str(cls._config['tooltips'])}\n", + '# Autostart\n', + f"{cls._config['autostart']}\n" + ] + Path(cls._config_file).write_text(''.join(lines), encoding="utf-8") + + @classmethod + def set(cls, key, value): + """Sets a configuration value and saves the change""" + cls.load() + cls._config[key] = value + cls.save() + + @classmethod + def get(cls, key, default=None): + """Returns a configuration value""" + config = cls.load() + return config.get(key, default) + + +class ThemeManager: + @staticmethod + def change_theme(root, theme_in_use, theme_name=None): + """Change application theme centrally""" + root.tk.call("set_theme", theme_in_use) + if theme_in_use == theme_name: + ConfigManager.set("theme", theme_in_use) + + class GiteaUpdate: """ Calling download requests the download URL of the running script, @@ -390,35 +454,46 @@ class GiteaUpdate: """ @staticmethod - def api_down(update_api_url: str, version: str, file: Optional[Path] = None) -> str: + def api_down(update_api_url: str, version: str, update_setting: str = None) -> str: """ Checks for updates via API - + Args: update_api_url: Update API URL version: Current version - file: Optional - Configuration file + update_setting: Update setting from ConfigManager (on/off) Returns: New version or status message """ + # If updates are disabled, return immediately + if update_setting != "on": + return "False" + try: response: requests.Response = requests.get(update_api_url, timeout=10) - response_dict: Any = response.json() - response_dict: Dict[str, Any] = response_dict[0] - with open(file, "r", encoding="utf-8") as set_f: - set_f = set_f.read() - if "on\n" in set_f: - if version[3:] != response_dict["tag_name"]: - req: str = response_dict["tag_name"] - else: - req: str = "No Updates" - else: - req: str = "False" - return req + response.raise_for_status() # Raise exception for HTTP errors + + response_data = response.json() + if not response_data: + return "No Updates" + + latest_version = response_data[0].get("tag_name") + if not latest_version: + return "Invalid API Response" + + # Compare versions (strip 'v. ' prefix if present) + current_version = version[3:] if version.startswith("v. ") else version + + if current_version != latest_version: + return latest_version + else: + return "No Updates" + except requests.exceptions.RequestException: - req: str = "No Internet Connection!" - return req + return "No Internet Connection!" + except (ValueError, KeyError, IndexError): + return "Invalid API Response" @staticmethod def download(urld: str, res: str, image_path: Path = None, image_path2: Path = None, image_path3: Path = None, diff --git a/wirepy.py b/wirepy.py index 101e3f8..181d298 100755 --- a/wirepy.py +++ b/wirepy.py @@ -14,17 +14,13 @@ from pathlib import Path from subprocess import check_call from tkinter import TclError, filedialog, ttk -from cls_mth_fc import (Create, GiteaUpdate, Tunnel, Tooltip, LxTools) +from cls_mth_fc import (ConfigManager, ThemeManager, Create, GiteaUpdate, Tunnel, Tooltip, LxTools) from wp_app_config import AppConfig, Msg LxTools.uos() Create.dir_and_files() Create.make_dir() Create.decrypt() -# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year -VERSION: str = "v. 2.04.1725" - -res = GiteaUpdate.api_down("https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases", VERSION, AppConfig.SETTINGS_FILE) class Wirepy(tk.Tk): """ @@ -43,22 +39,17 @@ class Wirepy(tk.Tk): self.geometry(f"{self.x_width}x{self.y_height}+{self.monitor_center_x}+{self.monitor_center_y}") self.columnconfigure(0, weight=1) self.rowconfigure(0, weight=1) - self.style = ttk.Style(self) self.tk.call("source", f"{AppConfig.SYSTEM_PATHS["tcl_path"]}/water.tcl") - lines = AppConfig.SETTINGS_FILE.read_text() - if "light\n" in lines: - self.tk.call("set_theme", "light") - else: - self.tk.call("set_theme", "dark") - + ConfigManager.init(AppConfig.SETTINGS_FILE) + theme = ConfigManager.get("theme") + ThemeManager.change_theme(self, theme) # Load the image file from the disk self.wg_icon = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"]) # Set it as the window icon self.iconphoto(True, self.wg_icon) - tips = LxTools.if_tip(AppConfig.SETTINGS_FILE) - FrameWidgets(self, tips_enabled=tips).grid() + FrameWidgets(self).grid() class FrameWidgets(ttk.Frame): @@ -68,47 +59,59 @@ class FrameWidgets(ttk.Frame): def __init__(self, container, tips_enabled=None, **kwargs): super().__init__(container, **kwargs) - self.tunnel = Tunnel() self.lb_tunnel = None self.btn_stst = None self.endpoint = None self.dns = None self.address = None self.auto_con = None + self.tips_enabled = tips_enabled + self.style = ttk.Style() self.wg_vpn_start = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_start"]) self.wg_vpn_stop = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_stop"]) self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"]) self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"]) self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"]) self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"]) - self.tips_enabled = tips_enabled if tips_enabled is not None else LxTools.if_tip(AppConfig.SETTINGS_FILE) + self.tips_enabled = tips_enabled if tips_enabled is not None else ConfigManager.get("tooltip") + # StringVar-Variables initialization + self.update_label = tk.StringVar() + self.update_tooltip = tk.StringVar() + self.update_foreground = tk.StringVar(value="red") + # Frame for Menu self.menu_frame = ttk.Frame(self) self.menu_frame.configure(relief="flat") self.menu_frame.grid(column=0, row=0, columnspan=4, sticky="w") # App Menu - self.version_lb = ttk.Label(self.menu_frame, text=VERSION) + self.version_lb = ttk.Label(self.menu_frame, text=AppConfig.VERSION) self.version_lb.config(font=("Ubuntu", 11), foreground="#00c4ff") self.version_lb.grid(column=0, row=0, rowspan=4, padx=10) - Tooltip(self.version_lb, f"Version: {VERSION[2:]}", self.tips_enabled) + Tooltip(self.version_lb, f"Version: {AppConfig.VERSION[2:]}", self.tips_enabled) self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options")) self.options_btn.grid(column=1, columnspan=1, row=0) Tooltip(self.options_btn, _("Click for Settings"), self.tips_enabled) - set_update = tk.IntVar() - set_tip = tk.BooleanVar() + self.set_update = tk.IntVar() + self.set_tip = tk.BooleanVar() self.settings = tk.Menu(self, relief="flat") self.options_btn.configure(menu=self.settings, style="Toolbutton") self.settings.add_checkbutton(label=_("Disable Updates"), - command=lambda: self.update_setting(set_update.get()), variable=set_update) - self.settings.add_checkbutton(label=_("Disable Tooltips"), - command=lambda: self.tooltip(set_tip.get()), variable=set_tip) - self.settings.add_command(label=_("Light"), command=self.theme_change_light) - self.settings.add_command(label=_("Dark"), command=self.theme_change_dark) + command=lambda: self.update_setting(self.set_update.get()), variable=self.set_update) + self.settings.add_command(label=_("Disable Tooltips"), + command=lambda: self.tooltip(self.set_tip.get()), variable=self.set_tip) + + self.updates_lb = ttk.Label(self.menu_frame) + res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates")) + self.update_ui_for_update(res) + # Label show dark or light + self.theme_label = tk.StringVar() + self.update_theme_label() + self.settings.add_command(label=self.theme_label.get(), command=self.on_theme_toggle) # About BTN Menu / Label self.about_btn = ttk.Button( @@ -116,49 +119,10 @@ class FrameWidgets(ttk.Frame): self.about_btn.grid(column=2, columnspan=2, row=0) self.readme = tk.Menu(self) - # Update and Tooltip Label - self.updates_lb = ttk.Label(self.menu_frame) - self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) # View Checkbox to enable or disable Tooltip - if tips: - set_tip.set(value=False) - else: - set_tip.set(value=True) + self.set_tip.set(value=not self.tips_enabled) - # View Checkbox for enable or disable Updates - if res == "False": - set_update.set(value=1) - self.updates_lb.configure(text=_("Update search off")) - - Tooltip(self.updates_lb, _("Updates you have disabled"), self.tips_enabled) - - elif res == "No Internet Connection!": - self.updates_lb.configure(text=_("No Server Connection!"), foreground="red") - elif res == "No Updates": - self.updates_lb.configure(text=_("No Updates")) - - Tooltip(self.updates_lb, _("Congratulations! Wire-Py is up to date"), self.tips_enabled) - - else: - set_update.set(value=0) - text = f"Update {res} {_("available!")}" - - # Update BTN Menu - self.update_btn = ttk.Menubutton(self.menu_frame, text=text) - self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) - - Tooltip(self.update_btn, _("Click to download new version"), self.tips_enabled) - - self.download = tk.Menu(self, relief="flat") - - self.update_btn.configure(menu=self.download, style="Toolbutton") - self.download.add_command( - label=_("Download"), - command=lambda: GiteaUpdate.download(f"https://git.ilunix.de/punix/Wire-Py/archive/{res}.zip", - res, AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"])) - - # Show active Tunnel self.a = Tunnel.active() # Label Frame 1 @@ -254,8 +218,11 @@ class FrameWidgets(ttk.Frame): # Button Export self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic, - command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], + command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0) + self.btn_exp.grid(column=0, row=3, padx=15, pady=8) if self.l_box.size() == 0: @@ -304,40 +271,97 @@ class FrameWidgets(ttk.Frame): self.on_off() - - @staticmethod - def update_setting(update_res) -> None: - """ - write off or on in file - Args: - update_res (int): argument that is passed contains 0 or 1 - """ - if update_res == 1: - lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) - lines[1] = 'off\n' - Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") - + # Update the labels based on the result + def update_ui_for_update(self, res): + """Update UI elements based on update check result""" + if res == "False": + self.set_update.set(value=1) + self.update_label.set(_("Update search off")) + self.update_tooltip.set(_("Updates you have disabled")) + self.update_foreground.set("red") + + # Remove update button if it exists + if hasattr(self, 'update_btn'): + self.update_btn.grid_forget() + + # Display the label + self.updates_lb.configure( + textvariable=self.update_label, + foreground=self.update_foreground.get() + ) + self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) + Tooltip(self.updates_lb, self.update_tooltip.get(), self.tips_enabled) + + elif res == "No Internet Connection!": + self.update_label.set(_("No Server Connection!")) + self.update_foreground.set("red") + + # Remove update button if it exists + if hasattr(self, 'update_btn'): + self.update_btn.grid_forget() + + # Display the label + self.updates_lb.configure( + textvariable=self.update_label, + foreground=self.update_foreground.get() + ) + self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) + + elif res == "No Updates": + self.update_label.set(_("No Updates")) + self.update_tooltip.set(_("Congratulations! Wire-Py is up to date")) + self.update_foreground.set("black") + + # Remove update button if it exists + if hasattr(self, 'update_btn'): + self.update_btn.grid_forget() + + # Display the label + self.updates_lb.configure( + textvariable=self.update_label, + foreground=self.update_foreground.get() + ) + self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) + Tooltip(self.updates_lb, self.update_tooltip.get(), self.tips_enabled) + else: - lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) - lines[1] = 'on\n' - Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") - - @staticmethod - def tooltip(tip) -> None: + self.set_update.set(value=0) + update_text = f"Update {res} {_('available!')}" + + # Remove the label if displayed + self.updates_lb.grid_forget() + + # Create or update the update button + if not hasattr(self, 'update_btn'): + # Create the update button if it doesn't exist yet + self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text) + self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) + Tooltip(self.update_btn, _("Click to download new version"), self.tips_enabled) + + self.download = tk.Menu(self, relief="flat") + self.update_btn.configure(menu=self.download, style="Toolbutton") + self.download.add_command( + label=_("Download"), + command=lambda: GiteaUpdate.download( + f"{AppConfig.DOWNLOAD_URL}/{res}.zip", + res, + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_vpn"], + AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"] + ) + ) +} + def tooltip(self, tip) -> None: """ - write True or False in a file + Aktualisiert die Tooltip-Einstellung im ConfigManager Args: - tip (bool): argument that is passed contains True or False + tip (bool): True zum Deaktivieren, False zum Aktivieren von Tooltips """ - if tip: - lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) - lines[5] = 'False\n' - Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") - - else: - lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) - lines[5] = 'True\n' - Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") + # Beachten Sie die umgekehrte Logik: tip=True bedeutet Tooltips deaktivieren + ConfigManager.set("tooltip", not tip) + # Aktualisieren Sie die lokale Variable für sofortige Wirkung + self.tips_enabled = not tip @staticmethod def about() -> None: @@ -354,27 +378,163 @@ class FrameWidgets(ttk.Frame): LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn) - def theme_change_light(self) -> None: - """ - Set a light theme - """ - if self.tk.call("ttk::style", "theme", "use") == "water-dark": - self.tk.call("set_theme", "light") - lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) # (keepends=True) = not changed - lines[3] = 'light\n' - Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") - self.color_label() + def update_ui_for_update_status(self, res): + """Update UI elements based on update check result""" + print(f"Updating UI for result: {res}") # Debug output + + # First, clean up any existing UI elements + if hasattr(self, 'update_btn') and self.update_btn.winfo_exists(): + self.update_btn.grid_forget() + + # Reset all variables to ensure fresh state + self.update_label.set("") + self.update_tooltip.set("") + self.update_foreground.set("black") + + if res == "False": + self.set_update.set(value=1) + self.update_label.set(_("Update search off")) + self.update_tooltip.set(_("Updates you have disabled")) + self.update_foreground.set("red") + + # Display the label + self.updates_lb.configure( + textvariable=self.update_label, + foreground=self.update_foreground.get() + ) + self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) + Tooltip(self.updates_lb, self.update_tooltip.get(), self.tips_enabled) + + elif res == "No Internet Connection!": + self.update_label.set(_("No Server Connection!")) + self.update_foreground.set("red") + + # Display the label + self.updates_lb.configure( + textvariable=self.update_label, + foreground=self.update_foreground.get() + ) + self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) + + elif res == "No Updates": + self.update_label.set(_("No Updates")) + self.update_tooltip.set(_("Congratulations! Wire-Py is up to date")) + self.update_foreground.set("black") + + # Display the label + self.updates_lb.configure( + textvariable=self.update_label, + foreground=self.update_foreground.get() + ) + self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) + Tooltip(self.updates_lb, self.update_tooltip.get(), self.tips_enabled) + + else: + # We have an update available + self.set_update.set(value=0) + update_text = f"Update {res} {_('available!')}" + + # Hide the label if it's visible + if self.updates_lb.winfo_ismapped(): + self.updates_lb.grid_forget() + + # Create or update the update button + if not hasattr(self, 'update_btn') or not self.update_btn.winfo_exists(): + # Create the update button if it doesn't exist yet + self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text) + self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) + Tooltip(self.update_btn, _("Click to download new version"), self.tips_enabled) + + self.download = tk.Menu(self, relief="flat") + self.update_btn.configure(menu=self.download, style="Toolbutton") + self.download.add_command( + label=_("Download"), + command=lambda: GiteaUpdate.download( + f"{AppConfig.DOWNLOAD_URL}/{res}.zip", + res, + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_vpn"], + AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"] + ) + ) + else: + # Update the existing update button + self.update_btn.configure(text=update_text) + # Make sure it's visible + self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) + + # Update the download command + if hasattr(self, 'download'): + self.download.entryconfigure( + 0, # First entry in the menu + command=lambda: GiteaUpdate.download( + f"{AppConfig.DOWNLOAD_URL}/{res}.zip", + res, + AppConfig.IMAGE_PATHS["icon_info"], + AppConfig.IMAGE_PATHS["icon_vpn"], + AppConfig.IMAGE_PATHS["icon_error"], + AppConfig.IMAGE_PATHS["icon_msg"] + ) + ) - def theme_change_dark(self) -> None: + def update_setting(self, update_res) -> None: + """write off or on in file + Args: + update_res (int): argument that is passed contains 0 or 1 """ - Set a dark theme - """ - if not self.tk.call("ttk::style", "theme", "use") == "water-dark": - self.tk.call("set_theme", "dark") - lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) - lines[3] = 'dark\n' - Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") - self.color_label() + if update_res == 1: + # Disable updates + ConfigManager.set("updates", "off") + # When updates are disabled, we know the result should be "False" + self.update_ui_for_update_status("False") + else: + # Enable updates + ConfigManager.set("updates", "on") + # When enabling updates, we need to actually check for updates + try: + # Force a fresh check by passing "on" as the update setting + res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, "on") + print(f"API returned: {res}") # Debug output + + # Make sure UI is updated regardless of previous state + if hasattr(self, 'update_btn'): + self.update_btn.grid_forget() + if hasattr(self, 'updates_lb'): + self.updates_lb.grid_forget() + + # Now update the UI with the fresh result + self.update_ui_for_update_status(res) + except Exception as e: + print(f"Error checking for updates: {e}") + # Fallback to a default message if there's an error + self.update_ui_for_update_status("No Internet Connection!") + + def update_tooletip_label(self) -> str: + """Update the theme label based on current theme""" + current_value = ConfigManager.get("tooletip") + if current_value == "True": + self.set_tip.set(_("Enable Tooltips")) + else: + self.set_tip.set(_("Disable Tooltips")) + + def update_theme_label(self) -> str: + """Update the theme label based on current theme""" + current_theme = ConfigManager.get("theme") + if current_theme == "light": + self.theme_label.set(_("Dark")) + else: + self.theme_label.set(_("Light")) + + def on_theme_toggle(self) -> None: + """Toggle between light and dark theme""" + current_theme = ConfigManager.get("theme") + new_theme = "dark" if current_theme == "light" else "light" + ThemeManager.change_theme(self, new_theme, new_theme) + self.color_label() + self.update_theme_label() # Update the theme label + # Update Menulfield + self.settings.entryconfigure(2, label=self.theme_label.get()) def start(self) -> None: """ @@ -410,8 +570,8 @@ class FrameWidgets(ttk.Frame): """ View activ Tunnel in the color green or yellow """ - lines = AppConfig.SETTINGS_FILE.read_text() - if "light\n" in lines: + if ConfigManager.get("theme") == "light": + self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green") else: @@ -727,54 +887,6 @@ class FrameWidgets(ttk.Frame): except EOFError as e: print(e) - def activate_tunnel(self, tunnel_name): - """Activates a tunnel after a delay""" - try: - # First check if the tunnel exists in NetworkManager - nm_connections = subprocess.run( - ["nmcli", "-t", "-f", "NAME", "connection", "show"], - check=True, - stdout=subprocess.PIPE, - text=True - ).stdout.strip().split('\n') - - # Find the actual connection name (it might have been modified) - actual_name = None - for conn in nm_connections: - if tunnel_name in conn: - actual_name = conn - break - - if actual_name: - # Use the actual connection name - subprocess.run(["nmcli", "connection", "up", actual_name], - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - # Use the original name as fallback - subprocess.run(["nmcli", "connection", "up", tunnel_name], - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - - # After successful activation, update the display - self.a = Tunnel.active() - self.str_var.set(self.a) - self.color_label() - - # Try to load the tunnel data - try: - data = self.handle_tunnel_data(self.a) - self.init_and_report(data) - self.show_data() - self.stop() - except Exception as e: - print(f"Error loading tunnel data: {e}") - - except subprocess.CalledProcessError as e: - print(f"Error activating tunnel: {e}", "hier simma") - def init_and_report(self, data=None) -> None: """ Displays the value address, DNS and peer in the labels @@ -881,7 +993,6 @@ class FrameWidgets(ttk.Frame): if __name__ == "__main__": _ = AppConfig.setup_translations() - tips = LxTools.if_tip(AppConfig.SETTINGS_FILE) LxTools.sigi(AppConfig.TEMP_DIR, AppConfig.USER_FILE) window = Wirepy() """ diff --git a/wp_app_config.py b/wp_app_config.py index 26cc313..aa68942 100644 --- a/wp_app_config.py +++ b/wp_app_config.py @@ -24,6 +24,12 @@ class AppConfig: KEYS_FILE: Path = CONFIG_DIR / "keys" AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service" + # Updates + # 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year + VERSION: str = "v. 2.04.1725" + UPDATE_URL: str = "https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases" + DOWNLOAD_URL: str = "https://git.ilunix.de/punix/Wire-Py/archive" + # Default settings DEFAULT_SETTINGS: Dict[str, Any] = { "updates": "on",