fix message window a path errors

This commit is contained in:
Désiré Werner Menrath 2025-05-07 20:25:09 +02:00
parent c56b42df3e
commit f4a51f0050
7 changed files with 724 additions and 299 deletions

View File

@ -1,7 +1,5 @@
""" Classes Method and Functions for lx Apps """ """ Classes Method and Functions for lx Apps """
import gettext
import locale
import os import os
import shutil import shutil
import signal import signal
@ -20,6 +18,7 @@ import requests
# Translate # Translate
_ = AppConfig.setup_translations() _ = AppConfig.setup_translations()
class Create: class Create:
""" """
This class is for the creation of the folders and files This class is for the creation of the folders and files
@ -43,7 +42,9 @@ class Create:
else: else:
sett.touch() sett.touch()
sett.write_text("[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff\n") sett.write_text(
"[UPDATES]\non\n[THEME]\nlight\n[TOOLTIP]\nTrue\n[AUTOSTART ON]\noff\n"
)
if AppConfig.KEYS_FILE.exists(): if AppConfig.KEYS_FILE.exists():
pass pass
@ -66,9 +67,11 @@ class Create:
else: else:
wg_ser.touch() wg_ser.touch()
wg_ser.write_text("[Unit]\nDescription=Automatic Tunnel Start\nAfter=network-online.target\n\n[Service]\n" wg_ser.write_text(
"Type=oneshot\nExecStartPre=/bin/sleep 5\nExecStart=/usr/local/bin/start_wg.py\n[Install]" "[Unit]\nDescription=Automatic Tunnel Start\nAfter=network-online.target\n\n[Service]\n"
"\nWantedBy=default.target") "Type=oneshot\nExecStartPre=/bin/sleep 5\nExecStart=/usr/local/bin/start_wg.py\n[Install]"
"\nWantedBy=default.target"
)
check_call(["systemctl", "--user", "enable", "wg_start.service"]) check_call(["systemctl", "--user", "enable", "wg_start.service"])
@staticmethod @staticmethod
@ -85,8 +88,12 @@ class Create:
""" """
Starts SSL dencrypt Starts SSL dencrypt
""" """
process: CompletedProcess[str] = subprocess.run(["pkexec", "/usr/local/bin/ssl_decrypt.py"], process: CompletedProcess[str] = subprocess.run(
stdout=subprocess.PIPE, text=True, check=True) ["pkexec", "/usr/local/bin/ssl_decrypt.py"],
stdout=subprocess.PIPE,
text=True,
check=True,
)
path: Path = Path.home() / ".config/wire_py/" path: Path = Path.home() / ".config/wire_py/"
file_in_path: list[Path] = list(path.rglob("*.dat")) file_in_path: list[Path] = list(path.rglob("*.dat"))
if file_in_path: if file_in_path:
@ -102,8 +109,12 @@ class Create:
""" """
Starts SSL encryption Starts SSL encryption
""" """
process: CompletedProcess[str] = subprocess.run(["pkexec", "/usr/local/bin/ssl_encrypt.py"], process: CompletedProcess[str] = subprocess.run(
stdout=subprocess.PIPE, text=True, check=True) ["pkexec", "/usr/local/bin/ssl_encrypt.py"],
stdout=subprocess.PIPE,
text=True,
check=True,
)
print(process.stdout) print(process.stdout)
if process.returncode == 0: if process.returncode == 0:
print("All Files successfully encrypted...") print("All Files successfully encrypted...")
@ -123,40 +134,44 @@ class LxTools(tk.Tk):
def center_window_cross_platform(window, width, height): def center_window_cross_platform(window, width, height):
""" """
Centers a window on the primary monitor in a way that works on both X11 and Wayland Centers a window on the primary monitor in a way that works on both X11 and Wayland
Args: Args:
window: The tkinter window to center window: The tkinter window to center
width: Window width width: Window width
height: Window height height: Window height
""" """
# Calculate the position before showing the window # Calculate the position before showing the window
# First attempt: Try to use GDK if available (works on both X11 and Wayland) # First attempt: Try to use GDK if available (works on both X11 and Wayland)
try: try:
import gi import gi
gi.require_version('Gdk', '3.0')
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk from gi.repository import Gdk
display = Gdk.Display.get_default() display = Gdk.Display.get_default()
monitor = display.get_primary_monitor() or display.get_monitor(0) monitor = display.get_primary_monitor() or display.get_monitor(0)
geometry = monitor.get_geometry() geometry = monitor.get_geometry()
scale_factor = monitor.get_scale_factor() scale_factor = monitor.get_scale_factor()
# Calculate center position on primary monitor # Calculate center position on primary monitor
x = geometry.x + (geometry.width - width // scale_factor) // 2 x = geometry.x + (geometry.width - width // scale_factor) // 2
y = geometry.y + (geometry.height - height // scale_factor) // 2 y = geometry.y + (geometry.height - height // scale_factor) // 2
# Set window geometry # Set window geometry
window.geometry(f"{width}x{height}+{x}+{y}") window.geometry(f"{width}x{height}+{x}+{y}")
return return
except (ImportError, AttributeError): except (ImportError, AttributeError):
pass pass
# Second attempt: Try xrandr for X11 # Second attempt: Try xrandr for X11
try: try:
import subprocess import subprocess
output = subprocess.check_output(["xrandr", "--query"], universal_newlines=True)
output = subprocess.check_output(
["xrandr", "--query"], universal_newlines=True
)
# Parse the output to find the primary monitor # Parse the output to find the primary monitor
primary_info = None primary_info = None
for line in output.splitlines(): for line in output.splitlines():
@ -167,7 +182,7 @@ class LxTools(tk.Tk):
primary_info = part primary_info = part
break break
break break
if primary_info: if primary_info:
# Parse the geometry: WIDTHxHEIGHT+X+Y # Parse the geometry: WIDTHxHEIGHT+X+Y
geometry = primary_info.split("+") geometry = primary_info.split("+")
@ -176,32 +191,33 @@ class LxTools(tk.Tk):
primary_height = int(dimensions[1]) primary_height = int(dimensions[1])
primary_x = int(geometry[1]) primary_x = int(geometry[1])
primary_y = int(geometry[2]) primary_y = int(geometry[2])
# Calculate center position on primary monitor # Calculate center position on primary monitor
x = primary_x + (primary_width - width) // 2 x = primary_x + (primary_width - width) // 2
y = primary_y + (primary_height - height) // 2 y = primary_y + (primary_height - height) // 2
# Set window geometry # Set window geometry
window.geometry(f"{width}x{height}+{x}+{y}") window.geometry(f"{width}x{height}+{x}+{y}")
return return
except (subprocess.SubprocessError, ImportError, IndexError, ValueError): except (subprocess.SubprocessError, ImportError, IndexError, ValueError):
pass pass
# Final fallback: Use standard Tkinter method # Final fallback: Use standard Tkinter method
screen_width = window.winfo_screenwidth() screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight() screen_height = window.winfo_screenheight()
# Try to make an educated guess for multi-monitor setups # Try to make an educated guess for multi-monitor setups
# If screen width is much larger than height, assume multiple monitors side by side # If screen width is much larger than height, assume multiple monitors side by side
if screen_width > screen_height * 1.8: # Heuristic for detecting multiple monitors if (
screen_width > screen_height * 1.8
): # Heuristic for detecting multiple monitors
# Assume primary monitor is on the left half # Assume primary monitor is on the left half
screen_width = screen_width // 2 screen_width = screen_width // 2
x = (screen_width - width) // 2 x = (screen_width - width) // 2
y = (screen_height - height) // 2 y = (screen_height - height) // 2
window.geometry(f"{width}x{height}+{x}+{y}") window.geometry(f"{width}x{height}+{x}+{y}")
@staticmethod @staticmethod
def get_file_name(path: Path, i: int = 5) -> List[str]: def get_file_name(path: Path, i: int = 5) -> List[str]:
""" """
@ -253,8 +269,14 @@ class LxTools(tk.Tk):
Path.unlink(file) Path.unlink(file)
@staticmethod @staticmethod
def msg_window(image_path: Path, image_path2: Path, w_title: str, w_txt: str, txt2: Optional[str] = None, def msg_window(
com: Optional[str] = None) -> None: image_path: Path,
image_path2: Path,
w_title: str,
w_txt: str,
txt2: Optional[str] = None,
com: Optional[str] = None,
) -> None:
""" """
Creates message windows Creates message windows
@ -269,29 +291,44 @@ class LxTools(tk.Tk):
msg.resizable(width=False, height=False) msg.resizable(width=False, height=False)
msg.title(w_title) msg.title(w_title)
msg.configure(pady=15, padx=15) msg.configure(pady=15, padx=15)
msg.img = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_info"])
msg.i_window = tk.Label(msg, image=msg.img) # Lade das erste Bild für das Fenster
try:
msg.img = tk.PhotoImage(file=image_path)
msg.i_window = tk.Label(msg, image=msg.img)
except Exception as e:
print(f"Fehler beim Laden des Fensterbildes: {e}")
msg.i_window = tk.Label(msg, text="Bild nicht gefunden")
label: tk.Label = tk.Label(msg, text=w_txt) label: tk.Label = tk.Label(msg, text=w_txt)
label.grid(column=1, row=0) label.grid(column=1, row=0)
if txt2 is not None and com is not None: if txt2 is not None and com is not None:
label.config(font=("Ubuntu", 11), padx=15, justify="left") label.config(font=("Ubuntu", 11), padx=15, justify="left")
msg.i_window.grid(column=0, row=0, sticky="nw") msg.i_window.grid(column=0, row=0, sticky="nw")
button2: ttk.Button = ttk.Button(msg, text=f"{txt2}", command=com, padding=4) button2: ttk.Button = ttk.Button(
msg, text=f"{txt2}", command=com, padding=4
)
button2.grid(column=0, row=1, sticky="e", columnspan=2) button2.grid(column=0, row=1, sticky="e", columnspan=2)
button: ttk.Button = ttk.Button(msg, text="OK", command=msg.destroy, padding=4) button: ttk.Button = ttk.Button(
msg, text="OK", command=msg.destroy, padding=4
)
button.grid(column=0, row=1, sticky="w", columnspan=2) button.grid(column=0, row=1, sticky="w", columnspan=2)
else: else:
label.config(font=("Ubuntu", 11), padx=15) label.config(font=("Ubuntu", 11), padx=15)
msg.i_window.grid(column=0, row=0) msg.i_window.grid(column=0, row=0)
button: ttk.Button = ttk.Button(msg, text="OK", command=msg.destroy, padding=4) button: ttk.Button = ttk.Button(
msg, text="OK", command=msg.destroy, padding=4
)
button.grid(column=0, columnspan=2, row=1) button.grid(column=0, columnspan=2, row=1)
AppConfig.IMAGE_PATHS["icon_vpn"]: tk.PhotoImage = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"]) # Lade das Icon für das Fenster
msg.iconphoto(True, AppConfig.IMAGE_PATHS["icon_vpn"]) try:
icon = tk.PhotoImage(file=image_path2)
msg.iconphoto(True, icon)
except Exception as e:
print(f"Fehler beim Laden des Fenstericons: {e}")
msg.columnconfigure(0, weight=1) msg.columnconfigure(0, weight=1)
msg.rowconfigure(0, weight=1) msg.rowconfigure(0, weight=1)
msg.winfo_toplevel() msg.winfo_toplevel()
@ -317,15 +354,22 @@ class LxTools(tk.Tk):
NoReturn since the function either exits the program or continues execution NoReturn since the function either exits the program or continues execution
""" """
signals_to_names_dict: Dict[int, str] = dict((getattr(signal, n), n) for n in dir(signal) signals_to_names_dict: Dict[int, str] = dict(
if n.startswith("SIG") and "_" not in n) (getattr(signal, n), n)
for n in dir(signal)
if n.startswith("SIG") and "_" not in n
)
signal_name: str = signals_to_names_dict.get(signum, f"Unnamed signal: {signum}") signal_name: str = signals_to_names_dict.get(
signum, f"Unnamed signal: {signum}"
)
# End program for certain signals, report to others only reception # End program for certain signals, report to others only reception
if signum in (signal.SIGINT, signal.SIGTERM): if signum in (signal.SIGINT, signal.SIGTERM):
exit_code: int = 1 exit_code: int = 1
print(f"\nSignal {signal_name} {signum} received. => Aborting with exit code {exit_code}.") print(
f"\nSignal {signal_name} {signum} received. => Aborting with exit code {exit_code}."
)
LxTools.clean_files(file_path, file) LxTools.clean_files(file_path, file)
print("Breakdown by user...") print("Breakdown by user...")
sys.exit(exit_code) sys.exit(exit_code)
@ -343,7 +387,8 @@ class LxTools(tk.Tk):
class Tunnel: class Tunnel:
""" """
Class of Methods for Wire-Py Class of Methods for Wire-Py
""" """
@classmethod @classmethod
def con_to_dict(cls, file: TextIO) -> Tuple[str, str, str, Optional[str]]: def con_to_dict(cls, file: TextIO) -> Tuple[str, str, str, Optional[str]]:
""" """
@ -394,7 +439,11 @@ class Tunnel:
""" """
Shows the Active Tunnel Shows the Active Tunnel
""" """
active = (os.popen('nmcli con show --active | grep -iPo "(.*)(wireguard)"').read().split()) active = (
os.popen('nmcli con show --active | grep -iPo "(.*)(wireguard)"')
.read()
.split()
)
if not active: if not active:
active = "" active = ""
else: else:
@ -413,8 +462,14 @@ class Tunnel:
return wg_s return wg_s
@staticmethod @staticmethod
def export(image_path: Path = None, image_path2: Path = None, image_path3: Path = None, image_path4: Path = None, def export(
title: Dict = None, window_msg: Dict = None) -> None: image_path: Path = None,
image_path2: Path = None,
image_path3: Path = None,
image_path4: Path = None,
title: Dict = None,
window_msg: Dict = None,
) -> None:
""" """
This will export the tunnels. This will export the tunnels.
A zipfile with the current date and time is created A zipfile with the current date and time is created
@ -439,15 +494,30 @@ class Tunnel:
with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf: with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf:
if len(zf.namelist()) != 0: if len(zf.namelist()) != 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], Msg.STR["exp_succ"], Msg.STR["exp_in_home"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_vpn"],
Msg.STR["exp_succ"],
Msg.STR["exp_in_home"],
)
else: else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["exp_err"], Msg.STR["exp_try"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["exp_err"],
Msg.STR["exp_try"],
)
else: else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["tl_first"],
)
except TypeError: except TypeError:
pass pass
@ -459,15 +529,16 @@ class ConfigManager:
Universal class for managing configuration files with caching. Universal class for managing configuration files with caching.
Can be reused in different projects. Can be reused in different projects.
""" """
_config = None _config = None
_config_file = None _config_file = None
@classmethod @classmethod
def init(cls, config_file): def init(cls, config_file):
"""Initial the Configmanager with the given config file""" """Initial the Configmanager with the given config file"""
cls._config_file = config_file cls._config_file = config_file
cls._config = None # Reset the cache cls._config = None # Reset the cache
@classmethod @classmethod
def load(cls): def load(cls):
"""Load the config file and return the config as dict""" """Load the config file and return the config as dict"""
@ -475,44 +546,45 @@ class ConfigManager:
try: try:
lines = Path(cls._config_file).read_text(encoding="utf-8").splitlines() lines = Path(cls._config_file).read_text(encoding="utf-8").splitlines()
cls._config = { cls._config = {
'updates': lines[1].strip(), "updates": lines[1].strip(),
'theme': lines[3].strip(), "theme": lines[3].strip(),
'tooltips': lines[5].strip() == "True", # is converted here to boolean!!! "tooltips": lines[5].strip()
'autostart': lines[7].strip() if len(lines) > 7 else 'off' == "True", # is converted here to boolean!!!
"autostart": lines[7].strip() if len(lines) > 7 else "off",
} }
except (IndexError, FileNotFoundError): except (IndexError, FileNotFoundError):
# DeDefault values in case of error # DeDefault values in case of error
cls._config = { cls._config = {
'updates': 'on', "updates": "on",
'theme': 'light', "theme": "light",
'tooltips': "True", # Default Value as string ! "tooltips": "True", # Default Value as string !
'autostart': 'off' "autostart": "off",
} }
return cls._config return cls._config
@classmethod @classmethod
def save(cls): def save(cls):
"""Save the config to the config file""" """Save the config to the config file"""
if cls._config: if cls._config:
lines = [ lines = [
'# Configuration\n', "# Configuration\n",
f"{cls._config['updates']}\n", f"{cls._config['updates']}\n",
'# Theme\n', "# Theme\n",
f"{cls._config['theme']}\n", f"{cls._config['theme']}\n",
'# Tooltips\n', "# Tooltips\n",
f"{str(cls._config['tooltips'])}\n", f"{str(cls._config['tooltips'])}\n",
'# Autostart\n', "# Autostart\n",
f"{cls._config['autostart']}\n" f"{cls._config['autostart']}\n",
] ]
Path(cls._config_file).write_text(''.join(lines), encoding="utf-8") Path(cls._config_file).write_text("".join(lines), encoding="utf-8")
@classmethod @classmethod
def set(cls, key, value): def set(cls, key, value):
"""Sets a configuration value and saves the change""" """Sets a configuration value and saves the change"""
cls.load() cls.load()
cls._config[key] = value cls._config[key] = value
cls.save() cls.save()
@classmethod @classmethod
def get(cls, key, default=None): def get(cls, key, default=None):
"""Returns a configuration value""" """Returns a configuration value"""
@ -545,42 +617,48 @@ class GiteaUpdate:
update_api_url: Update API URL update_api_url: Update API URL
version: Current version version: Current version
update_setting: Update setting from ConfigManager (on/off) update_setting: Update setting from ConfigManager (on/off)
Returns: Returns:
New version or status message New version or status message
""" """
# If updates are disabled, return immediately # If updates are disabled, return immediately
if update_setting != "on": if update_setting != "on":
return "False" return "False"
try: try:
response: requests.Response = requests.get(update_api_url, timeout=10) response: requests.Response = requests.get(update_api_url, timeout=10)
response.raise_for_status() # Raise exception for HTTP errors response.raise_for_status() # Raise exception for HTTP errors
response_data = response.json() response_data = response.json()
if not response_data: if not response_data:
return "No Updates" return "No Updates"
latest_version = response_data[0].get("tag_name") latest_version = response_data[0].get("tag_name")
if not latest_version: if not latest_version:
return "Invalid API Response" return "Invalid API Response"
# Compare versions (strip 'v. ' prefix if present) # Compare versions (strip 'v. ' prefix if present)
current_version = version[3:] if version.startswith("v. ") else version current_version = version[3:] if version.startswith("v. ") else version
if current_version != latest_version: if current_version != latest_version:
return latest_version return latest_version
else: else:
return "No Updates" return "No Updates"
except requests.exceptions.RequestException: except requests.exceptions.RequestException:
return "No Internet Connection!" return "No Internet Connection!"
except (ValueError, KeyError, IndexError): except (ValueError, KeyError, IndexError):
return "Invalid API Response" return "Invalid API Response"
@staticmethod @staticmethod
def download(urld: str, res: str, image_path: Path = None, image_path2: Path = None, image_path3: Path = None, def download(
image_path4: Path = None) -> None: urld: str,
res: str,
image_path: Path = None,
image_path2: Path = None,
image_path3: Path = None,
image_path4: Path = None,
) -> None:
""" """
Downloads new version of wirepy Downloads new version of wirepy
@ -600,35 +678,57 @@ class GiteaUpdate:
wt: str = _("Download Successful") wt: str = _("Download Successful")
msg_t: str = _("Your zip file is in home directory") msg_t: str = _("Your zip file is in home directory")
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], wt, msg_t) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_vpn"],
wt,
msg_t,
)
else: else:
wt: str = _("Download error") wt: str = _("Download error")
msg_t: str = _("Download failed! Please try again") msg_t: str = _("Download failed! Please try again")
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], wt, msg_t) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
wt,
msg_t,
)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
wt: str = _("Download error") wt: str = _("Download error")
msg_t: str = _("Download failed! No internet connection!") msg_t: str = _("Download failed! No internet connection!")
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], wt, msg_t) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
wt,
msg_t,
)
class Tooltip(): class Tooltip:
"""Class for Tooltip """Class for Tooltip
from common_tools.py import Tooltip from common_tools.py import Tooltip
example: Tooltip(label, "Show tooltip on label") example: Tooltip(label, "Show tooltip on label")
example: Tooltip(button, "Show tooltip on button") example: Tooltip(button, "Show tooltip on button")
example: Tooltip(widget, "Text", state_var=tk.BooleanVar()) example: Tooltip(widget, "Text", state_var=tk.BooleanVar())
example: Tooltip(widget, "Text", state_var=tk.BooleanVar(), x_offset=20, y_offset=10) example: Tooltip(widget, "Text", state_var=tk.BooleanVar(), x_offset=20, y_offset=10)
info: label and button are parent widgets. info: label and button are parent widgets.
NOTE: When using with state_var, pass the tk.BooleanVar object directly, NOTE: When using with state_var, pass the tk.BooleanVar object directly,
NOT its value. For example: use state_var=my_bool_var, NOT state_var=my_bool_var.get() NOT its value. For example: use state_var=my_bool_var, NOT state_var=my_bool_var.get()
""" """
def __init__(self, widget: Any, text: str, state_var: Optional[tk.BooleanVar] = None,
x_offset: int = 65, y_offset: int = 40) -> None: def __init__(
self,
widget: Any,
text: str,
state_var: Optional[tk.BooleanVar] = None,
x_offset: int = 65,
y_offset: int = 40,
) -> None:
"""Tooltip Class""" """Tooltip Class"""
self.widget: Any = widget self.widget: Any = widget
self.text: str = text self.text: str = text
@ -636,55 +736,59 @@ class Tooltip():
self.state_var = state_var self.state_var = state_var
self.x_offset = x_offset self.x_offset = x_offset
self.y_offset = y_offset self.y_offset = y_offset
# Initial binding based on current state # Initial binding based on current state
self.update_bindings() self.update_bindings()
# Add trace to the state_var if provided # Add trace to the state_var if provided
if self.state_var is not None: if self.state_var is not None:
self.state_var.trace_add("write", self.update_bindings) self.state_var.trace_add("write", self.update_bindings)
def update_bindings(self, *args) -> None: def update_bindings(self, *args) -> None:
"""Updates the bindings based on the current state""" """Updates the bindings based on the current state"""
# Remove existing bindings first # Remove existing bindings first
self.widget.unbind("<Enter>") self.widget.unbind("<Enter>")
self.widget.unbind("<Leave>") self.widget.unbind("<Leave>")
# Add new bindings if tooltips are enabled # Add new bindings if tooltips are enabled
if self.state_var is None or self.state_var.get(): if self.state_var is None or self.state_var.get():
self.widget.bind("<Enter>", self.show_tooltip) self.widget.bind("<Enter>", self.show_tooltip)
self.widget.bind("<Leave>", self.hide_tooltip) self.widget.bind("<Leave>", self.hide_tooltip)
def show_tooltip(self, event: Optional[Any] = None) -> None: def show_tooltip(self, event: Optional[Any] = None) -> None:
"""Shows the tooltip""" """Shows the tooltip"""
if self.tooltip_window or not self.text: if self.tooltip_window or not self.text:
return return
x: int x: int
y: int y: int
cx: int cx: int
cy: int cy: int
x, y, cx, cy = self.widget.bbox("insert") x, y, cx, cy = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + self.x_offset x += self.widget.winfo_rootx() + self.x_offset
y += self.widget.winfo_rooty() + self.y_offset y += self.widget.winfo_rooty() + self.y_offset
self.tooltip_window = tw = tk.Toplevel(self.widget) self.tooltip_window = tw = tk.Toplevel(self.widget)
tw.wm_overrideredirect(True) tw.wm_overrideredirect(True)
tw.wm_geometry(f"+{x}+{y}") tw.wm_geometry(f"+{x}+{y}")
label: tk.Label = tk.Label( label: tk.Label = tk.Label(
tw, text=self.text, background="lightgreen", foreground="black", tw,
relief="solid", borderwidth=1, padx=5, pady=5 text=self.text,
background="lightgreen",
foreground="black",
relief="solid",
borderwidth=1,
padx=5,
pady=5,
) )
label.grid() label.grid()
self.tooltip_window.after(2200, lambda: tw.destroy()) self.tooltip_window.after(2200, lambda: tw.destroy())
def hide_tooltip(self, event: Optional[Any] = None) -> None: def hide_tooltip(self, event: Optional[Any] = None) -> None:
"""Hides the tooltip""" """Hides the tooltip"""
if self.tooltip_window: if self.tooltip_window:
self.tooltip_window.destroy() self.tooltip_window.destroy()
self.tooltip_window = None self.tooltip_window = None

View File

@ -1,15 +0,0 @@
#!/usr/bin/python3
from pathlib import Path
from subprocess import check_call
from tkinter import filedialog, ttk
from common_tools import Create, LxTools
from wp_app_config import AppConfig, Msg
import gettext
import locale
import os
import shutil
import subprocess
from typing import Optional, Dict, Any, NoReturn, TextIO, Tuple, List
# Translate
_ = AppConfig.setup_translations()

View File

@ -12,11 +12,24 @@ uname: Path = Path("/tmp/.log_user")
log_name = Path(uname).read_text(encoding="utf-8") log_name = Path(uname).read_text(encoding="utf-8")
keyfile: Path = Path(f"/home/{log_name}/.config/wire_py/pbwgk.pem") keyfile: Path = Path(f"/home/{log_name}/.config/wire_py/pbwgk.pem")
#PKEYFILE: Path = "/usr/local/etc/ssl/pwgk.pem"
# PKEYFILE: Path = "/usr/local/etc/ssl/pwgk.pem"
if not keyfile.is_file(): if not keyfile.is_file():
check_call(["openssl", "rsa", "-in", AppConfig.SYSTEM_PATHS["pkey_path"], "-out", keyfile, "-outform", "PEM", "-pubout"]) check_call(
[
"openssl",
"rsa",
"-in",
AppConfig.SYSTEM_PATHS["pkey_path"],
"-out",
keyfile,
"-outform",
"PEM",
"-pubout",
]
)
shutil.chown(keyfile, 1000, 1000) shutil.chown(keyfile, 1000, 1000)
AppConfig.TEMP_DIR2 = f"/home/{log_name}/.config/wire_py/" AppConfig.TEMP_DIR2 = f"/home/{log_name}/.config/wire_py/"
@ -29,6 +42,17 @@ if os.path.exists(f"{AppConfig.TEMP_DIR2}pbwgk.pem"):
for detunnels in detl: for detunnels in detl:
tlname2 = f"{detunnels[:-4]}.conf" tlname2 = f"{detunnels[:-4]}.conf"
extpath = f"{AppConfig.TEMP_DIR}/{tlname2}" extpath = f"{AppConfig.TEMP_DIR}/{tlname2}"
check_call(["openssl", "pkeyutl", "-decrypt", "-inkey", AppConfig.SYSTEM_PATHS["pkey_path"], "-in", detunnels, check_call(
"-out", extpath]) [
"openssl",
"pkeyutl",
"-decrypt",
"-inkey",
AppConfig.SYSTEM_PATHS["pkey_path"],
"-in",
detunnels,
"-out",
extpath,
]
)
shutil.chown(extpath, 1000, 1000) shutil.chown(extpath, 1000, 1000)

View File

@ -8,15 +8,26 @@ from subprocess import check_call
from common_tools import LxTools from common_tools import LxTools
from wp_app_config import AppConfig from wp_app_config import AppConfig
#uname: Path = Path("/tmp/.log_user")
#log_name = AppConfig.USER_FILE.read_text(encoding="utf-8") keyfile: Path = Path(
f"/home/{AppConfig.USER_FILE.read_text(encoding="utf-8")}/.config/wire_py/pbwgk.pem"
keyfile: Path = Path(f"/home/{AppConfig.USER_FILE.read_text(encoding="utf-8")}/.config/wire_py/pbwgk.pem") )
if not keyfile.is_file(): if not keyfile.is_file():
check_call(["openssl", "rsa", "-in", AppConfig.SYSTEM_PATHS["pkey_path"], "-out", keyfile, "-outform", "PEM", "-pubout"]) check_call(
[
"openssl",
"rsa",
"-in",
AppConfig.SYSTEM_PATHS["pkey_path"],
"-out",
keyfile,
"-outform",
"PEM",
"-pubout",
]
)
shutil.chown(keyfile, 1000, 1000) shutil.chown(keyfile, 1000, 1000)
if AppConfig.TEMP_DIR.exists(): if AppConfig.TEMP_DIR.exists():
@ -28,8 +39,20 @@ if not keyfile.is_file():
for tunnels in tl: for tunnels in tl:
sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}" sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}"
tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat" tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat"
check_call(["openssl", "pkeyutl", "-encrypt", "-inkey", keyfile, "-pubin", "-in", sourcetl, "-out", check_call(
tlname,]) [
"openssl",
"pkeyutl",
"-encrypt",
"-inkey",
keyfile,
"-pubin",
"-in",
sourcetl,
"-out",
tlname,
]
)
else: else:
@ -42,5 +65,17 @@ else:
for tunnels in tl: for tunnels in tl:
sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}" sourcetl: str = f"{AppConfig.TEMP_DIR}/{tunnels}"
tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat" tlname: str = f"{CRYPTFILES}{tunnels[:-5]}.dat"
check_call(["openssl", "pkeyutl", "-encrypt", "-inkey", keyfile, "-pubin", "-in", sourcetl, "-out", check_call(
tlname]) [
"openssl",
"pkeyutl",
"-encrypt",
"-inkey",
keyfile,
"-pubin",
"-in",
sourcetl,
"-out",
tlname,
]
)

View File

@ -1,7 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
""" """
This script belongs to wirepy and is for the auto start of the tunnel This script belongs to wirepy and is for the auto start of the tunnel
""" """
from pathlib import Path from pathlib import Path
from subprocess import check_call from subprocess import check_call

536
wirepy.py
View File

@ -2,8 +2,6 @@
""" """
this script is a simple GUI for managing Wireguard Tunnels this script is a simple GUI for managing Wireguard Tunnels
""" """
import gettext
import locale
import os import os
import shutil import shutil
import subprocess import subprocess
@ -14,7 +12,15 @@ from pathlib import Path
from subprocess import check_call from subprocess import check_call
from tkinter import TclError, filedialog, ttk from tkinter import TclError, filedialog, ttk
from common_tools import (ConfigManager, ThemeManager, Create, GiteaUpdate, Tunnel, Tooltip, LxTools) from common_tools import (
ConfigManager,
ThemeManager,
Create,
GiteaUpdate,
Tunnel,
Tooltip,
LxTools,
)
from wp_app_config import AppConfig, Msg from wp_app_config import AppConfig, Msg
LxTools.uos() LxTools.uos()
@ -22,26 +28,30 @@ Create.dir_and_files()
Create.make_dir() Create.make_dir()
Create.decrypt() Create.decrypt()
class Wirepy(tk.Tk): class Wirepy(tk.Tk):
""" """
Class Wirepy this is the Main Window of wirepy Class Wirepy this is the Main Window of wirepy
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Hide the window initially # Hide the window initially
self.withdraw() self.withdraw()
self.my_tool_tip = None self.my_tool_tip = None
self.x_width = AppConfig.UI_CONFIG["window_size"][0] self.x_width = AppConfig.UI_CONFIG["window_size"][0]
self.y_height = AppConfig.UI_CONFIG["window_size"][1] self.y_height = AppConfig.UI_CONFIG["window_size"][1]
# Set the window size # Set the window size
self.geometry(f"{self.x_width}x{self.y_height}") self.geometry(f"{self.x_width}x{self.y_height}")
self.resizable(AppConfig.UI_CONFIG["resizable_window"][0], self.resizable(
AppConfig.UI_CONFIG["resizable_window"][1]) AppConfig.UI_CONFIG["resizable_window"][0],
AppConfig.UI_CONFIG["resizable_window"][1],
)
self.title(AppConfig.UI_CONFIG["window_title"]) self.title(AppConfig.UI_CONFIG["window_title"])
self.columnconfigure(0, weight=1) self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1) self.rowconfigure(0, weight=1)
self.tk.call("source", f"{AppConfig.SYSTEM_PATHS['tcl_path']}/water.tcl") self.tk.call("source", f"{AppConfig.SYSTEM_PATHS['tcl_path']}/water.tcl")
@ -57,10 +67,10 @@ class Wirepy(tk.Tk):
# Add the widgets # Add the widgets
FrameWidgets(self).grid() FrameWidgets(self).grid()
# Center the window on the primary monitor # Center the window on the primary monitor
LxTools.center_window_cross_platform(self, self.x_width, self.y_height) LxTools.center_window_cross_platform(self, self.x_width, self.y_height)
# Now show the window after it has been positioned # Now show the window after it has been positioned
self.after(10, self.deiconify) self.after(10, self.deiconify)
@ -69,9 +79,10 @@ class FrameWidgets(ttk.Frame):
""" """
ttk frame class for better structure ttk frame class for better structure
""" """
def __init__(self, container, **kwargs): def __init__(self, container, **kwargs):
super().__init__(container, **kwargs) super().__init__(container, **kwargs)
self.lb_tunnel = None self.lb_tunnel = None
self.btn_stst = None self.btn_stst = None
self.endpoint = None self.endpoint = None
@ -100,13 +111,16 @@ class FrameWidgets(ttk.Frame):
else: else:
# If it's a string or something else # If it's a string or something else
self.tooltip_state.set(str(state) == "True") self.tooltip_state.set(str(state) == "True")
self.tooltip_label = tk.StringVar() # StringVar-Variable for tooltip label for view Disabled/Enabled
self.tooltip_update_label()
self.update_label = tk.StringVar() # StringVar-Variable for update label
self.update_tooltip = tk.StringVar() # StringVar-Variable for update tooltip please not remove!
self.update_foreground = tk.StringVar(value="red")
self.tooltip_label = (
tk.StringVar()
) # StringVar-Variable for tooltip label for view Disabled/Enabled
self.tooltip_update_label()
self.update_label = tk.StringVar() # StringVar-Variable for update label
self.update_tooltip = (
tk.StringVar()
) # StringVar-Variable for update tooltip please not remove!
self.update_foreground = tk.StringVar(value="red")
# Frame for Menu # Frame for Menu
self.menu_frame = ttk.Frame(self) self.menu_frame = ttk.Frame(self)
@ -118,7 +132,9 @@ class FrameWidgets(ttk.Frame):
self.version_lb.config(font=("Ubuntu", 11), foreground="#00c4ff") self.version_lb.config(font=("Ubuntu", 11), foreground="#00c4ff")
self.version_lb.grid(column=0, row=0, rowspan=4, padx=10) self.version_lb.grid(column=0, row=0, rowspan=4, padx=10)
Tooltip(self.version_lb, f"Version: {AppConfig.VERSION[2:]}", self.tooltip_state) Tooltip(
self.version_lb, f"Version: {AppConfig.VERSION[2:]}", self.tooltip_state
)
self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options")) self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options"))
self.options_btn.grid(column=1, columnspan=1, row=0) self.options_btn.grid(column=1, columnspan=1, row=0)
@ -128,27 +144,37 @@ class FrameWidgets(ttk.Frame):
self.set_update = tk.IntVar() self.set_update = tk.IntVar()
self.settings = tk.Menu(self, relief="flat") self.settings = tk.Menu(self, relief="flat")
self.options_btn.configure(menu=self.settings, style="Toolbutton") self.options_btn.configure(menu=self.settings, style="Toolbutton")
self.settings.add_checkbutton(label=_("Disable Updates"), self.settings.add_checkbutton(
command=lambda: self.update_setting(self.set_update.get()), variable=self.set_update) label=_("Disable Updates"),
command=lambda: self.update_setting(self.set_update.get()),
variable=self.set_update,
)
self.updates_lb = ttk.Label(self.menu_frame, textvariable=self.update_label) self.updates_lb = ttk.Label(self.menu_frame, textvariable=self.update_label)
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10) self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
self.updates_lb.grid_remove() self.updates_lb.grid_remove()
self.update_label.trace_add("write", self.update_label_display) self.update_label.trace_add("write", self.update_label_display)
self.update_foreground.trace_add("write", self.update_label_display) self.update_foreground.trace_add("write", self.update_label_display)
res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates")) res = GiteaUpdate.api_down(
AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates")
)
self.update_ui_for_update(res) self.update_ui_for_update(res)
# Tooltip Menu # Tooltip Menu
self.settings.add_command(label=self.tooltip_label.get(), command=self.tooltips_toggle) self.settings.add_command(
# Label show dark or light label=self.tooltip_label.get(), command=self.tooltips_toggle
)
# Label show dark or light
self.theme_label = tk.StringVar() self.theme_label = tk.StringVar()
self.update_theme_label() self.update_theme_label()
self.settings.add_command(label=self.theme_label.get(), command=self.on_theme_toggle) self.settings.add_command(
label=self.theme_label.get(), command=self.on_theme_toggle
)
# About BTN Menu / Label # About BTN Menu / Label
self.about_btn = ttk.Button( self.about_btn = ttk.Button(
self.menu_frame, text=_("About"), style="Toolbutton", command=self.about) self.menu_frame, text=_("About"), style="Toolbutton", command=self.about
)
self.about_btn.grid(column=2, columnspan=2, row=0) self.about_btn.grid(column=2, columnspan=2, row=0)
self.readme = tk.Menu(self) self.readme = tk.Menu(self)
@ -172,7 +198,9 @@ class FrameWidgets(ttk.Frame):
# Bottom Frame 4 # Bottom Frame 4
self.lb_frame3 = ttk.Frame(self) self.lb_frame3 = ttk.Frame(self)
self.lb_frame3.configure(relief="flat") self.lb_frame3.configure(relief="flat")
self.lb_frame3.grid(column=0, row=5, columnspan=4, sticky="snew", padx=2, pady=2) self.lb_frame3.grid(
column=0, row=5, columnspan=4, sticky="snew", padx=2, pady=2
)
# Bottom Frame 5 # Bottom Frame 5
self.lb_frame4 = ttk.Frame(self) self.lb_frame4 = ttk.Frame(self)
@ -205,7 +233,9 @@ class FrameWidgets(ttk.Frame):
self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns") self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns")
self.l_box.event_add("<<ClickEvent>>", "<Button-1>") self.l_box.event_add("<<ClickEvent>>", "<Button-1>")
self.l_box.bind("<<ClickEvent>>", self.enable_check_box) self.l_box.bind("<<ClickEvent>>", self.enable_check_box)
self.scrollbar = ttk.Scrollbar(self.lb_frame_btn_lbox, orient="vertical", command=self.l_box.yview) self.scrollbar = ttk.Scrollbar(
self.lb_frame_btn_lbox, orient="vertical", command=self.l_box.yview
)
self.scrollbar.grid(column=1, rowspan=4, row=0, sticky="nse") self.scrollbar.grid(column=1, rowspan=4, row=0, sticky="nse")
self.l_box.configure(yscrollcommand=self.scrollbar.set) self.l_box.configure(yscrollcommand=self.scrollbar.set)
@ -230,14 +260,24 @@ class FrameWidgets(ttk.Frame):
self.show_data() self.show_data()
# Button Import # Button Import
self.btn_i = ttk.Button(self.lb_frame_btn_lbox, image=self.imp_pic, command=self.import_sl, padding=0) self.btn_i = ttk.Button(
self.lb_frame_btn_lbox,
image=self.imp_pic,
command=self.import_sl,
padding=0,
)
self.btn_i.grid(column=0, row=1, padx=15, pady=8) self.btn_i.grid(column=0, row=1, padx=15, pady=8)
Tooltip(self.btn_i, Msg.TTIP["import_tl"], self.tooltip_state) Tooltip(self.btn_i, Msg.TTIP["import_tl"], self.tooltip_state)
# Button Trash # Button Trash
self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=self.delete, padding=0, self.btn_tr = ttk.Button(
style="CButton.TButton") self.lb_frame_btn_lbox,
image=self.tr_pic,
command=self.delete,
padding=0,
style="CButton.TButton",
)
self.btn_tr.grid(column=0, row=2, padx=15, pady=8) self.btn_tr.grid(column=0, row=2, padx=15, pady=8)
if self.l_box.size() == 0: if self.l_box.size() == 0:
@ -246,12 +286,20 @@ class FrameWidgets(ttk.Frame):
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state) Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state)
# Button Export # Button Export
self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic, self.btn_exp = ttk.Button(
command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"], self.lb_frame_btn_lbox,
AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], image=self.exp_pic,
AppConfig.IMAGE_PATHS["icon_msg"], command=lambda: Tunnel.export(
Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0) 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) self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
if self.l_box.size() == 0: if self.l_box.size() == 0:
@ -266,13 +314,31 @@ class FrameWidgets(ttk.Frame):
self.lb_rename.config(state="disable") self.lb_rename.config(state="disable")
if self.l_box.size() != 0: if self.l_box.size() != 0:
Tooltip(self.lb_rename, Msg.TTIP["rename_tl"], self.tooltip_state, x_offset= -120, y_offset=-70) Tooltip(
self.lb_rename,
Msg.TTIP["rename_tl"],
self.tooltip_state,
x_offset=-120,
y_offset=-70,
)
else: else:
Tooltip(self.lb_rename, Msg.TTIP["rename_tl_info"], self.tooltip_state, x_offset=-180, y_offset=-50) Tooltip(
self.lb_rename,
Msg.TTIP["rename_tl_info"],
self.tooltip_state,
x_offset=-180,
y_offset=-50,
)
# Button Rename # Button Rename
self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=self.tl_rename, self.btn_rename = ttk.Button(
padding=4, style="RnButton.TButton") self.lb_frame4,
text=_("Rename"),
state="disable",
command=self.tl_rename,
padding=4,
style="RnButton.TButton",
)
self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne") self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne")
# Check Buttons # Check Buttons
@ -281,22 +347,46 @@ class FrameWidgets(ttk.Frame):
self.autoconnect_var.set(f"{self.auto_con}") self.autoconnect_var.set(f"{self.auto_con}")
# Frame for Labels, Entry and Button # Frame for Labels, Entry and Button
self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, width=15) self.autoconnect = ttk.Label(
self.lb_frame3, textvariable=self.autoconnect_var, width=15
)
self.autoconnect.config(font=("Ubuntu", 11)) self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19) self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
self.wg_autostart = ttk.Checkbutton(self.lb_frame3, text=_("Autoconnect on:"), variable=self.selected_option, self.wg_autostart = ttk.Checkbutton(
command=self.box_set) self.lb_frame3,
text=_("Autoconnect on:"),
variable=self.selected_option,
command=self.box_set,
)
self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw") self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw")
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1: if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state, x_offset=-10, y_offset=-40) Tooltip(
self.wg_autostart,
Msg.TTIP["autostart"],
self.tooltip_state,
x_offset=-10,
y_offset=-40,
)
if self.l_box.size() == 0: if self.l_box.size() == 0:
Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"], self.tooltip_state, x_offset=30, y_offset=-50) Tooltip(
self.wg_autostart,
Msg.TTIP["autostart_info"],
self.tooltip_state,
x_offset=30,
y_offset=-50,
)
else: else:
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state, x_offset=-10, y_offset=-40) Tooltip(
self.wg_autostart,
Msg.TTIP["autostart"],
self.tooltip_state,
x_offset=-10,
y_offset=-40,
)
self.on_off() self.on_off()
@ -316,10 +406,10 @@ class FrameWidgets(ttk.Frame):
def update_ui_for_update(self, res): def update_ui_for_update(self, res):
"""Update UI elements based on update check result""" """Update UI elements based on update check result"""
# First, remove the update button if it exists to avoid conflicts # First, remove the update button if it exists to avoid conflicts
if hasattr(self, 'update_btn'): if hasattr(self, "update_btn"):
self.update_btn.grid_forget() self.update_btn.grid_forget()
delattr(self, 'update_btn') delattr(self, "update_btn")
if res == "False": if res == "False":
self.set_update.set(value=1) self.set_update.set(value=1)
self.update_label.set(_("Update search off")) self.update_label.set(_("Update search off"))
@ -328,44 +418,50 @@ class FrameWidgets(ttk.Frame):
self.update_foreground.set("") self.update_foreground.set("")
# Set tooltip for the label # Set tooltip for the label
Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state) Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state)
elif res == "No Internet Connection!": elif res == "No Internet Connection!":
self.update_label.set(_("No Server Connection!")) self.update_label.set(_("No Server Connection!"))
self.update_foreground.set("red") self.update_foreground.set("red")
# Set tooltip for "No Server Connection" # Set tooltip for "No Server Connection"
Tooltip(self.updates_lb, _("Could not connect to update server"), self.tooltip_state) Tooltip(
self.updates_lb,
_("Could not connect to update server"),
self.tooltip_state,
)
elif res == "No Updates": elif res == "No Updates":
self.update_label.set(_("No Updates")) self.update_label.set(_("No Updates"))
self.update_tooltip.set(_("Congratulations! Wire-Py is up to date")) self.update_tooltip.set(_("Congratulations! Wire-Py is up to date"))
self.update_foreground.set("") self.update_foreground.set("")
# Set tooltip for the label # Set tooltip for the label
Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state) Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state)
else: else:
self.set_update.set(value=0) self.set_update.set(value=0)
update_text = f"Update {res} {_('available!')}" update_text = f"Update {res} {_('available!')}"
# Clear the label text since we'll show the button instead # Clear the label text since we'll show the button instead
self.update_label.set("") self.update_label.set("")
# Create the update button # Create the update button
self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text) self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text)
self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) self.update_btn.grid(column=4, columnspan=3, row=0, padx=0)
Tooltip(self.update_btn, _("Click to download new version"), self.tooltip_state) Tooltip(
self.update_btn, _("Click to download new version"), self.tooltip_state
)
self.download = tk.Menu(self, relief="flat") self.download = tk.Menu(self, relief="flat")
self.update_btn.configure(menu=self.download, style="Toolbutton") self.update_btn.configure(menu=self.download, style="Toolbutton")
self.download.add_command( self.download.add_command(
label=_("Download"), label=_("Download"),
command=lambda: GiteaUpdate.download( command=lambda: GiteaUpdate.download(
f"{AppConfig.DOWNLOAD_URL}/{res}.zip", f"{AppConfig.DOWNLOAD_URL}/{res}.zip",
res, res,
AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"],
AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"] AppConfig.IMAGE_PATHS["icon_msg"],
) ),
) )
@staticmethod @staticmethod
@ -373,15 +469,26 @@ class FrameWidgets(ttk.Frame):
""" """
a tk.Toplevel window a tk.Toplevel window
""" """
def link_btn() -> str | None: def link_btn() -> str | None:
webbrowser.open("https://git.ilunix.de/punix/Wire-Py") webbrowser.open("https://git.ilunix.de/punix/Wire-Py")
msg_t = _("Wire-Py a simple Wireguard Gui for Linux systems.\n\n" msg_t = _(
"Wire-Py is open source software written in Python.\n\n" "Wire-Py a simple Wireguard Gui for Linux systems.\n\n"
"Email: polunga40@unity-mail.de also likes for donation.\n\n" "Wire-Py is open source software written in Python.\n\n"
"Use without warranty!\n") "Email: polunga40@unity-mail.de also likes for donation.\n\n"
"Use without warranty!\n"
)
LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_vpn"],
AppConfig.IMAGE_PATHS["icon_vpn"],
_("Info"),
msg_t,
_("Go to Wire-Py git"),
link_btn,
)
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn)
def update_setting(self, update_res) -> None: def update_setting(self, update_res) -> None:
"""write off or on in file """write off or on in file
Args: Args:
@ -398,14 +505,16 @@ class FrameWidgets(ttk.Frame):
# When enabling updates, we need to actually check for updates # When enabling updates, we need to actually check for updates
try: try:
# Force a fresh check by passing "on" as the update setting # Force a fresh check by passing "on" as the update setting
res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, "on") res = GiteaUpdate.api_down(
AppConfig.UPDATE_URL, AppConfig.VERSION, "on"
)
# Make sure UI is updated regardless of previous state # Make sure UI is updated regardless of previous state
if hasattr(self, 'update_btn'): if hasattr(self, "update_btn"):
self.update_btn.grid_forget() self.update_btn.grid_forget()
if hasattr(self, 'updates_lb'): if hasattr(self, "updates_lb"):
self.updates_lb.grid_forget() self.updates_lb.grid_forget()
# Now update the UI with the fresh result # Now update the UI with the fresh result
self.update_ui_for_update(res) self.update_ui_for_update(res)
except Exception as e: except Exception as e:
@ -431,10 +540,10 @@ class FrameWidgets(ttk.Frame):
ConfigManager.set("tooltips", str(new_bool_state)) ConfigManager.set("tooltips", str(new_bool_state))
# Update the tooltip_state variable for immediate effect # Update the tooltip_state variable for immediate effect
self.tooltip_state.set(new_bool_state) self.tooltip_state.set(new_bool_state)
# Update the menu label # Update the menu label
self.tooltip_update_label() self.tooltip_update_label()
# Update the menu entry - find the correct index # Update the menu entry - find the correct index
# This assumes it's the third item (index 2) in your menu # This assumes it's the third item (index 2) in your menu
self.settings.entryconfigure(1, label=self.tooltip_label.get()) self.settings.entryconfigure(1, label=self.tooltip_label.get())
@ -461,8 +570,12 @@ class FrameWidgets(ttk.Frame):
""" """
Start Button Start Button
""" """
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start, self.btn_stst = ttk.Button(
command=lambda: self.wg_switch("start"), padding=0) self.lb_frame_btn_lbox,
image=self.wg_vpn_start,
command=lambda: self.wg_switch("start"),
padding=0,
)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8) self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
tl = LxTools.get_file_name(AppConfig.TEMP_DIR) tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
@ -491,12 +604,16 @@ class FrameWidgets(ttk.Frame):
""" """
View activ Tunnel in the color green or yellow View activ Tunnel in the color green or yellow
""" """
if ConfigManager.get("theme") == "light": if ConfigManager.get("theme") == "light":
self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green") self.lb_tunnel = ttk.Label(
self, textvariable=self.str_var, foreground="green"
)
else: else:
self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="yellow") self.lb_tunnel = ttk.Label(
self, textvariable=self.str_var, foreground="yellow"
)
self.lb_tunnel.config(font=("Ubuntu", 11, "bold")) self.lb_tunnel.config(font=("Ubuntu", 11, "bold"))
self.lb_tunnel.grid(column=2, padx=10, row=1) self.lb_tunnel.grid(column=2, padx=10, row=1)
@ -505,8 +622,12 @@ class FrameWidgets(ttk.Frame):
""" """
Stop Button Stop Button
""" """
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop, self.btn_stst = ttk.Button(
command=lambda: self.wg_switch("stop"), padding=0) self.lb_frame_btn_lbox,
image=self.wg_vpn_stop,
command=lambda: self.wg_switch("stop"),
padding=0,
)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8) self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state) Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state)
@ -525,58 +646,91 @@ class FrameWidgets(ttk.Frame):
Create.dir_and_files() Create.dir_and_files()
try: try:
filepath = filedialog.askopenfilename( filepath = filedialog.askopenfilename(
initialdir=f"{Path.home()}", initialdir=f"{Path.home()}",
title=_("Select Wireguard config File"), title=_("Select Wireguard config File"),
filetypes=[(_("WG config files"), "*.conf")] filetypes=[(_("WG config files"), "*.conf")],
) )
# Überprüfe, ob der Benutzer den Dialog abgebrochen hat # Überprüfe, ob der Benutzer den Dialog abgebrochen hat
if not filepath: if not filepath:
print("File import: abort by user...") print("File import: abort by user...")
return return
with open(filepath, "r", encoding="utf-8") as file: with open(filepath, "r", encoding="utf-8") as file:
read = file.read() read = file.read()
path_split = filepath.split("/") path_split = filepath.split("/")
path_split1 = path_split[-1] path_split1 = path_split[-1]
if "PrivateKey = " in read and "PublicKey = " in read and "Endpoint =" in read: if (
"PrivateKey = " in read
and "PublicKey = " in read
and "Endpoint =" in read
):
with open(filepath, "r", encoding="utf-8") as file: with open(filepath, "r", encoding="utf-8") as file:
key = Tunnel.con_to_dict(file) key = Tunnel.con_to_dict(file)
pre_key = key[3] pre_key = key[3]
if len(pre_key) != 0: if len(pre_key) != 0:
p_key = AppConfig.KEYS_FILE.read_text(encoding="utf-8") p_key = AppConfig.KEYS_FILE.read_text(encoding="utf-8")
if pre_key in p_key or f"{pre_key}\n" in p_key: if pre_key in p_key or f"{pre_key}\n" in p_key:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["imp_err"], Msg.STR["tl_exist"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["imp_err"],
Msg.STR["tl_exist"],
)
else: else:
with open(AppConfig.KEYS_FILE, "a", encoding="utf-8") as keyfile: with open(
AppConfig.KEYS_FILE, "a", encoding="utf-8"
) as keyfile:
keyfile.write(f"{pre_key}\r") keyfile.write(f"{pre_key}\r")
if len(path_split1) > 17: if len(path_split1) > 17:
p1 = shutil.copy(filepath, AppConfig.TEMP_DIR) p1 = shutil.copy(filepath, AppConfig.TEMP_DIR)
path_split = path_split1[len(path_split1) - 17:] path_split = path_split1[len(path_split1) - 17 :]
os.rename(p1, f"{AppConfig.TEMP_DIR}/{path_split}") os.rename(p1, f"{AppConfig.TEMP_DIR}/{path_split}")
new_conf = f"{AppConfig.TEMP_DIR}/{path_split}" new_conf = f"{AppConfig.TEMP_DIR}/{path_split}"
if self.a != "": if self.a != "":
check_call(["nmcli", "connection", "down", self.a]) check_call(["nmcli", "connection", "down", self.a])
self.reset_fields() self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type", "wireguard", "file", new_conf], text=True) subprocess.check_output(
[
"nmcli",
"connection",
"import",
"type",
"wireguard",
"file",
new_conf,
],
text=True,
)
Create.encrypt() Create.encrypt()
else: else:
shutil.copy(filepath, f"{AppConfig.TEMP_DIR}/") shutil.copy(filepath, f"{AppConfig.TEMP_DIR}/")
if self.a != "": if self.a != "":
check_call(["nmcli", "connection", "down", self.a]) check_call(["nmcli", "connection", "down", self.a])
self.reset_fields() self.reset_fields()
subprocess.check_output(["nmcli", "connection", "import", "type", "wireguard", "file", filepath], text=True) subprocess.check_output(
[
"nmcli",
"connection",
"import",
"type",
"wireguard",
"file",
filepath,
],
text=True,
)
Create.encrypt() Create.encrypt()
self.str_var.set("") self.str_var.set("")
self.a = Tunnel.active() self.a = Tunnel.active()
self.l_box.insert(0, self.a) self.l_box.insert(0, self.a)
@ -584,23 +738,45 @@ class FrameWidgets(ttk.Frame):
self.l_box.selection_clear(0, tk.END) self.l_box.selection_clear(0, tk.END)
self.l_box.update() self.l_box.update()
self.l_box.selection_set(0) self.l_box.selection_set(0)
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state, x_offset=-10, y_offset=-40) Tooltip(
self.wg_autostart,
Msg.TTIP["autostart"],
self.tooltip_state,
x_offset=-10,
y_offset=-40,
)
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state) Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state)
Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state) Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state)
Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], self.tooltip_state) Tooltip(
self.btn_rename, Msg.TTIP["rename_tl"], self.tooltip_state
)
self.lb_rename.insert(0, "Max. 12 characters!") self.lb_rename.insert(0, "Max. 12 characters!")
self.str_var = tk.StringVar() self.str_var = tk.StringVar()
self.str_var.set(self.a) self.str_var.set(self.a)
self.color_label() self.color_label()
self.stop() self.stop()
data = self.handle_tunnel_data(self.a) data = self.handle_tunnel_data(self.a)
check_call(["nmcli", "con", "mod", self.a, "connection.autoconnect", "no"]) check_call(
[
"nmcli",
"con",
"mod",
self.a,
"connection.autoconnect",
"no",
]
)
elif ("PrivateKey = " in read) and ("Endpoint = " in read): elif ("PrivateKey = " in read) and ("Endpoint = " in read):
pass pass
else: else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["imp_err"], Msg.STR["no_valid_file"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["imp_err"],
Msg.STR["no_valid_file"],
)
except EOFError as e: except EOFError as e:
print(e) print(e)
@ -618,15 +794,16 @@ class FrameWidgets(ttk.Frame):
try: try:
self.select_tunnel = self.l_box.curselection() self.select_tunnel = self.l_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.l_box.get(self.select_tunnel[0])
with open(f"/tmp/tlecdcwg/{select_tl}.conf", "r+", encoding="utf-8") as file2: with open(
f"/tmp/tlecdcwg/{select_tl}.conf", "r+", encoding="utf-8"
) as file2:
key = Tunnel.con_to_dict(file2) key = Tunnel.con_to_dict(file2)
pre_key = key[3] pre_key = key[3]
check_call(["nmcli", "connection", "delete", select_tl]) check_call(["nmcli", "connection", "delete", select_tl])
self.l_box.delete(self.select_tunnel[0]) self.l_box.delete(self.select_tunnel[0])
with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f6: with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f6:
lines6 = set_f6.readlines() lines6 = set_f6.readlines()
if (select_tl == lines6[7].strip() if select_tl == lines6[7].strip() and "off\n" not in lines6[7].strip():
and "off\n" not in lines6[7].strip()):
lines6[7] = "off\n" lines6[7] = "off\n"
with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as set_f7: with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as set_f7:
set_f7.writelines(lines6) set_f7.writelines(lines6)
@ -637,7 +814,9 @@ class FrameWidgets(ttk.Frame):
Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat") Path.unlink(f"{Path.home()}/.config/wire_py/{select_tl}.dat")
Path.unlink(f"/tmp/tlecdcwg/{select_tl}.conf") Path.unlink(f"/tmp/tlecdcwg/{select_tl}.conf")
with open(AppConfig.KEYS_FILE, "r", encoding="utf-8") as readfile: with open(AppConfig.KEYS_FILE, "r", encoding="utf-8") as readfile:
with open(f"{Path.home()}/.config/wire_py/keys2", "w", encoding="utf-8") as writefile: with open(
f"{Path.home()}/.config/wire_py/keys2", "w", encoding="utf-8"
) as writefile:
for line in readfile: for line in readfile:
if pre_key not in line.strip("\n"): if pre_key not in line.strip("\n"):
writefile.write(line) writefile.write(line)
@ -650,8 +829,13 @@ class FrameWidgets(ttk.Frame):
if self.l_box.size() == 0: if self.l_box.size() == 0:
self.wg_autostart.configure(state="disabled") self.wg_autostart.configure(state="disabled")
self.lb_rename.configure(state="disabled") self.lb_rename.configure(state="disabled")
Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"] Tooltip(
, self.tooltip_state, x_offset=30, y_offset=-50) self.wg_autostart,
Msg.TTIP["autostart_info"],
self.tooltip_state,
x_offset=30,
y_offset=-50,
)
Tooltip(self.btn_exp, Msg.TTIP["export_tl_info"], self.tooltip_state) Tooltip(self.btn_exp, Msg.TTIP["export_tl_info"], self.tooltip_state)
Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state) Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state)
@ -668,11 +852,21 @@ class FrameWidgets(ttk.Frame):
if self.l_box.size() != 0: if self.l_box.size() != 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["sel_list"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["sel_list"],
)
else: else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["tl_first"],
)
def enable_check_box(self, _) -> None: def enable_check_box(self, _) -> None:
""" """
@ -691,7 +885,11 @@ class FrameWidgets(ttk.Frame):
Set (on), the selected tunnel is displayed in the label. Set (on), the selected tunnel is displayed in the label.
At (off) the label is first emptied then filled with No Autoconnect At (off) the label is first emptied then filled with No Autoconnect
""" """
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines = (
Path(AppConfig.SETTINGS_FILE)
.read_text(encoding="utf-8")
.splitlines(keepends=True)
)
if lines[7] != "off\n": if lines[7] != "off\n":
print(f"{lines[7]} starts automatically when the system starts.") print(f"{lines[7]} starts automatically when the system starts.")
@ -707,7 +905,12 @@ class FrameWidgets(ttk.Frame):
self.autoconnect_var = tk.StringVar() self.autoconnect_var = tk.StringVar()
self.autoconnect_var.set(self.auto_con) self.autoconnect_var.set(self.auto_con)
self.autoconnect = ttk.Label(self.lb_frame3, textvariable=self.autoconnect_var, foreground="#0071ff", width=15) self.autoconnect = ttk.Label(
self.lb_frame3,
textvariable=self.autoconnect_var,
foreground="#0071ff",
width=15,
)
self.autoconnect.config(font=("Ubuntu", 11)) self.autoconnect.config(font=("Ubuntu", 11))
self.autoconnect.grid(column=1, row=0, sticky="e", pady=19) self.autoconnect.grid(column=1, row=0, sticky="e", pady=19)
@ -727,9 +930,15 @@ class FrameWidgets(ttk.Frame):
select_tl = self.l_box.get(select_tunnel[0]) select_tl = self.l_box.get(select_tunnel[0])
if self.selected_option.get() == 0: if self.selected_option.get() == 0:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines = (
lines[7] = 'off\n' Path(AppConfig.SETTINGS_FILE)
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") .read_text(encoding="utf-8")
.splitlines(keepends=True)
)
lines[7] = "off\n"
Path(AppConfig.SETTINGS_FILE).write_text(
"".join(lines), encoding="utf-8"
)
tl = LxTools.get_file_name(AppConfig.TEMP_DIR) tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
@ -737,9 +946,15 @@ class FrameWidgets(ttk.Frame):
self.wg_autostart.configure(state="disabled") self.wg_autostart.configure(state="disabled")
if self.selected_option.get() >= 1: if self.selected_option.get() >= 1:
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) lines = (
Path(AppConfig.SETTINGS_FILE)
.read_text(encoding="utf-8")
.splitlines(keepends=True)
)
lines[7] = select_tl lines[7] = select_tl
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8") Path(AppConfig.SETTINGS_FILE).write_text(
"".join(lines), encoding="utf-8"
)
except IndexError: except IndexError:
self.selected_option.set(1) self.selected_option.set(1)
@ -755,19 +970,39 @@ class FrameWidgets(ttk.Frame):
if len(self.lb_rename.get()) > 12: if len(self.lb_rename.get()) > 12:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sign_len"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["sign_len"],
)
elif len(self.lb_rename.get()) == 0: elif len(self.lb_rename.get()) == 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["zero_signs"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["zero_signs"],
)
elif any(ch in special_characters for ch in self.lb_rename.get()): elif any(ch in special_characters for ch in self.lb_rename.get()):
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["false_signs"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["false_signs"],
)
elif self.lb_rename.get() in name_of_file: elif self.lb_rename.get() in name_of_file:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["is_in_use"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["is_in_use"],
)
else: else:
@ -776,7 +1011,17 @@ class FrameWidgets(ttk.Frame):
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.l_box.get(self.select_tunnel[0])
# nmcli connection modify old connection.id iphone # nmcli connection modify old connection.id iphone
subprocess.check_output(["nmcli", "connection", "modify", select_tl, "connection.id", self.lb_rename.get()], text=True) subprocess.check_output(
[
"nmcli",
"connection",
"modify",
select_tl,
"connection.id",
self.lb_rename.get(),
],
text=True,
)
source = Path(f"/tmp/tlecdcwg/{select_tl}.conf") source = Path(f"/tmp/tlecdcwg/{select_tl}.conf")
destination = source.with_name(f"{self.lb_rename.get()}.conf") destination = source.with_name(f"{self.lb_rename.get()}.conf")
source.replace(destination) source.replace(destination)
@ -787,12 +1032,14 @@ class FrameWidgets(ttk.Frame):
self.l_box.update() self.l_box.update()
new_a_connect = self.lb_rename.get() new_a_connect = self.lb_rename.get()
self.lb_rename.delete(0, tk.END) self.lb_rename.delete(0, tk.END)
with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f5: with open(AppConfig.SETTINGS_FILE, "r", encoding="utf-8") as set_f5:
lines5 = set_f5.readlines() lines5 = set_f5.readlines()
if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip(): if select_tl == lines5[7].strip() and "off\n" not in lines5[7].strip():
lines5[7] = new_a_connect lines5[7] = new_a_connect
with open(AppConfig.SETTINGS_FILE, "w", encoding="utf-8") as theme_set5: with open(
AppConfig.SETTINGS_FILE, "w", encoding="utf-8"
) as theme_set5:
theme_set5.writelines(lines5) theme_set5.writelines(lines5)
self.autoconnect_var.set(value=new_a_connect) self.autoconnect_var.set(value=new_a_connect)
self.update_connection_display() self.update_connection_display()
@ -800,14 +1047,19 @@ class FrameWidgets(ttk.Frame):
except IndexError: except IndexError:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["ren_err"], Msg.STR["sel_list"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["sel_list"],
)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
pass pass
except EOFError as e: except EOFError as e:
print(e) print(e)
def init_and_report(self, data=None) -> None: def init_and_report(self, data=None) -> None:
""" """
Displays the value address, DNS and peer in the labels Displays the value address, DNS and peer in the labels
@ -826,7 +1078,9 @@ class FrameWidgets(ttk.Frame):
shows data in the label shows data in the label
""" """
# Address Label # Address Label
self.address = ttk.Label(self.lb_frame, textvariable=self.add, foreground="#0071ff") self.address = ttk.Label(
self.lb_frame, textvariable=self.add, foreground="#0071ff"
)
self.address.grid(column=0, row=5, sticky="w", padx=10, pady=6) self.address.grid(column=0, row=5, sticky="w", padx=10, pady=6)
self.address.config(font=("Ubuntu", 9)) self.address.config(font=("Ubuntu", 9))
@ -836,7 +1090,9 @@ class FrameWidgets(ttk.Frame):
self.dns.config(font=("Ubuntu", 9)) self.dns.config(font=("Ubuntu", 9))
# Endpoint Label # Endpoint Label
self.endpoint = ttk.Label(self.lb_frame2, textvariable=self.enp, foreground="#0071ff") self.endpoint = ttk.Label(
self.lb_frame2, textvariable=self.enp, foreground="#0071ff"
)
self.endpoint.grid(column=0, row=8, sticky="w", padx=10, pady=20) self.endpoint.grid(column=0, row=8, sticky="w", padx=10, pady=20)
self.endpoint.config(font=("Ubuntu", 9)) self.endpoint.config(font=("Ubuntu", 9))
@ -851,7 +1107,7 @@ class FrameWidgets(ttk.Frame):
self.handle_connection_state("start", select_tl) self.handle_connection_state("start", select_tl)
else: else:
data = self.handle_tunnel_data(self.a) data = self.handle_tunnel_data(self.a)
if data: if data:
@ -861,11 +1117,21 @@ class FrameWidgets(ttk.Frame):
if self.l_box.size() != 0: if self.l_box.size() != 0:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["sel_list"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["sel_list"],
)
else: else:
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_msg"], Msg.STR["sel_tl"], Msg.STR["tl_first"]) LxTools.msg_window(
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["tl_first"],
)
def handle_connection_state(self, action: str, tunnel_name: str = None) -> None: def handle_connection_state(self, action: str, tunnel_name: str = None) -> None:
""" """

View File

@ -6,9 +6,10 @@ import locale
from pathlib import Path from pathlib import Path
from typing import Dict, Any from typing import Dict, Any
class AppConfig: class AppConfig:
"""Central configuration class for Wire-Py application""" """Central configuration class for Wire-Py application"""
# Localization # Localization
APP_NAME: str = "wirepy" APP_NAME: str = "wirepy"
LOCALE_DIR: Path = Path("/usr/share/locale/") LOCALE_DIR: Path = Path("/usr/share/locale/")
@ -23,32 +24,30 @@ class AppConfig:
SETTINGS_FILE: Path = CONFIG_DIR / "settings" SETTINGS_FILE: Path = CONFIG_DIR / "settings"
KEYS_FILE: Path = CONFIG_DIR / "keys" KEYS_FILE: Path = CONFIG_DIR / "keys"
AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service" AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service"
# Updates # Updates
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year # 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
VERSION: str = "v. 2.04.1725" VERSION: str = "v. 2.04.1725"
UPDATE_URL: str = "https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases" 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" DOWNLOAD_URL: str = "https://git.ilunix.de/punix/Wire-Py/archive"
# UI configuration # UI configuration
UI_CONFIG: Dict[str, Any] = { UI_CONFIG: Dict[str, Any] = {
"window_title": "Wire-Py", "window_title": "Wire-Py",
"window_size": (600, 383), "window_size": (600, 383),
"font_family": "Ubuntu", "font_family": "Ubuntu",
"font_size": 11, "font_size": 11,
"resizable_window": (False, False) "resizable_window": (False, False),
} }
# System-dependent paths # System-dependent paths
SYSTEM_PATHS: Dict[str, str]= { SYSTEM_PATHS: Dict[str, str] = {
"ssl_decrypt": "/usr/local/bin/ssl_decrypt.py", "ssl_decrypt": "/usr/local/bin/ssl_decrypt.py",
"ssl_encrypt": "/usr/local/bin/ssl_encrypt.py", "ssl_encrypt": "/usr/local/bin/ssl_encrypt.py",
"tcl_path": "/usr/share/TK-Themes", "tcl_path": "/usr/share/TK-Themes",
"pkey_path": "/usr/local/etc/ssl/pwgk.pem" "pkey_path": "/usr/local/etc/ssl/pwgk.pem",
} }
# Images and icons paths # Images and icons paths
IMAGE_PATHS: Dict[str, str] = { IMAGE_PATHS: Dict[str, str] = {
"icon_vpn": "/usr/share/icons/lx-icons/48/wg_vpn.png", "icon_vpn": "/usr/share/icons/lx-icons/48/wg_vpn.png",
@ -59,16 +58,15 @@ class AppConfig:
"icon_start": "/usr/share/icons/lx-icons/48/wg_vpn-start.png", "icon_start": "/usr/share/icons/lx-icons/48/wg_vpn-start.png",
"icon_stop": "/usr/share/icons/lx-icons/48/wg_vpn-stop.png", "icon_stop": "/usr/share/icons/lx-icons/48/wg_vpn-stop.png",
"icon_info": "/usr/share/icons/lx-icons/64/info.png", "icon_info": "/usr/share/icons/lx-icons/64/info.png",
"icon_error": "/usr/share/icons/lx-icons/64/error.png" "icon_error": "/usr/share/icons/lx-icons/64/error.png",
}
}
@staticmethod @staticmethod
def setup_translations() -> gettext.gettext: def setup_translations() -> gettext.gettext:
""" """
Initialize translations and set the translation function Initialize translations and set the translation function
Special method for translating strings in this file Special method for translating strings in this file
Returns: Returns:
The gettext translation function The gettext translation function
""" """
@ -82,14 +80,16 @@ class AppConfig:
"""Ensures that all required directories exist""" """Ensures that all required directories exist"""
cls.CONFIG_DIR.mkdir(parents=True, exist_ok=True) cls.CONFIG_DIR.mkdir(parents=True, exist_ok=True)
cls.TEMP_DIR.mkdir(parents=True, exist_ok=True) cls.TEMP_DIR.mkdir(parents=True, exist_ok=True)
@classmethod @classmethod
def create_default_settings(cls) -> None: def create_default_settings(cls) -> None:
"""Creates default settings if they don't exist""" """Creates default settings if they don't exist"""
if not cls.SETTINGS_FILE.exists(): if not cls.SETTINGS_FILE.exists():
content = "\n".join(f"[{k.upper()}]\n{v}" for k, v in cls.DEFAULT_SETTINGS.items()) content = "\n".join(
f"[{k.upper()}]\n{v}" for k, v in cls.DEFAULT_SETTINGS.items()
)
cls.SETTINGS_FILE.write_text(content) cls.SETTINGS_FILE.write_text(content)
@classmethod @classmethod
def get_image_paths(cls) -> Dict[str, Path]: def get_image_paths(cls) -> Dict[str, Path]:
"""Returns paths to UI images""" """Returns paths to UI images"""
@ -97,14 +97,14 @@ class AppConfig:
"main_icon": cls.SYSTEM_PATHS["image_path"] / "48/wg_vpn.png", "main_icon": cls.SYSTEM_PATHS["image_path"] / "48/wg_vpn.png",
"warning": cls.CONFIG_DIR / "images/warning.png", "warning": cls.CONFIG_DIR / "images/warning.png",
"success": cls.CONFIG_DIR / "images/success.png", "success": cls.CONFIG_DIR / "images/success.png",
"error": cls.CONFIG_DIR / "images/error.png" "error": cls.CONFIG_DIR / "images/error.png",
} }
@classmethod @classmethod
def get_autostart_content(cls) -> str: def get_autostart_content(cls) -> str:
"""Returns the content for the autostart service file""" """Returns the content for the autostart service file"""
return """[Unit]
Description=Automatic Tunnel Start return """[Unit]Description=Automatic Tunnel Start
After=network-online.target After=network-online.target
[Service] [Service]
@ -114,29 +114,32 @@ ExecStart=/usr/local/bin/start_wg.py
[Install] [Install]
WantedBy=default.target""" WantedBy=default.target"""
# here is inizialize the class for translate strrings # here is inizialize the class for translate strrings
_ = AppConfig.setup_translations() _ = AppConfig.setup_translations()
class Msg: class Msg:
""" """
A utility class that provides centralized access to translated message strings. A utility class that provides centralized access to translated message strings.
This class contains a dictionary of message strings used throughout the Wire-Py application. This class contains a dictionary of message strings used throughout the Wire-Py application.
All strings are prepared for translation using gettext. The short key names make the code All strings are prepared for translation using gettext. The short key names make the code
more concise while maintaining readability. more concise while maintaining readability.
Attributes: Attributes:
STR (dict): A dictionary mapping short keys to translated message strings. STR (dict): A dictionary mapping short keys to translated message strings.
Keys are abbreviated for brevity but remain descriptive. Keys are abbreviated for brevity but remain descriptive.
Usage: Usage:
Import this class and access messages using the dictionary: Import this class and access messages using the dictionary:
`Msg.STR["sel_tl"]` returns the translated "Select tunnel" message. `Msg.STR["sel_tl"]` returns the translated "Select tunnel" message.
Note: Note:
Ensure that gettext translation is properly initialized before Ensure that gettext translation is properly initialized before
accessing these strings to ensure correct localization. accessing these strings to ensure correct localization.
""" """
STR: Dict[str, str] = { STR: Dict[str, str] = {
# Strings for messages # Strings for messages
"sel_tl": _("Select tunnel"), "sel_tl": _("Select tunnel"),
@ -150,31 +153,38 @@ class Msg:
"sel_list": _("Please select a tunnel from the list"), "sel_list": _("Please select a tunnel from the list"),
"sign_len": _("The new name may contain only 12 characters"), "sign_len": _("The new name may contain only 12 characters"),
"zero_signs": _("At least one character must be entered"), "zero_signs": _("At least one character must be entered"),
"false signs": _("No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n"), "false signs": _(
"No valid sign. These must not be used.\nBlank, Slash, Backslash and { }\n"
),
"is_in_use": _("The tunnel is already in use"), "is_in_use": _("The tunnel is already in use"),
"no_valid_file": _("Oh... no valid Wireguard File!\nPlease select a valid Wireguard File"), "no_valid_file": _(
"tl_exist": _("Tunnel already available!\nPlease use another file for import") "Oh... no valid Wireguard File!\nPlease select a valid Wireguard File"
),
} "tl_exist": _("Tunnel already available!\nPlease use another file for import"),
}
TTIP: Dict[str, str] = { TTIP: Dict[str, str] = {
#Strings for Tooltips # Strings for Tooltips
"settings": _("Click for Settings"), "settings": _("Click for Settings"),
"import_tl": _("Click to import a Wireguard Tunnel"), "import_tl": _("Click to import a Wireguard Tunnel"),
"start_tl": _("Click to start selected Wireguard Tunnel"), "start_tl": _("Click to start selected Wireguard Tunnel"),
"empty_list": _("No tunnels to start in the list"), "empty_list": _("No tunnels to start in the list"),
"stop_tl": _("Click to stop selected Wireguard Tunnel"), "stop_tl": _("Click to stop selected Wireguard Tunnel"),
"del_tl": _("Click to delete selected Wireguard Tunnel"), "del_tl": _("Click to delete selected Wireguard Tunnel"),
"rename_tl": _("To rename a tunnel, you need to\nselect a tunnel from the list"), "rename_tl": _(
"To rename a tunnel, you need to\nselect a tunnel from the list"
),
"export_tl": _(" Click to export all\nWireguard Tunnel to Zipfile"), "export_tl": _(" Click to export all\nWireguard Tunnel to Zipfile"),
"trash_tl": _("Click to delete a Wireguard Tunnel\nSelect from the list!"), "trash_tl": _("Click to delete a Wireguard Tunnel\nSelect from the list!"),
"autostart": _("To use the autostart, enable this Checkbox"), "autostart": _("To use the autostart, enable this Checkbox"),
"autostart_info": _("You must have at least one\ntunnel in the list,to use the autostart"), "autostart_info": _(
"You must have at least one\ntunnel in the list,to use the autostart"
),
"export_tl_info": _("No Tunnels in List for Export"), "export_tl_info": _("No Tunnels in List for Export"),
"start_tl_info": _("Click to start selected Wireguard Tunnel"), "start_tl_info": _("Click to start selected Wireguard Tunnel"),
"rename_tl_info": _("To rename a tunnel, at least one must be in the list"), "rename_tl_info": _("To rename a tunnel, at least one must be in the list"),
"trash_tl_info": _("No tunnels to delete in the list"), "trash_tl_info": _("No tunnels to delete in the list"),
"list_auto_info": _("To use the autostart, a tunnel must be selected from the list"), "list_auto_info": _(
"download": _("Click to download new version") "To use the autostart, a tunnel must be selected from the list"
),
"download": _("Click to download new version"),
} }