1 Commits

Author SHA1 Message Date
6051ad1546 Version number update 2025-06-04 18:54:45 +02:00
39 changed files with 326 additions and 437 deletions

View File

@ -6,54 +6,12 @@ My standard System: Linux Mint 22 Cinnamon
- os import in common_tools.py replaced by other methods
- If Wire-Py already runs, prevent further start
- for loops with lists replaced by List Comprehensions
- 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
07-06-2025
- ssl_decrypt now directly checks whether encrypted files are located in the specified directory.
- This method has been removed from common_tools. The path has been adjusted ssl_decrypt likely
had an incorrect path. No files were decrypted. This has been fixed.
- Key generation has been removed from ssl_decrypt, as it is only needed in ssl_encrypt.
- Logviewer now in the settings menu. Moduls now as libs in share_libs.
- The Lx Tools installer is there to ensure that everything required for the selected app is installed.
04-06-2025
- large update in the module were outsourced and can now be used as
dependencies to wirepy us other apps from git.ilunix.de
### Added
13-04-20255
@ -93,14 +51,12 @@ My standard System: Linux Mint 22 Cinnamon
- Tunnels are now read from the directory to view them in the list.
To display only own tunnels, and read errors are minimized.
### Added
10-11-2024
- Fix Checkbutton Autostart when first install Wire-Py
- Update Translate Files
### Added
10-11-2024

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -5,34 +5,55 @@ from pathlib import Path
import pwd
import shutil
from subprocess import CompletedProcess, run
from shared_libs.wp_app_config import AppConfig
from shared_libs.message import MessageDialog
from shared_libs.wp_app_config import AppConfig, logging
parser = argparse.ArgumentParser()
parser.add_argument("--user", required=True, help="Username of the target file system")
args = parser.parse_args()
_ = AppConfig.setup_translations()
try:
# Retrieve UID and GID
user_info = pwd.getpwnam(args.user)
uid = user_info.pw_uid # User ID (e.g., 1000)
gid = user_info.pw_gid # Group ID (e.g., 1000)
except KeyError:
MessageDialog(
"error", _(f"User '{args.user}' not found."), title="Error decrypt"
).show()
logging.error(f"User '{args.user}' not found.", exc_info=True)
exit(1)
crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py")
keyfile: Path = Path(f"/home/{args.user}/.config/wire_py/pbwgk.pem")
path_of_crypted_tunnel: Path = Path(f"/home/{args.user}/.config/wire_py")
if len([str(file) for file in crypted_tunnel.glob("*.dat")]) == 0:
pass
else:
crypted_tunnel = [str(file) for file in crypted_tunnel.glob("*.dat")]
if not keyfile.is_file():
process: CompletedProcess[str] = run(
[
"openssl",
"rsa",
"-in",
AppConfig.SYSTEM_PATHS["pkey_path"],
"-out",
keyfile,
"-outform",
"PEM",
"-pubout",
],
capture_output=True,
text=True,
check=False,
)
for tunnel_path in crypted_tunnel:
if process.returncode == 0:
logging.info("Public key generated successfully.", exc_info=True)
else:
logging.error(
f"Error with the following code... {process.returncode}", exc_info=True
)
shutil.chown(keyfile, uid, gid)
if AppConfig.PUBLICKEY.exists():
crypted__tunnel = [str(file) for file in path_of_crypted_tunnel.glob("*.dat")]
for tunnel_path in crypted__tunnel:
base_name = Path(tunnel_path).stem
@ -50,16 +71,14 @@ else:
],
capture_output=True,
text=True,
check=True,
check=False,
)
shutil.chown(f"{AppConfig.TEMP_DIR}/{base_name}.conf", uid, gid)
logging.info(f"Processing of the file: {tunnel_path}", exc_info=True)
# Output from Openssl Error
if process.stderr:
MessageDialog(
"error",
_(
f"{process.stderr} Error by [{tunnel_path}] Code: {process.returncode}"
),
title="Error decrypt",
).show()
logging.error(
f"{process.stderr} Error by [{tunnel_path}] Code: {process.returncode}",
exc_info=True,
)

View File

@ -2,19 +2,16 @@
""" This Script encrypt Wireguardfiles for Wirepy users for more Security """
import argparse
import logging
from pathlib import Path
import pwd
import shutil
from subprocess import CompletedProcess, run
from shared_libs.wp_app_config import AppConfig
from shared_libs.common_tools import LogConfig
from shared_libs.wp_app_config import AppConfig, logging
parser = argparse.ArgumentParser()
parser.add_argument("--user", required=True, help="Username of the target file system")
args = parser.parse_args()
LogConfig.logger(f"/home/{args.user}/.local/share/lxlogs/wirepy.log")
try:
# Retrieve UID and GID
user_info = pwd.getpwnam(args.user)

View File

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

528
wirepy.py
View File

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

View File

@ -3,8 +3,6 @@
import logging
import gettext
import locale
import tkinter as tk
import os
from pathlib import Path
from subprocess import CompletedProcess, run
from typing import Dict, Any
@ -59,18 +57,18 @@ class AppConfig:
# Updates
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
VERSION: str = "v. 2.07.0225"
VERSION: str = "v. 2.06.0425"
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"
# UI configuration
UI_CONFIG: Dict[str, Any] = {
"window_title": "",
"window_title": "Wire-Py",
"window_title2": "LogViewer",
"window_size": (590, 450),
"window_size": (600, 383),
"font_family": "Ubuntu",
"font_size": 11,
"resizable_window": (True, True),
"resizable_window": (False, False),
}
# System-dependent paths
@ -81,6 +79,20 @@ class AppConfig:
"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
def setup_translations() -> gettext.gettext:
"""
@ -141,8 +153,6 @@ class AppConfig:
text=True,
check=False,
)
if process.returncode == 0:
logging.info(process.stdout, exc_info=True)
if process.stderr:
logging.error(f"{process.stderr} Code: {process.returncode}", exc_info=True)
@ -158,65 +168,6 @@ class AppConfig:
_ = 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:
"""
A utility class that provides centralized access to translated message strings.
@ -240,8 +191,6 @@ class Msg:
STR: Dict[str, str] = {
# Strings for messages
"header_left_bottom": _("Simple GUI for Wireguard"),
"header_right_top": _("Wireguard VPN Manager"),
"sel_tl": _("Select tunnel"),
"ren_err": _("Renaming not possible"),
"exp_succ": _("Export successful"),