8 Commits

38 changed files with 412 additions and 298 deletions

View File

@ -6,9 +6,43 @@ My standard System: Linux Mint 22 Cinnamon
- os import in common_tools.py replaced by other methods - os import in common_tools.py replaced by other methods
- If Wire-Py already runs, prevent further start - If Wire-Py already runs, prevent further start
- for loops with lists replaced by List Comprehensions - for loops with lists replaced by List Comprehensions
- Tunnel in tk.canvas for modern look
- Replace Download Button with Lx Tools installer - Replace Download Button with Lx Tools installer
### Added
02-07-2025
- Complete code for faulty f" string configuration dur addicted and fixed
- updater replace Downloadbutton with Lx Tools installer
- remove logviewer icon
- add settings.png icon for update button
### Added
27-06-2025
- Header added for more modern desing
- Sizes adjust the frames and labels improve
- More modern desing for listbox, Address, Dns and Endpoint
- ui works now better with rename button
- add Image class for manage Images
### Added
23-06-2025
- all msg_window with MassageDialog replaced
### Added
14-06-2025
- replace msg_window with MassageDialog
- vpn stop icon corrected
### Added ### Added
07-06-2025 07-06-2025

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
lx-icons/128/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 10 KiB

BIN
lx-icons/16/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

BIN
lx-icons/16/wg_vpn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
lx-icons/256/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
lx-icons/32/Lunix_Tools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
lx-icons/32/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
lx-icons/32/wg_vpn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
lx-icons/48/Lunix_Tools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
lx-icons/48/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
lx-icons/64/Lunix_Tools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
lx-icons/64/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -6,25 +6,23 @@ import pwd
import shutil import shutil
from subprocess import CompletedProcess, run from subprocess import CompletedProcess, run
from shared_libs.wp_app_config import AppConfig from shared_libs.wp_app_config import AppConfig
from shared_libs.common_tools import LxTools from shared_libs.message import MessageDialog
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--user", required=True, help="Username of the target file system") parser.add_argument("--user", required=True, help="Username of the target file system")
args = parser.parse_args() args = parser.parse_args()
_ = AppConfig.setup_translations()
try: try:
# Retrieve UID and GID # Retrieve UID and GID
user_info = pwd.getpwnam(args.user) user_info = pwd.getpwnam(args.user)
uid = user_info.pw_uid # User ID (e.g., 1000) uid = user_info.pw_uid # User ID (e.g., 1000)
gid = user_info.pw_gid # Group ID (e.g., 1000) gid = user_info.pw_gid # Group ID (e.g., 1000)
except KeyError: except KeyError:
LxTools.msg_window( MessageDialog(
AppConfig.IMAGE_PATHS["icon_error"], "error", _(f"User '{args.user}' not found."), title="Error decrypt"
AppConfig.IMAGE_PATHS["icon_error"], ).show()
"Error decrypt",
f"User '{args.user}' not found.",
exc_info=True,
)
exit(1) exit(1)
crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py") crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py")
@ -32,9 +30,9 @@ crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py")
if len([str(file) for file in crypted_tunnel.glob("*.dat")]) == 0: if len([str(file) for file in crypted_tunnel.glob("*.dat")]) == 0:
pass pass
else: else:
crypted__tunnel = [str(file) for file in crypted_tunnel.glob("*.dat")] crypted_tunnel = [str(file) for file in crypted_tunnel.glob("*.dat")]
for tunnel_path in crypted__tunnel: for tunnel_path in crypted_tunnel:
base_name = Path(tunnel_path).stem base_name = Path(tunnel_path).stem
@ -52,15 +50,16 @@ else:
], ],
capture_output=True, capture_output=True,
text=True, text=True,
check=False, check=True,
) )
shutil.chown(f"{AppConfig.TEMP_DIR}/{base_name}.conf", uid, gid) shutil.chown(f"{AppConfig.TEMP_DIR}/{base_name}.conf", uid, gid)
# Output from Openssl Error # Output from Openssl Error
if process.stderr: if process.stderr:
LxTools.msg_window( MessageDialog(
AppConfig.IMAGE_PATHS["icon_error"], "error",
AppConfig.IMAGE_PATHS["icon_error"], _(
"Error decrypt", f"{process.stderr} Error by [{tunnel_path}] Code: {process.returncode}"
f"{process.stderr} Error by [{tunnel_path}] Code: {process.returncode}", ),
) title="Error decrypt",
).show()

View File

@ -9,6 +9,7 @@ from subprocess import run, CompletedProcess
import secrets import secrets
from shared_libs.wp_app_config import AppConfig, Msg from shared_libs.wp_app_config import AppConfig, Msg
from shared_libs.common_tools import LxTools, CryptoUtil from shared_libs.common_tools import LxTools, CryptoUtil
from shared_libs.message import MessageDialog
# Translate # Translate
_ = AppConfig.setup_translations() _ = AppConfig.setup_translations()
@ -172,12 +173,8 @@ class Tunnel:
CryptoUtil.decrypt(getpass.getuser()) CryptoUtil.decrypt(getpass.getuser())
if len([file.name for file in AppConfig.TEMP_DIR.glob("*.conf")]) == 0: if len([file.name for file in AppConfig.TEMP_DIR.glob("*.conf")]) == 0:
LxTools.msg_window( MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["tl_first"],
)
return False return False
else: else:
wg_tar: str = f"{AppConfig.BASE_DIR}/{now_datetime}" wg_tar: str = f"{AppConfig.BASE_DIR}/{now_datetime}"
@ -186,22 +183,20 @@ class Tunnel:
with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf: with zipfile.ZipFile(f"{wg_tar}.zip", "r") as zf:
if zf.namelist(): if zf.namelist():
LxTools.msg_window( MessageDialog(
AppConfig.IMAGE_PATHS["icon_info"], "info",
AppConfig.IMAGE_PATHS["icon_vpn"],
Msg.STR["exp_succ"],
Msg.STR["exp_in_home"], Msg.STR["exp_in_home"],
title=Msg.STR["exp_succ"],
) )
else: else:
logging.error( logging.error(
"There was a mistake at creating the Zip file. File is empty." "There was a mistake at creating the Zip file. File is empty."
) )
LxTools.msg_window( MessageDialog(
AppConfig.IMAGE_PATHS["icon_error"], "error", Msg.STR["exp_zip"], title=Msg.STR["exp_err"]
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["exp_err"],
Msg.STR["exp_zip"],
) )
return False return False
return True return True
except PermissionError: except PermissionError:
@ -217,12 +212,7 @@ class Tunnel:
pass pass
except Exception as e: except Exception as e:
logging.error(f"Export failed: {str(e)}") logging.error(f"Export failed: {str(e)}")
LxTools.msg_window( MessageDialog("error", Msg.STR["exp_try"], title=Msg.STR["exp_err"])
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["exp_err"],
Msg.STR["exp_try"],
)
return False return False
finally: finally:

524
wirepy.py
View File

@ -6,13 +6,16 @@ import logging
import getpass import getpass
import shutil import shutil
import sys import sys
import os
import subprocess
import tkinter as tk import tkinter as tk
import webbrowser import webbrowser
from functools import partial
from pathlib import Path from pathlib import Path
from subprocess import CompletedProcess, run from subprocess import CompletedProcess, run
from tkinter import TclError, filedialog, ttk from tkinter import TclError, filedialog, ttk
from tunnel import Tunnel from tunnel import Tunnel
from shared_libs.message import MessageDialog
from shared_libs.gitea import GiteaUpdate from shared_libs.gitea import GiteaUpdate
from shared_libs.common_tools import ( from shared_libs.common_tools import (
LxTools, LxTools,
@ -23,7 +26,7 @@ from shared_libs.common_tools import (
Tooltip, Tooltip,
) )
from shared_libs.wp_app_config import AppConfig, Msg from shared_libs.wp_app_config import AppConfig, Image, Msg
class Wirepy(tk.Tk): class Wirepy(tk.Tk):
@ -38,6 +41,7 @@ class Wirepy(tk.Tk):
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]
@ -47,23 +51,29 @@ class Wirepy(tk.Tk):
AppConfig.UI_CONFIG["resizable_window"][0], AppConfig.UI_CONFIG["resizable_window"][0],
AppConfig.UI_CONFIG["resizable_window"][1], AppConfig.UI_CONFIG["resizable_window"][1],
) )
self.minsize(
AppConfig.UI_CONFIG["window_size"][0],
AppConfig.UI_CONFIG["window_size"][1],
)
self.title(AppConfig.UI_CONFIG["window_title"]) self.title(AppConfig.UI_CONFIG["window_title"])
self.image_manager = Image()
self.columnconfigure(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")
ConfigManager.init(AppConfig.SETTINGS_FILE) ConfigManager.init(AppConfig.SETTINGS_FILE)
theme = ConfigManager.get("theme") theme = ConfigManager.get("theme")
ThemeManager.change_theme(self, theme) ThemeManager.change_theme(self, theme)
# Load the image file from the disk # Try to set icon
self.wg_icon = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"]) try:
icon = self.image_manager.load_image("icon_vpn")
# Set it as the window icon if icon:
self.iconphoto(True, self.wg_icon) self.iconphoto(True, icon)
except:
pass
# Add the widgets # Add the widgets
FrameWidgets(self).grid() FrameWidgets(self).grid()
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# 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)
@ -86,13 +96,14 @@ class FrameWidgets(ttk.Frame):
self.dns = None self.dns = None
self.address = None self.address = None
self.auto_con = None self.auto_con = None
self.style = ttk.Style() self.image_manager = Image()
self.wg_vpn_start = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_start"]) self.columnconfigure(0, weight=1)
self.wg_vpn_stop = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_stop"]) self.rowconfigure(0, weight=1)
self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"]) self.columnconfigure(1, weight=1)
self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"]) self.rowconfigure(1, weight=1)
self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"]) self.columnconfigure(2, weight=18)
self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"]) self.rowconfigure(2, weight=1)
self.rowconfigure(3, weight=1)
# StringVar-Variables initialization # StringVar-Variables initialization
self.tooltip_state = tk.BooleanVar() self.tooltip_state = tk.BooleanVar()
@ -121,20 +132,10 @@ class FrameWidgets(ttk.Frame):
# Frame for Menu # Frame for Menu
self.menu_frame = ttk.Frame(self) self.menu_frame = ttk.Frame(self)
self.menu_frame.configure(relief="flat") self.menu_frame.grid(column=0, columnspan=3, row=1, sticky="we")
self.menu_frame.grid(column=0, row=0, columnspan=4, sticky="w")
# App Menu
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: {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=0, row=0)
Tooltip(self.options_btn, Msg.TTIP["settings"], self.tooltip_state) Tooltip(self.options_btn, Msg.TTIP["settings"], self.tooltip_state)
@ -146,9 +147,8 @@ class FrameWidgets(ttk.Frame):
command=lambda: self.update_setting(self.set_update.get()), command=lambda: self.update_setting(self.set_update.get()),
variable=self.set_update, 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=2, row=0)
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)
@ -176,69 +176,148 @@ class FrameWidgets(ttk.Frame):
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=1, row=0)
self.readme = tk.Menu(self)
self.a = Tunnel.get_active() self.a = Tunnel.get_active()
# Label Frame 1 # Header Frame
self.lb_frame_btn_lbox = ttk.Frame(self) # Festlegen der Farbe
self.lb_frame_btn_lbox.configure(relief="flat") self.header_frame = tk.Frame(self, bg="#2c3e50")
self.lb_frame_btn_lbox.grid(column=0, rowspan=3, row=1) self.wg_icon_header_frame = tk.Frame(self.header_frame, bg="#2c3e50")
self.header_label = tk.Label(
# Label Frame 2 self.header_frame,
self.lb_frame = ttk.Frame(self) text=_("Lx Tools Wire-Py"),
self.lb_frame.configure(relief="solid") font=("Helvetica", 12, "bold"),
self.lb_frame.grid(column=2, row=2, sticky="snew", padx=20, pady=5) fg="#ffffff",
bg="#2c3e50",
# Label Frame 3
self.lb_frame2 = ttk.Frame(self)
self.lb_frame2.configure(relief="solid")
self.lb_frame2.grid(column=2, row=3, sticky="snew", padx=20, pady=5)
# Bottom Frame 4
self.lb_frame3 = ttk.Frame(self)
self.lb_frame3.configure(relief="flat")
self.lb_frame3.grid(
column=0, row=5, columnspan=4, sticky="snew", padx=2, pady=2
) )
# Bottom Frame 5 self.version_label = tk.Label(
self.lb_frame4 = ttk.Frame(self) self.header_frame,
self.lb_frame4.configure(relief="flat") text=f"{AppConfig.VERSION}{Msg.STR['header_left_bottom']}",
self.lb_frame4.grid(column=2, row=5, columnspan=3, sticky="e", padx=15) font=("Helvetica", 9),
fg="#bdc3c7",
bg="#2c3e50",
)
self.info_label = tk.Label(
self.header_frame,
text=Msg.STR["header_right_top"],
font=("Helvetica", 10),
fg="#ecf0f1",
bg="#2c3e50",
)
self.header_frame.grid(column=0, columnspan=3, row=0, sticky="nsew")
self.wg_icon_header_frame.grid(column=0, row=0, rowspan=2, sticky="w")
self.wg_icon_header_label = tk.Label(
self.wg_icon_header_frame,
image=self.image_manager.load_image("icon_header"),
bg="#2c3e50",
)
self.wg_icon_header_label.grid(column=0, row=0, sticky="e", ipadx=10)
self.header_label.grid(
column=1,
row=0,
sticky="w",
padx=(5, 20),
pady=(10, 0),
ipady=4,
)
self.version_label.grid(column=1, row=1, sticky="w", padx=(5, 20), pady=(0, 10))
self.info_label.grid(column=2, row=0, sticky="ne", padx=(10, 10), pady=(10, 0))
self.header_frame.columnconfigure(1, weight=1, pad=2)
self.header_frame.rowconfigure(0, weight=1)
# Frame for Control Buttons (Start, Stop, Import, Trash, Export)
self.control_buttons_frame = ttk.Frame(self)
self.control_buttons_frame.grid(column=0, row=2, sticky="w", padx=(15, 0))
self.control_buttons_frame.columnconfigure(0, weight=1)
self.control_buttons_frame.rowconfigure(2, weight=1)
# Frame for Listbox and Scrollbar
self.list_container_frame = ttk.Frame(self)
self.list_container_frame.grid(column=1, row=2, sticky="nsew", pady=3)
self.list_container_frame.columnconfigure(1, weight=1)
self.list_container_frame.rowconfigure(2, weight=1)
self.list_frame = ttk.LabelFrame(self.list_container_frame, text=_("Tunnels"))
self.list_frame.grid(column=0, row=0, sticky="nsew", padx=10, ipady=25)
# Listbox with Scrollbar
self.list_box = tk.Listbox(self.list_frame, selectmode="single")
self.list_box.config(
relief="flat",
font=("Ubuntu", 12, "bold"),
)
self.list_box.grid(column=0, row=0, sticky="nsew")
self.list_box.event_add("<<ClickEvent>>", "<Button-1>")
self.list_box.bind("<<ClickEvent>>", self.enable_check_box)
self.scrollbar = ttk.Scrollbar(
self.list_frame, orient="vertical", command=self.list_box.yview
)
self.scrollbar.grid(column=1, row=0, sticky="ns")
self.list_box.configure(yscrollcommand=self.scrollbar.set)
self.scrollbar.columnconfigure(1, weight=1)
self.scrollbar.rowconfigure(0, weight=1)
# Frame for Active Tunnel, Interface and Peer
# Right Side Frame
self.right_side_frame = ttk.Frame(self)
self.right_side_frame.grid(column=2, row=2, sticky="nsew")
self.right_side_frame.columnconfigure(2, weight=1)
self.right_side_frame.rowconfigure(2, weight=1)
# Show active Label # Show active Label
self.select_tunnel = None self.select_tunnel = None
self.lb = ttk.Label(self, text=_("Active: ")) self.active_frame = ttk.LabelFrame(
self.lb.config(font=("Ubuntu", 11, "bold")) self.right_side_frame, text=_("Active Tunnel")
self.lb.grid(column=2, row=1, padx=15, pady=4, sticky="w") )
self.active_frame.grid(
column=0, row=0, sticky="nsew", padx=10, pady=5, columnspan=3
)
self.active_frame.columnconfigure(0, weight=1)
self.active_frame.rowconfigure(0, weight=1)
# Interface Label Frame
self.interface_frame = ttk.LabelFrame(
self.right_side_frame, text=_("Interface")
)
self.interface_frame.grid(
column=0, row=1, sticky="nsew", padx=10, pady=5, columnspan=3
)
self.interface_frame.columnconfigure(0, weight=1)
self.interface_frame.rowconfigure(1, weight=1)
# Peer Label Frame
self.peer_frame = ttk.LabelFrame(self.right_side_frame, text=_("Peer"))
self.peer_frame.grid(
column=0, row=2, sticky="nsew", padx=10, pady=5, columnspan=3
)
self.peer_frame.columnconfigure(0, weight=1)
self.peer_frame.rowconfigure(2, weight=1)
# Auto Start Label Frame
self.autoconnect_frame = ttk.Frame(self)
self.autoconnect_frame.grid(column=0, row=3, columnspan=2, sticky="w")
# Rename Frame
self.rename_frame = ttk.Frame(self)
self.rename_frame.grid(column=2, padx=10, row=3, sticky="nsew")
self.rename_frame.columnconfigure(0, weight=2)
self.rename_frame.columnconfigure(1, weight=1)
# Label to Show active Tunnel # Label to Show active Tunnel
self.str_var = tk.StringVar(value=self.a) self.str_var = tk.StringVar(value=self.a)
self.color_label() self.color_label()
# Interface Label # Interface Label
self.interface = ttk.Label(self.lb_frame, text=_("Interface")) self.interface = ttk.Label(self.interface_frame)
self.interface.grid(column=0, row=3, sticky="we", padx=120) self.interface.grid(column=0, row=4, sticky="we")
self.interface.config(font=("Ubuntu", 9)) self.interface.config(font=("Ubuntu", 9))
# Peer Label # Peer Label
self.peer = ttk.Label(self.lb_frame2, text=_("Peer")) self.peer = ttk.Label(self.peer_frame)
self.peer.config(font=("Ubuntu", 9)) self.peer.config(font=("Ubuntu", 9))
self.peer.grid(column=0, row=4, sticky="we", padx=130) self.peer.grid(column=0, row=5, sticky="we")
# Listbox with Scrollbar
self.l_box = tk.Listbox(self.lb_frame_btn_lbox, selectmode="single")
self.l_box.config(relief="ridge", font=("Ubuntu", 12, "bold"))
self.l_box.grid(column=1, rowspan=4, row=0, sticky="ns")
self.l_box.event_add("<<ClickEvent>>", "<Button-1>")
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.grid(column=1, rowspan=4, row=0, sticky="nse")
self.l_box.configure(yscrollcommand=self.scrollbar.set)
# Tunnel List # Tunnel List
self.tl = Tunnel.parse_files_to_dictionary(directory=AppConfig.TEMP_DIR) self.tl = Tunnel.parse_files_to_dictionary(directory=AppConfig.TEMP_DIR)
@ -246,8 +325,8 @@ class FrameWidgets(ttk.Frame):
AppConfig.ensure_directories() AppConfig.ensure_directories()
for tunnels, values in self.tl.items(): for tunnels, values in self.tl.items():
self.l_box.insert("end", tunnels) self.list_box.insert("end", tunnels)
self.l_box.update() self.list_box.update()
# Button Vpn # Button Vpn
if self.a != "": if self.a != "":
@ -266,52 +345,55 @@ class FrameWidgets(ttk.Frame):
# Button Import # Button Import
self.btn_i = ttk.Button( self.btn_i = ttk.Button(
self.lb_frame_btn_lbox, self.control_buttons_frame,
image=self.imp_pic, image=self.image_manager.load_image("icon_import"),
command=self.import_sl, command=self.import_sl,
padding=0, padding=0,
) )
self.btn_i.grid(column=0, row=1, padx=15, pady=8) self.btn_i.grid(column=0, row=1, 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.btn_tr = ttk.Button(
self.lb_frame_btn_lbox, self.control_buttons_frame,
image=self.tr_pic, image=self.image_manager.load_image("icon_trash"),
command=self.delete, command=self.delete,
padding=0, 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, pady=8)
if self.l_box.size() == 0: if self.list_box.size() == 0:
Tooltip(self.btn_tr, Msg.TTIP["trash_tl_info"], self.tooltip_state) Tooltip(self.btn_tr, Msg.TTIP["trash_tl_info"], self.tooltip_state)
else: else:
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.btn_exp = ttk.Button(
self.lb_frame_btn_lbox, self.control_buttons_frame,
image=self.exp_pic, image=self.image_manager.load_image("icon_export"),
command=lambda: Tunnel.export(), command=lambda: Tunnel.export(),
padding=0, padding=0,
) )
self.btn_exp.grid(column=0, row=3, padx=15, pady=8) self.btn_exp.grid(column=0, row=3, pady=8)
self.btn_exp.columnconfigure(0, weight=1)
self.btn_exp.rowconfigure(3, weight=1)
if self.l_box.size() == 0: if self.list_box.size() == 0:
Tooltip(self.btn_exp, Msg.TTIP["export_tl_info"], self.tooltip_state) Tooltip(self.btn_exp, Msg.TTIP["export_tl_info"], self.tooltip_state)
else: else:
Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state) Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state)
# Label Entry # Label Entry
self.lb_rename = ttk.Entry(self.lb_frame4, width=20) self.lb_rename = ttk.Entry(self.rename_frame)
self.lb_rename.grid(column=2, row=0, padx=8, pady=10, sticky="ne") self.lb_rename.grid(column=0, row=0, padx=8, pady=10, sticky="ne")
self.lb_rename.config(width=15)
self.lb_rename.insert(0, _("Max. 12 characters!")) self.lb_rename.insert(0, _("Max. 12 characters!"))
self.lb_rename.config(state="disable") self.lb_rename.config(state="disable")
if self.l_box.size() != 0: if self.list_box.size() != 0:
Tooltip( Tooltip(
self.lb_rename, self.lb_rename,
Msg.TTIP["rename_tl"], Msg.TTIP["rename_tl"],
@ -330,14 +412,13 @@ class FrameWidgets(ttk.Frame):
# Button Rename # Button Rename
self.btn_rename = ttk.Button( self.btn_rename = ttk.Button(
self.lb_frame4, self.rename_frame,
text=_("Rename"), text=_("Rename"),
state="disable", state="disable",
command=self.tl_rename, command=self.tl_rename,
padding=4, width=15,
style="RnButton.TButton",
) )
self.btn_rename.grid(column=3, row=0, padx=5, pady=10, sticky="ne") self.btn_rename.grid(column=1, row=0, pady=10, sticky="nsew")
# Check Buttons # Check Buttons
self.selected_option = tk.IntVar() self.selected_option = tk.IntVar()
@ -346,19 +427,21 @@ class FrameWidgets(ttk.Frame):
# Frame for Labels, Entry and Button # Frame for Labels, Entry and Button
self.autoconnect = ttk.Label( self.autoconnect = ttk.Label(
self.lb_frame3, textvariable=self.autoconnect_var, width=15 self.autoconnect_frame, textvariable=self.autoconnect_var
) )
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, pady=10, sticky="nsew")
self.autoconnect.columnconfigure(1, weight=1)
self.autoconnect.rowconfigure(0, weight=1)
self.wg_autostart = ttk.Checkbutton( self.wg_autostart = ttk.Checkbutton(
self.lb_frame3, self.autoconnect_frame,
text=_("Autoconnect on:"), text=_("Autoconnect on:"),
variable=self.selected_option, variable=self.selected_option,
command=self.box_set, 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=10, padx=(10, 0), sticky="ew")
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1: if self.list_box.size() >= 1 and len(self.list_box.curselection()) >= 1:
Tooltip( Tooltip(
self.wg_autostart, self.wg_autostart,
Msg.TTIP["autostart"], Msg.TTIP["autostart"],
@ -367,7 +450,7 @@ class FrameWidgets(ttk.Frame):
y_offset=-40, y_offset=-40,
) )
if self.l_box.size() == 0: if self.list_box.size() == 0:
Tooltip( Tooltip(
self.wg_autostart, self.wg_autostart,
Msg.TTIP["autostart_info"], Msg.TTIP["autostart_info"],
@ -400,6 +483,15 @@ class FrameWidgets(ttk.Frame):
else: else:
self.updates_lb.grid_remove() self.updates_lb.grid_remove()
def updater(self):
"""Start the lxtools_installer"""
tmp_dir = Path("/tmp/lxtools")
Path.mkdir(tmp_dir, exist_ok=True)
os.chdir(tmp_dir)
result = subprocess.run(["/usr/local/bin/lxtools_installer"], check=False)
if result.returncode != 0:
MessageDialog("error", result.stderr)
# Update the labels based on the result # Update the labels based on the result
def update_ui_for_update(self, res): def update_ui_for_update(self, res):
"""Update UI elements based on an update check result""" """Update UI elements based on an update check result"""
@ -436,25 +528,20 @@ class FrameWidgets(ttk.Frame):
else: else:
self.set_update.set(value=0) self.set_update.set(value=0)
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.Button(
self.update_btn.grid(column=4, columnspan=3, row=0, padx=0) self.menu_frame,
Tooltip( image=self.image_manager.load_image("update"),
self.update_btn, _("Click to download new version"), self.tooltip_state style="Toolbutton",
command=self.updater,
) )
self.update_btn.grid(column=5, row=0, padx=0)
self.download = tk.Menu(self, relief="flat") Tooltip(
self.update_btn.configure(menu=self.download, style="Toolbutton") self.update_btn, _("Click to install new version"), self.tooltip_state
self.download.add_command(
label=_("Download"),
command=lambda: GiteaUpdate.download(
f"{AppConfig.DOWNLOAD_URL}/{res}.zip", res
),
) )
@staticmethod @staticmethod
@ -462,10 +549,6 @@ class FrameWidgets(ttk.Frame):
""" """
a tk.Toplevel window a tk.Toplevel window
""" """
def link_btn() -> None:
webbrowser.open("https://git.ilunix.de/punix/Wire-Py")
msg_t = _( msg_t = _(
"Wire-Py a simple Wireguard Gui for Linux systems.\n\n" "Wire-Py a simple Wireguard Gui for Linux systems.\n\n"
"Wire-Py is open source software written in Python.\n\n" "Wire-Py is open source software written in Python.\n\n"
@ -473,13 +556,17 @@ class FrameWidgets(ttk.Frame):
"Use without warranty!\n" "Use without warranty!\n"
) )
LxTools.msg_window( MessageDialog(
AppConfig.IMAGE_PATHS["icon_vpn"], "info",
AppConfig.IMAGE_PATHS["icon_vpn"],
_("Info"),
msg_t, msg_t,
_("Go to Wire-Py git"), buttons=["OK", _("Go to Wire-Py git")],
link_btn, title=_("Info"),
commands=[
None,
partial(webbrowser.open, "https://git.ilunix.de/punix/Wire-Py"),
],
icon="/usr/share/icons/lx-icons/64/wg_vpn.png",
wraplength=420,
) )
def update_setting(self, update_res) -> None: def update_setting(self, update_res) -> None:
@ -561,7 +648,9 @@ class FrameWidgets(ttk.Frame):
new_theme = "dark" if current_theme == "light" else "light" new_theme = "dark" if current_theme == "light" else "light"
ThemeManager.change_theme(self, new_theme, new_theme) ThemeManager.change_theme(self, new_theme, new_theme)
self.color_label() self.color_label()
self.header_label.config(fg="#ffffff")
self.update_theme_label() # Update the theme label self.update_theme_label() # Update the theme label
# Update Menulfield # Update Menulfield
self.settings.entryconfigure(2, label=self.theme_label.get()) self.settings.entryconfigure(2, label=self.theme_label.get())
@ -570,14 +659,16 @@ class FrameWidgets(ttk.Frame):
Start Button Start Button
""" """
self.btn_stst = ttk.Button( self.btn_stst = ttk.Button(
self.lb_frame_btn_lbox, self.control_buttons_frame,
image=self.wg_vpn_start, image=self.image_manager.load_image("icon_start"),
command=lambda: self.wg_switch("start"), command=lambda: self.wg_switch("start"),
padding=0, padding=0,
) )
self.btn_stst.grid(column=0, row=0, padx=5, pady=8) self.btn_stst.grid(column=0, row=0, pady=8)
self.btn_stst.columnconfigure(0, weight=1)
self.btn_stst.rowconfigure(0, weight=1)
if self.l_box.size() == 0: if self.list_box.size() == 0:
Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state) Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state)
else: else:
Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tooltip_state) Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tooltip_state)
@ -589,28 +680,32 @@ class FrameWidgets(ttk.Frame):
if ConfigManager.get("theme") == "light": if ConfigManager.get("theme") == "light":
self.lb_tunnel = ttk.Label( self.lb_tunnel = ttk.Label(
self, textvariable=self.str_var, foreground="green" self.active_frame, textvariable=self.str_var, foreground="green"
) )
else: else:
self.lb_tunnel = ttk.Label( self.lb_tunnel = ttk.Label(
self, textvariable=self.str_var, foreground="yellow" self.active_frame, 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=0, row=0, padx=10, pady=(0, 10), sticky="n")
self.lb_tunnel.columnconfigure(0, weight=1)
self.lb_tunnel.rowconfigure(0, weight=1)
def stop(self) -> None: def stop(self) -> None:
""" """
Stop Button Stop Button
""" """
self.btn_stst = ttk.Button( self.btn_stst = ttk.Button(
self.lb_frame_btn_lbox, self.control_buttons_frame,
image=self.wg_vpn_stop, image=self.image_manager.load_image("icon_stop"),
command=lambda: self.wg_switch("stop"), command=lambda: self.wg_switch("stop"),
padding=0, padding=0,
) )
self.btn_stst.grid(column=0, row=0, padx=5, pady=8) self.btn_stst.grid(column=0, row=0, pady=8)
self.btn_stst.columnconfigure(0, weight=1)
self.btn_stst.rowconfigure(0, weight=1)
Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state) Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state)
@ -634,22 +729,16 @@ class FrameWidgets(ttk.Frame):
) )
data_import, key_name = Tunnel.parse_files_to_dictionary(filepath=filepath) data_import, key_name = Tunnel.parse_files_to_dictionary(filepath=filepath)
if CryptoUtil.find_key(f"{data_import[key_name]["PrivateKey"]}="): if CryptoUtil.find_key(f"{data_import[key_name]['PrivateKey']}="):
LxTools.msg_window( MessageDialog("error", Msg.STR["tl_exist"], title=Msg.STR["imp_err"])
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["imp_err"],
Msg.STR["tl_exist"],
)
elif not CryptoUtil.is_valid_base64( elif not CryptoUtil.is_valid_base64(
f"{data_import[key_name]["PrivateKey"]}=" f"{data_import[key_name]['PrivateKey']}="
): # 2. Second check: Is it valid Base64? ): # 2. Second check: Is it valid Base64?
LxTools.msg_window( MessageDialog(
AppConfig.IMAGE_PATHS["icon_error"], "error",
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["imp_err"],
Msg.STR["invalid_base64"], Msg.STR["invalid_base64"],
title=Msg.STR["imp_err"],
) )
else: else:
filepath = Path(filepath) filepath = Path(filepath)
@ -704,11 +793,11 @@ class FrameWidgets(ttk.Frame):
AppConfig.ensure_directories() AppConfig.ensure_directories()
self.str_var.set("") self.str_var.set("")
self.a = Tunnel.get_active() self.a = Tunnel.get_active()
self.l_box.insert(0, self.a) self.list_box.insert(0, self.a)
self.wg_autostart.configure(state="normal") self.wg_autostart.configure(state="normal")
self.l_box.selection_clear(0, tk.END) self.list_box.selection_clear(0, tk.END)
self.l_box.update() self.list_box.update()
self.l_box.selection_set(0) self.list_box.selection_set(0)
Tooltip( Tooltip(
self.wg_autostart, self.wg_autostart,
@ -742,12 +831,7 @@ class FrameWidgets(ttk.Frame):
print(f">> {import_file.stem} << autostart is disabled by default") print(f">> {import_file.stem} << autostart is disabled by default")
except UnboundLocalError: except UnboundLocalError:
LxTools.msg_window( MessageDialog("error", Msg.STR["no_valid_file"], title=Msg.STR["imp_err"])
AppConfig.IMAGE_PATHS["icon_error"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["imp_err"],
Msg.STR["no_valid_file"],
)
except (IsADirectoryError, TypeError, FileNotFoundError): except (IsADirectoryError, TypeError, FileNotFoundError):
print("File import: abort by user...") print("File import: abort by user...")
except EOFError as e: except EOFError as e:
@ -758,8 +842,8 @@ class FrameWidgets(ttk.Frame):
delete Wireguard Tunnel delete Wireguard Tunnel
""" """
try: try:
self.select_tunnel = self.l_box.curselection() self.select_tunnel = self.list_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.list_box.get(self.select_tunnel[0])
process: CompletedProcess[str] = run( process: CompletedProcess[str] = run(
["nmcli", "connection", "delete", select_tl], ["nmcli", "connection", "delete", select_tl],
@ -773,7 +857,7 @@ class FrameWidgets(ttk.Frame):
f"{process.stderr} Code: {process.returncode}", exc_info=True f"{process.stderr} Code: {process.returncode}", exc_info=True
) )
self.l_box.delete(self.select_tunnel[0]) self.list_box.delete(self.select_tunnel[0])
Path.unlink(f"{AppConfig.CONFIG_DIR}/{select_tl}.dat") Path.unlink(f"{AppConfig.CONFIG_DIR}/{select_tl}.dat")
if select_tl == ConfigManager.get("autostart"): if select_tl == ConfigManager.get("autostart"):
@ -784,7 +868,7 @@ class FrameWidgets(ttk.Frame):
self.wg_autostart.configure(state="disabled") self.wg_autostart.configure(state="disabled")
# for disabling checkbox when Listbox empty # for disabling checkbox when Listbox empty
if self.l_box.size() == 0: if self.list_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( Tooltip(
@ -803,35 +887,25 @@ class FrameWidgets(ttk.Frame):
if self.a != "" and self.a == select_tl: if self.a != "" and self.a == select_tl:
self.str_var.set(value="") self.str_var.set(value="")
self.start() self.start()
self.l_box.update() self.list_box.update()
self.reset_fields() self.reset_fields()
except IndexError: except IndexError:
if self.l_box.size() != 0: if self.list_box.size() != 0:
LxTools.msg_window( MessageDialog("info", Msg.STR["sel_list"], title=Msg.STR["sel_tl"])
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["sel_list"],
)
else: else:
LxTools.msg_window( MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
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:
""" """
checkbox for enable autostart Tunnel checkbox for enable autostart Tunnel
""" """
AppConfig.get_autostart_content() AppConfig.get_autostart_content()
if self.l_box.size() != 0: if self.list_box.size() != 0:
self.wg_autostart.configure(state="normal") self.wg_autostart.configure(state="normal")
self.lb_rename.config(state="normal") self.lb_rename.config(state="normal")
self.lb_rename.delete(0, tk.END) self.lb_rename.delete(0, tk.END)
@ -858,13 +932,14 @@ class FrameWidgets(ttk.Frame):
self.autoconnect_var.set(self.auto_con) self.autoconnect_var.set(self.auto_con)
self.autoconnect = ttk.Label( self.autoconnect = ttk.Label(
self.lb_frame3, self.autoconnect_frame,
textvariable=self.autoconnect_var, textvariable=self.autoconnect_var,
foreground="#0071ff", foreground="#0071ff",
width=15, width=18,
) )
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="ew", pady=19)
self.autoconnect.rowconfigure(0, weight=1)
def box_set(self) -> None: def box_set(self) -> None:
""" """
@ -878,13 +953,13 @@ class FrameWidgets(ttk.Frame):
to disable the autostart. to disable the autostart.
""" """
try: try:
select_tunnel = self.l_box.curselection() select_tunnel = self.list_box.curselection()
select_tl = self.l_box.get(select_tunnel[0]) select_tl = self.list_box.get(select_tunnel[0])
if self.selected_option.get() == 0: if self.selected_option.get() == 0:
ConfigManager.set("autostart", "off") ConfigManager.set("autostart", "off")
if self.l_box.size() == 0: if self.list_box.size() == 0:
self.wg_autostart.configure(state="disabled") self.wg_autostart.configure(state="disabled")
if self.selected_option.get() >= 1: if self.selected_option.get() >= 1:
@ -907,47 +982,27 @@ class FrameWidgets(ttk.Frame):
if len(self.lb_rename.get()) > 12: if len(self.lb_rename.get()) > 12:
LxTools.msg_window( MessageDialog("info", Msg.STR["sign_len"], title=Msg.STR["ren_err"])
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( MessageDialog("info", Msg.STR["zero_signs"], title=Msg.STR["ren_err"])
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( MessageDialog("info", Msg.STR["false_signs"], title=Msg.STR["ren_err"])
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["false_signs"],
)
elif self.lb_rename.get() in [ elif self.lb_rename.get() in [
file.stem for file in AppConfig.CONFIG_DIR.glob("*.dat") file.stem for file in AppConfig.CONFIG_DIR.glob("*.dat")
]: ]:
LxTools.msg_window( MessageDialog("info", Msg.STR["is_in_use"], title=Msg.STR["ren_err"])
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["is_in_use"],
)
else: else:
try: try:
self.select_tunnel = self.l_box.curselection() self.select_tunnel = self.list_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.list_box.get(self.select_tunnel[0])
# nmcli connection modify old connection.id iphone # nmcli connection modify old connection.id iphone
process: CompletedProcess[str] = run( process: CompletedProcess[str] = run(
@ -975,20 +1030,15 @@ class FrameWidgets(ttk.Frame):
if select_tl == ConfigManager.get("autostart"): if select_tl == ConfigManager.get("autostart"):
ConfigManager.set("autostart", self.lb_rename.get()) ConfigManager.set("autostart", self.lb_rename.get())
self.autoconnect_var.set(value=self.lb_rename.get()) self.autoconnect_var.set(value=self.lb_rename.get())
self.l_box.delete(self.select_tunnel[0]) self.list_box.delete(self.select_tunnel[0])
self.l_box.insert("end", self.lb_rename.get()) self.list_box.insert("end", self.lb_rename.get())
self.l_box.update() self.list_box.update()
self.lb_rename.delete(0, tk.END) self.lb_rename.delete(0, tk.END)
self.update_connection_display() self.update_connection_display()
except IndexError: except IndexError:
LxTools.msg_window( MessageDialog("info", Msg.STR["sel_list"], title=Msg.STR["ren_err"])
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["ren_err"],
Msg.STR["sel_list"],
)
except EOFError as e: except EOFError as e:
logging.error(e, exc_info=True) logging.error(e, exc_info=True)
@ -1013,34 +1063,36 @@ class FrameWidgets(ttk.Frame):
in the UI using ttk.Label widgets. in the UI using ttk.Label widgets.
Creates three labels for address, DNS, and endpoint with Creates three labels for address, DNS, and endpoint with
specific styling (color, font), positioning them in a specific styling (color, font), positioning them in a
grid layout (`lb_frame` and `lb_frame2`). grid layout (`lb_frame` and `peer_frame`).
Each label is linked to a corresponding text variable Each label is linked to a corresponding text variable
(`self.add`, `self.DNS`, `self.enp`) for dynamic data updates. (`self.add`, `self.DNS`, `self.enp`) for dynamic data updates.
""" """
# Address Label # Address Label
self.address = ttk.Label( self.address = ttk.Label(
self.lb_frame, textvariable=self.add, foreground="#0071ff" self.interface_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=(0, 20))
self.address.config(font=("Ubuntu", 9)) self.address.config(font=("Ubuntu", 9))
# DNS Label # DNS Label
self.dns = ttk.Label(self.lb_frame, textvariable=self.DNS, foreground="#0071ff") self.dns = ttk.Label(
self.dns.grid(column=0, row=7, sticky="w", padx=10, pady=6) self.interface_frame, textvariable=self.DNS, foreground="#0071ff"
)
self.dns.grid(column=0, row=7, sticky="w", padx=10, pady=(0, 20))
self.dns.config(font=("Ubuntu", 9)) self.dns.config(font=("Ubuntu", 9))
# Endpoint Label # Endpoint Label
self.endpoint = ttk.Label( self.endpoint = ttk.Label(
self.lb_frame2, textvariable=self.enp, foreground="#0071ff" self.peer_frame, 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=(0, 30))
self.endpoint.config(font=("Ubuntu", 9)) self.endpoint.config(font=("Ubuntu", 9))
def wg_switch(self, event=None) -> None: def wg_switch(self, event=None) -> None:
""" """
Manages switching between active and inactiveVPN connections. Manages switching between active and inactiveVPN connections.
If no tunnel is selected (`self.a == ""`), it starts a new connection If no tunnel is selected (`self.a == ""`), it starts a new connection
with the selected tunnel from the listbox (`l_box`). with the selected tunnel from the listbox (`list_box`).
Otherwise, it stops the current connection and updates Otherwise, it stops the current connection and updates
tunnel data using `handle_tunnel_data`. tunnel data using `handle_tunnel_data`.
Handles errors like `IndexError` by displaying appropriate Handles errors like `IndexError` by displaying appropriate
@ -1048,8 +1100,8 @@ class FrameWidgets(ttk.Frame):
""" """
try: try:
if self.a == "": if self.a == "":
self.select_tunnel = self.l_box.curselection() self.select_tunnel = self.list_box.curselection()
select_tl = self.l_box.get(self.select_tunnel[0]) select_tl = self.list_box.get(self.select_tunnel[0])
self.handle_connection_state("start", select_tl) self.handle_connection_state("start", select_tl)
else: else:
@ -1059,23 +1111,13 @@ class FrameWidgets(ttk.Frame):
except IndexError: except IndexError:
if self.l_box.size() != 0: if self.list_box.size() != 0:
LxTools.msg_window( MessageDialog("info", Msg.STR["sel_list"], title=Msg.STR["sel_tl"])
AppConfig.IMAGE_PATHS["icon_info"],
AppConfig.IMAGE_PATHS["icon_msg"],
Msg.STR["sel_tl"],
Msg.STR["sel_list"],
)
else: else:
LxTools.msg_window( MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
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

@ -3,6 +3,8 @@
import logging import logging
import gettext import gettext
import locale import locale
import tkinter as tk
import os
from pathlib import Path from pathlib import Path
from subprocess import CompletedProcess, run from subprocess import CompletedProcess, run
from typing import Dict, Any from typing import Dict, Any
@ -57,18 +59,18 @@ class AppConfig:
# 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.06.0725" VERSION: str = "v. 2.07.0225"
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": "",
"window_title2": "LogViewer", "window_title2": "LogViewer",
"window_size": (600, 383), "window_size": (590, 450),
"font_family": "Ubuntu", "font_family": "Ubuntu",
"font_size": 11, "font_size": 11,
"resizable_window": (False, False), "resizable_window": (True, True),
} }
# System-dependent paths # System-dependent paths
@ -79,20 +81,6 @@ class AppConfig:
"pkey_path": "/usr/local/etc/ssl/pwgk.pem", "pkey_path": "/usr/local/etc/ssl/pwgk.pem",
} }
# Images and icons paths
IMAGE_PATHS: Dict[str, Path] = {
"icon_vpn": "/usr/share/icons/lx-icons/48/wg_vpn.png",
"icon_msg": "/usr/share/icons/lx-icons/48/wg_msg.png",
"icon_import": "/usr/share/icons/lx-icons/48/wg_import.png",
"icon_export": "/usr/share/icons/lx-icons/48/wg_export.png",
"icon_trash": "/usr/share/icons/lx-icons/48/wg_trash.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_info": "/usr/share/icons/lx-icons/64/info.png",
"icon_error": "/usr/share/icons/lx-icons/64/error.png",
"icon_log": "/usr/share/icons/lx-icons/48/log.png",
}
@staticmethod @staticmethod
def setup_translations() -> gettext.gettext: def setup_translations() -> gettext.gettext:
""" """
@ -170,6 +158,65 @@ class AppConfig:
_ = AppConfig.setup_translations() _ = AppConfig.setup_translations()
class Image:
def __init__(self):
self.images = {}
def load_image(self, image_key, fallback_paths=None) -> None | tk.PhotoImage:
"""Load PNG image using tk.PhotoImage with fallback options"""
if image_key in self.images:
return self.images[image_key]
# Define image paths based on key
image_paths = {
"icon_header": [
"/usr/share/icons/lx-icons/32/wg_vpn.png",
],
"icon_vpn": [
"/usr/share/icons/lx-icons/48/wg_vpn.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_import": [
"/usr/share/icons/lx-icons/48/wg_import.png",
],
"icon_export": [
"/usr/share/icons/lx-icons/48/wg_export.png",
],
"icon_trash": [
"/usr/share/icons/lx-icons/48/wg_trash.png",
],
"update": [
"/usr/share/icons/lx-icons/16/settings.png",
],
}
# Get paths to try
paths_to_try = image_paths.get(image_key, [])
# Add fallback paths if provided
if fallback_paths:
paths_to_try.extend(fallback_paths)
# Try to load image from paths
for path in paths_to_try:
try:
if os.path.exists(path):
photo = tk.PhotoImage(file=path)
self.images[image_key] = photo
return photo
except tk.TclError as e:
print(f"{LocaleStrings.MSGP['fail_load_image']}{path}: {e}")
continue
# Return None if no image found
return None
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.
@ -193,6 +240,8 @@ class Msg:
STR: Dict[str, str] = { STR: Dict[str, str] = {
# Strings for messages # Strings for messages
"header_left_bottom": _("Simple GUI for Wireguard"),
"header_right_top": _("Wireguard VPN Manager"),
"sel_tl": _("Select tunnel"), "sel_tl": _("Select tunnel"),
"ren_err": _("Renaming not possible"), "ren_err": _("Renaming not possible"),
"exp_succ": _("Export successful"), "exp_succ": _("Export successful"),