04-06-2025_large_update #35
							
								
								
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "workbench.settings.openDefaultSettings": true
 | 
			
		||||
    "workbench.startupEditor": "none"
 | 
			
		||||
    "update.showReleaseNotes": false
 | 
			
		||||
    "terminal.integrated.fontSize": 22
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										133
									
								
								cls_mth_fc.py
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								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, 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										443
									
								
								wirepy.py
									
									
									
									
									
								
							
							
						
						
									
										443
									
								
								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()
 | 
			
		||||
    """
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user