ui fixes expand widget on x and y works
12
logger.py
Normal file
@@ -0,0 +1,12 @@
|
||||
class Logger:
|
||||
def __init__(self):
|
||||
self._log_func = print # Default to print if not initialized
|
||||
|
||||
def init_logger(self, log_func):
|
||||
self._log_func = log_func
|
||||
|
||||
def log(self, message):
|
||||
self._log_func(message)
|
||||
|
||||
# Global instance
|
||||
app_logger = Logger()
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 906 B After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 16 KiB |
@@ -2,27 +2,27 @@
|
||||
""" 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
|
||||
|
||||
|
||||
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)
|
||||
uid = user_info.pw_uid # User ID (e.g., 1000)
|
||||
gid = user_info.pw_gid # Group ID (e.g., 1000)
|
||||
except KeyError:
|
||||
logging.error(f"User '{args.user}' not found.")
|
||||
|
||||
exit(1)
|
||||
|
||||
keyfile: Path = Path(f"/home/{args.user}/.config/wire_py/pbwgk.pem")
|
||||
@@ -50,13 +50,7 @@ if not keyfile.is_file():
|
||||
|
||||
# Output from Openssl Error
|
||||
if process.stderr:
|
||||
if "writing RSA key" in process.stderr:
|
||||
logging.info(f"{process.stderr}")
|
||||
else:
|
||||
logging.error(f"{process.stderr}")
|
||||
|
||||
if process.returncode == 0:
|
||||
logging.info("Public key generated successfully.")
|
||||
pass
|
||||
|
||||
shutil.chown(keyfile, uid, gid)
|
||||
|
||||
@@ -86,4 +80,4 @@ if AppConfig.TEMP_DIR.exists() and any(AppConfig.TEMP_DIR.iterdir()):
|
||||
|
||||
# Output from Openssl Error
|
||||
if process.stderr:
|
||||
logging.error(process.stderr)
|
||||
pass
|
@@ -2,13 +2,13 @@
|
||||
"""
|
||||
This script belongs to wirepy and is for the auto start of the tunnel
|
||||
"""
|
||||
import logging
|
||||
|
||||
from subprocess import CompletedProcess, run
|
||||
from shared_libs.wp_app_config import AppConfig
|
||||
from shared_libs.common_tools import ConfigManager, LogConfig
|
||||
from shared_libs.common_tools import ConfigManager
|
||||
|
||||
ConfigManager.init(AppConfig.SETTINGS_FILE)
|
||||
LogConfig.logger(ConfigManager.get("logfile"))
|
||||
|
||||
if ConfigManager.get("autostart") != "off":
|
||||
process: CompletedProcess[str] = run(
|
||||
["nmcli", "connection", "up", ConfigManager.get("autostart")],
|
||||
@@ -18,7 +18,7 @@ if ConfigManager.get("autostart") != "off":
|
||||
)
|
||||
# Output from start_wg error
|
||||
if process.stderr:
|
||||
logging.error(process.stderr)
|
||||
|
||||
|
||||
else:
|
||||
pass
|
||||
|
21
tunnel.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3
|
||||
import logging
|
||||
from logger import app_logger
|
||||
import getpass
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
@@ -25,6 +25,7 @@ class Tunnel:
|
||||
|
||||
if filepath is not None:
|
||||
filepath = Path(filepath)
|
||||
truncated_stem = None
|
||||
try:
|
||||
content = filepath.read_text()
|
||||
|
||||
@@ -70,17 +71,17 @@ class Tunnel:
|
||||
content = secrets.token_bytes(len(content))
|
||||
|
||||
except StopIteration:
|
||||
pass
|
||||
return None, None
|
||||
|
||||
elif directory is not None:
|
||||
|
||||
if not directory.exists() or not directory.is_dir():
|
||||
logging.error(
|
||||
app_logger.log(
|
||||
"Temp directory does not exist or is not a directory.")
|
||||
return None
|
||||
|
||||
# Get a list of all files in the directory
|
||||
files = [file for file in AppConfig.TEMP_DIR.iterdir()
|
||||
files = [file for file in directory.iterdir()
|
||||
if file.is_file()]
|
||||
|
||||
# Search for the string in the files
|
||||
@@ -146,12 +147,12 @@ class Tunnel:
|
||||
)
|
||||
|
||||
if process.stderr and "error" in process.stderr.lower():
|
||||
logging.error(f"Error output on nmcli: {process.stderr}")
|
||||
app_logger.log(f"Error output on nmcli: {process.stderr}")
|
||||
|
||||
except StopIteration:
|
||||
active = None
|
||||
except Exception as e:
|
||||
logging.error(f"Error on nmcli: {e}")
|
||||
app_logger.log(f"Error on nmcli: {e}")
|
||||
active = None
|
||||
|
||||
return active if active is not None else ""
|
||||
@@ -189,7 +190,7 @@ class Tunnel:
|
||||
)
|
||||
|
||||
else:
|
||||
logging.error(
|
||||
app_logger.log(
|
||||
"There was a mistake at creating the Zip file. File is empty."
|
||||
)
|
||||
MessageDialog(
|
||||
@@ -199,18 +200,18 @@ class Tunnel:
|
||||
return False
|
||||
return True
|
||||
except PermissionError:
|
||||
logging.error(
|
||||
app_logger.log(
|
||||
f"Permission denied when creating archive in {wg_tar}"
|
||||
)
|
||||
return False
|
||||
|
||||
except zipfile.BadZipFile as e:
|
||||
logging.error(f"Invalid ZIP file: {e}")
|
||||
app_logger.log(f"Invalid ZIP file: {e}")
|
||||
return False
|
||||
except TypeError:
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.error(f"Export failed: {str(e)}")
|
||||
app_logger.log(f"Export failed: {str(e)}")
|
||||
MessageDialog(
|
||||
"error", Msg.STR["exp_try"], title=Msg.STR["exp_err"])
|
||||
return False
|
||||
|
0
ui/__init__.py
Normal file
87
ui/controls.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
from shared_libs.common_tools import Tooltip
|
||||
from shared_libs.wp_app_config import Msg
|
||||
from tunnel import Tunnel
|
||||
|
||||
|
||||
class Controls(ttk.Frame):
|
||||
def __init__(self, container, image_manager, tooltip_state, import_callback, delete_callback, start_stop_callback, **kwargs):
|
||||
super().__init__(container, **kwargs)
|
||||
self.image_manager = image_manager
|
||||
self.tooltip_state = tooltip_state
|
||||
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
self.rowconfigure(1, weight=1)
|
||||
self.rowconfigure(2, weight=1)
|
||||
self.rowconfigure(3, weight=1)
|
||||
|
||||
self.btn_stst = ttk.Button(
|
||||
self,
|
||||
image=self.image_manager.get_icon("vpn_start_large"),
|
||||
command=start_stop_callback,
|
||||
padding=0,
|
||||
)
|
||||
self.btn_stst.grid(column=0, row=0, sticky="ew")
|
||||
Tooltip(self.btn_stst, Msg.TTIP["start_tl"],
|
||||
state_var=self.tooltip_state)
|
||||
|
||||
self.btn_i = ttk.Button(
|
||||
self,
|
||||
image=self.image_manager.get_icon("import_large"),
|
||||
command=import_callback,
|
||||
padding=0,
|
||||
)
|
||||
self.btn_i.grid(column=0, row=1, sticky="ew")
|
||||
Tooltip(self.btn_i, Msg.TTIP["import_tl"],
|
||||
state_var=self.tooltip_state)
|
||||
|
||||
self.btn_tr = ttk.Button(
|
||||
self,
|
||||
image=self.image_manager.get_icon("trash_large"),
|
||||
command=delete_callback,
|
||||
padding=0,
|
||||
)
|
||||
self.btn_tr.grid(column=0, row=2, sticky="ew")
|
||||
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"],
|
||||
state_var=self.tooltip_state)
|
||||
|
||||
self.btn_exp = ttk.Button(
|
||||
self,
|
||||
image=self.image_manager.get_icon("export_large"),
|
||||
command=lambda: Tunnel.export(),
|
||||
padding=0,
|
||||
)
|
||||
self.btn_exp.grid(column=0, row=3, sticky="ew")
|
||||
Tooltip(self.btn_exp, Msg.TTIP["export_tl"],
|
||||
state_var=self.tooltip_state)
|
||||
|
||||
def set_start_stop_button_state(self, is_active, list_box_size):
|
||||
if is_active:
|
||||
self.btn_stst.config(
|
||||
image=self.image_manager.get_icon("vpn_stop_large"))
|
||||
Tooltip(self.btn_stst,
|
||||
Msg.TTIP["stop_tl"], state_var=self.tooltip_state)
|
||||
else:
|
||||
self.btn_stst.config(
|
||||
image=self.image_manager.get_icon("vpn_start_large"))
|
||||
if list_box_size == 0:
|
||||
Tooltip(self.btn_stst,
|
||||
Msg.TTIP["empty_list"], state_var=self.tooltip_state)
|
||||
else:
|
||||
Tooltip(self.btn_stst,
|
||||
Msg.TTIP["start_tl"], state_var=self.tooltip_state)
|
||||
|
||||
def update_tooltips(self, list_box_size):
|
||||
if list_box_size == 0:
|
||||
Tooltip(self.btn_tr,
|
||||
Msg.TTIP["trash_tl_info"], state_var=self.tooltip_state)
|
||||
Tooltip(self.btn_exp,
|
||||
Msg.TTIP["export_tl_info"], state_var=self.tooltip_state)
|
||||
else:
|
||||
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"],
|
||||
state_var=self.tooltip_state)
|
||||
Tooltip(self.btn_exp,
|
||||
Msg.TTIP["export_tl"], state_var=self.tooltip_state)
|
58
ui/header.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import tkinter as tk
|
||||
from shared_libs.wp_app_config import AppConfig, Msg
|
||||
|
||||
|
||||
class Header(tk.Frame):
|
||||
def __init__(self, container, image_manager, **kwargs):
|
||||
super().__init__(container, bg="#2c3e50", **kwargs)
|
||||
|
||||
self.image_manager = image_manager
|
||||
|
||||
wg_icon_header_frame = tk.Frame(self, bg="#2c3e50")
|
||||
wg_icon_header_frame.grid(column=0, row=0, rowspan=2, sticky="w")
|
||||
|
||||
wg_icon_header_label = tk.Label(
|
||||
wg_icon_header_frame,
|
||||
image=self.image_manager.get_icon("vpn_small"),
|
||||
bg="#2c3e50",
|
||||
)
|
||||
wg_icon_header_label.grid(column=0, row=0, sticky="e", ipadx=10)
|
||||
|
||||
self.header_label = tk.Label(
|
||||
self,
|
||||
text=Msg.STR["lx_tools"],
|
||||
font=("Helvetica", 12, "bold"),
|
||||
fg="#ffffff",
|
||||
bg="#2c3e50",
|
||||
)
|
||||
self.header_label.grid(
|
||||
column=1,
|
||||
row=0,
|
||||
sticky="w",
|
||||
padx=(5, 20),
|
||||
pady=(15, 5),
|
||||
ipady=4,
|
||||
)
|
||||
|
||||
self.version_label = tk.Label(
|
||||
self,
|
||||
text=f"{AppConfig.VERSION} • {Msg.STR['header_left_bottom']}",
|
||||
font=("Helvetica", 9),
|
||||
fg="#bdc3c7",
|
||||
bg="#2c3e50",
|
||||
)
|
||||
self.version_label.grid(
|
||||
column=1, row=1, sticky="w", padx=(5, 20), pady=(0, 10))
|
||||
|
||||
info_label = tk.Label(
|
||||
self,
|
||||
text=Msg.STR["header_right_top"],
|
||||
font=("Helvetica", 10),
|
||||
fg="#ecf0f1",
|
||||
bg="#2c3e50",
|
||||
)
|
||||
info_label.grid(column=2, row=0, sticky="ne",
|
||||
padx=(10, 10), pady=(10, 0))
|
||||
|
||||
self.columnconfigure(1, weight=1, pad=2)
|
||||
self.rowconfigure(0, weight=1)
|
47
ui/log_window.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from datetime import datetime
|
||||
|
||||
class LogWindow(tk.Toplevel):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.title("Log")
|
||||
self.geometry("600x400")
|
||||
self.protocol("WM_DELETE_WINDOW", self.withdraw) # Hide on close
|
||||
|
||||
log_container = tk.Frame(self)
|
||||
log_container.pack(fill="both", expand=True, padx=10, pady=10)
|
||||
|
||||
self.log_text = tk.Text(
|
||||
log_container,
|
||||
wrap=tk.WORD,
|
||||
font=("Consolas", 9),
|
||||
bg="#1e1e1e",
|
||||
fg="#d4d4d4",
|
||||
insertbackground="white",
|
||||
selectbackground="#264f78",
|
||||
)
|
||||
|
||||
log_scrollbar = ttk.Scrollbar(
|
||||
log_container, orient="vertical", command=self.log_text.yview
|
||||
)
|
||||
self.log_text.configure(yscrollcommand=log_scrollbar.set)
|
||||
|
||||
self.log_text.pack(side="left", fill="both", expand=True)
|
||||
log_scrollbar.pack(side="right", fill="y")
|
||||
|
||||
self.withdraw() # Start hidden
|
||||
|
||||
def log_message(self, message):
|
||||
"""Add message to log"""
|
||||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||||
log_entry = f"[{timestamp}] {message}\n"
|
||||
self.log_text.insert(tk.END, log_entry)
|
||||
self.log_text.see(tk.END)
|
||||
self.log_text.update()
|
||||
|
||||
def toggle(self):
|
||||
if self.winfo_viewable():
|
||||
self.withdraw()
|
||||
else:
|
||||
self.deiconify()
|
272
ui/main_frame.py
Normal file
@@ -0,0 +1,272 @@
|
||||
|
||||
import getpass
|
||||
import shutil
|
||||
import tkinter as tk
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from subprocess import CompletedProcess, run
|
||||
from tkinter import ttk
|
||||
from shared_libs.custom_file_dialog import CustomFileDialog
|
||||
|
||||
from shared_libs.common_tools import (
|
||||
LxTools,
|
||||
CryptoUtil,
|
||||
ConfigManager,
|
||||
ThemeManager,
|
||||
)
|
||||
from shared_libs.message import MessageDialog
|
||||
from shared_libs.wp_app_config import AppConfig, Msg
|
||||
from tunnel import Tunnel
|
||||
from .header import Header
|
||||
from .menu_bar import MenuBar
|
||||
from .controls import Controls
|
||||
from .tunnel_list import TunnelList
|
||||
from .tunnel_details import TunnelDetails
|
||||
from .status_panel import StatusPanel
|
||||
|
||||
|
||||
from logger import app_logger
|
||||
|
||||
|
||||
class MainFrame(ttk.Frame):
|
||||
def __init__(self, container, image_manager, toggle_log_window, **kwargs):
|
||||
super().__init__(container, **kwargs)
|
||||
self.image_manager = image_manager
|
||||
|
||||
self.columnconfigure(1, weight=1)
|
||||
self.columnconfigure(2, weight=18)
|
||||
self.rowconfigure(2, weight=1)
|
||||
|
||||
self.tooltip_state = tk.BooleanVar()
|
||||
state = ConfigManager.get("tooltips")
|
||||
self.tooltip_state.set(str(state) == "True")
|
||||
|
||||
self.active_tunnel_name = Tunnel.get_active()
|
||||
self.tunnels = Tunnel.parse_files_to_dictionary(
|
||||
directory=AppConfig.TEMP_DIR)
|
||||
if self.tunnels is None:
|
||||
self.tunnels = {}
|
||||
LxTools.clean_files(AppConfig.TEMP_DIR, file=None)
|
||||
AppConfig.ensure_directories()
|
||||
|
||||
# Create components
|
||||
self.header = Header(self, self.image_manager)
|
||||
self.menu_bar = MenuBar(self, self.image_manager,
|
||||
self.tooltip_state, self.on_theme_toggle,
|
||||
toggle_log_window)
|
||||
self.controls = Controls(
|
||||
self, self.image_manager, self.tooltip_state, self.import_sl, self.delete, self.wg_switch)
|
||||
self.tunnel_list = TunnelList(self, self.on_tunnel_select)
|
||||
self.tunnel_details = TunnelDetails(self)
|
||||
self.status_panel = StatusPanel(
|
||||
self, self.tooltip_state, self.tl_rename, self.box_set)
|
||||
|
||||
# Layout components
|
||||
self.header.grid(column=0, columnspan=3, row=0, sticky="nsew")
|
||||
self.menu_bar.grid(column=0, columnspan=3, row=1, sticky="we")
|
||||
self.controls.grid(column=0, row=2, sticky="nsew", padx=(15, 0))
|
||||
self.tunnel_list.grid(column=1, row=2, sticky="nsew")
|
||||
self.tunnel_details.grid(column=2, row=2, sticky="nsew")
|
||||
self.status_panel.grid(column=0, row=3, columnspan=3, sticky="nsew")
|
||||
|
||||
self.tunnel_list.populate(self.tunnels.keys())
|
||||
self.update_ui_state()
|
||||
|
||||
def on_theme_toggle(self):
|
||||
current_theme = ConfigManager.get("theme")
|
||||
new_theme = "dark" if current_theme == "light" else "light"
|
||||
ThemeManager.change_theme(self, new_theme, new_theme)
|
||||
self.header.header_label.config(fg="#ffffff")
|
||||
self.tunnel_details.color_label()
|
||||
self.menu_bar.update_theme_label()
|
||||
self.menu_bar.settings.entryconfigure(
|
||||
2, label=self.menu_bar.theme_label.get())
|
||||
|
||||
def update_ui_state(self):
|
||||
self.active_tunnel_name = Tunnel.get_active()
|
||||
self.controls.set_start_stop_button_state(
|
||||
bool(self.active_tunnel_name), self.tunnel_list.get_size())
|
||||
self.controls.update_tooltips(self.tunnel_list.get_size())
|
||||
self.tunnel_details.update_details(
|
||||
self.active_tunnel_name, self.tunnels)
|
||||
self.status_panel.enable_controls(
|
||||
self.tunnel_list.get_size(), bool(self.tunnel_list.get_selected()))
|
||||
self.status_panel.update_autoconnect_display()
|
||||
|
||||
def on_tunnel_select(self, event=None):
|
||||
self.status_panel.enable_controls(self.tunnel_list.get_size(), True)
|
||||
|
||||
def wg_switch(self):
|
||||
try:
|
||||
if not self.active_tunnel_name:
|
||||
selected_tunnel = self.tunnel_list.get_selected()
|
||||
if not selected_tunnel:
|
||||
MessageDialog(
|
||||
"info", Msg.STR["sel_list"], title=Msg.STR["sel_tl"])
|
||||
return
|
||||
self.handle_connection_state("start", selected_tunnel)
|
||||
else:
|
||||
self.handle_connection_state("stop")
|
||||
except IndexError:
|
||||
MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
|
||||
|
||||
def handle_connection_state(self, action: str, tunnel_name: str = None):
|
||||
cmd = []
|
||||
if action == "stop":
|
||||
if self.active_tunnel_name:
|
||||
cmd = ["nmcli", "connection", "down", self.active_tunnel_name]
|
||||
elif action == "start" and tunnel_name:
|
||||
cmd = ["nmcli", "connection", "up", tunnel_name]
|
||||
|
||||
if cmd:
|
||||
process: CompletedProcess[str] = run(
|
||||
cmd, capture_output=True, text=True, check=False)
|
||||
if process.stderr:
|
||||
app_logger.log(f"{process.stderr} Code: {process.returncode}")
|
||||
|
||||
self.update_ui_state()
|
||||
|
||||
def import_sl(self):
|
||||
try:
|
||||
dialog = CustomFileDialog(
|
||||
self,
|
||||
initial_dir=f"{Path.home()}",
|
||||
title="Select Wireguard config File",
|
||||
filetypes=[("WG config files", "*.conf")],
|
||||
dialog_mode="open"
|
||||
)
|
||||
self.wait_window(dialog)
|
||||
filepath = dialog.get_selected_file()
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
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"])
|
||||
return
|
||||
|
||||
if not CryptoUtil.is_valid_base64(f"{data_import[key_name]['PrivateKey']}="):
|
||||
MessageDialog(
|
||||
"error", Msg.STR["invalid_base64"], title=Msg.STR["imp_err"])
|
||||
return
|
||||
|
||||
filepath = Path(filepath)
|
||||
truncated_name = (
|
||||
filepath.name[-17:] if len(filepath.name) > 17 else filepath.name)
|
||||
import_file = shutil.copy2(
|
||||
filepath, AppConfig.TEMP_DIR / truncated_name)
|
||||
import_file = Path(import_file)
|
||||
|
||||
if self.active_tunnel_name:
|
||||
self.handle_connection_state("stop")
|
||||
|
||||
process: CompletedProcess[str] = run(
|
||||
["nmcli", "connection", "import", "type",
|
||||
"wireguard", "file", import_file],
|
||||
capture_output=True, text=True, check=False)
|
||||
if process.stderr:
|
||||
app_logger.log(f"{process.stderr} Code: {process.returncode}")
|
||||
|
||||
CryptoUtil.encrypt(getpass.getuser())
|
||||
CryptoUtil.decrypt(getpass.getuser()) # Decrypt all files again
|
||||
self.active_tunnel_name = Tunnel.get_active()
|
||||
self.tunnels = Tunnel.parse_files_to_dictionary(
|
||||
directory=AppConfig.TEMP_DIR) # Read from TEMP_DIR
|
||||
if self.tunnels is None:
|
||||
self.tunnels = {}
|
||||
self.tunnel_list.populate(self.tunnels.keys())
|
||||
self.tunnel_list.set_selection(0)
|
||||
|
||||
run(["nmcli", "con", "mod", self.active_tunnel_name,
|
||||
"connection.autoconnect", "no"], check=False)
|
||||
self.update_ui_state()
|
||||
LxTools.clean_files(AppConfig.TEMP_DIR, file=None)
|
||||
|
||||
except (UnboundLocalError, TypeError, FileNotFoundError):
|
||||
MessageDialog(
|
||||
"error", Msg.STR["no_valid_file"], title=Msg.STR["imp_err"])
|
||||
except Exception as e:
|
||||
app_logger.log(f"Import failed: {e}")
|
||||
|
||||
def delete(self):
|
||||
try:
|
||||
select_tl = self.tunnel_list.get_selected()
|
||||
if not select_tl:
|
||||
MessageDialog(
|
||||
"info", Msg.STR["sel_list"], title=Msg.STR["sel_tl"])
|
||||
return
|
||||
|
||||
run(["nmcli", "connection", "delete", select_tl], check=False)
|
||||
Path.unlink(f"{AppConfig.CONFIG_DIR}/{select_tl}.dat")
|
||||
|
||||
if select_tl == ConfigManager.get("autostart"):
|
||||
ConfigManager.set("autostart", "off")
|
||||
|
||||
self.tunnel_list.delete_selected()
|
||||
del self.tunnels[select_tl]
|
||||
self.update_ui_state()
|
||||
|
||||
except IndexError:
|
||||
MessageDialog("info", Msg.STR["tl_first"], title=Msg.STR["sel_tl"])
|
||||
|
||||
def box_set(self):
|
||||
try:
|
||||
select_tl = self.tunnel_list.get_selected()
|
||||
if self.status_panel.selected_option.get() == 1 and select_tl:
|
||||
ConfigManager.set("autostart", select_tl)
|
||||
else:
|
||||
ConfigManager.set("autostart", "off")
|
||||
except IndexError:
|
||||
self.status_panel.selected_option.set(0)
|
||||
self.update_ui_state()
|
||||
|
||||
def tl_rename(self):
|
||||
special_characters = ["\\", "/", "{", "}", " "]
|
||||
new_name = self.status_panel.get_rename_value()
|
||||
|
||||
if len(new_name) > 12:
|
||||
MessageDialog("info", Msg.STR["sign_len"],
|
||||
title=Msg.STR["ren_err"])
|
||||
return
|
||||
if len(new_name) == 0:
|
||||
MessageDialog(
|
||||
"info", Msg.STR["zero_signs"], title=Msg.STR["ren_err"])
|
||||
return
|
||||
if any(ch in special_characters for ch in new_name):
|
||||
MessageDialog(
|
||||
"info", Msg.STR["false_signs"], title=Msg.STR["ren_err"])
|
||||
return
|
||||
if new_name in self.tunnels:
|
||||
MessageDialog(
|
||||
"info", Msg.STR["is_in_use"], title=Msg.STR["ren_err"])
|
||||
return
|
||||
|
||||
try:
|
||||
old_name = self.tunnel_list.get_selected()
|
||||
if not old_name:
|
||||
MessageDialog(
|
||||
"info", Msg.STR["sel_list"], title=Msg.STR["ren_err"])
|
||||
return
|
||||
|
||||
run(["nmcli", "connection", "modify", old_name,
|
||||
"connection.id", new_name], check=False)
|
||||
|
||||
source = Path(f"{AppConfig.CONFIG_DIR}/{old_name}.dat")
|
||||
destination = AppConfig.CONFIG_DIR / f"{new_name}.dat"
|
||||
source.replace(destination)
|
||||
|
||||
self.tunnels[new_name] = self.tunnels.pop(old_name)
|
||||
if old_name == ConfigManager.get("autostart"):
|
||||
ConfigManager.set("autostart", new_name)
|
||||
|
||||
self.tunnel_list.delete_selected()
|
||||
self.tunnel_list.populate(self.tunnels.keys())
|
||||
self.status_panel.clear_rename_entry()
|
||||
self.update_ui_state()
|
||||
|
||||
except IndexError:
|
||||
MessageDialog("info", Msg.STR["sel_list"],
|
||||
title=Msg.STR["ren_err"])
|
179
ui/menu_bar.py
Normal file
@@ -0,0 +1,179 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import tkinter as tk
|
||||
import webbrowser
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from tkinter import ttk
|
||||
|
||||
from logger import app_logger
|
||||
|
||||
from shared_libs.common_tools import ConfigManager, Tooltip
|
||||
from shared_libs.gitea import GiteaUpdate
|
||||
from shared_libs.message import MessageDialog
|
||||
from shared_libs.wp_app_config import AppConfig, Msg
|
||||
|
||||
|
||||
class MenuBar(ttk.Frame):
|
||||
def __init__(self, container, image_manager, tooltip_state, on_theme_toggle, toggle_log_window, **kwargs):
|
||||
super().__init__(container, **kwargs)
|
||||
self.image_manager = image_manager
|
||||
self.tooltip_state = tooltip_state
|
||||
self.on_theme_toggle = on_theme_toggle
|
||||
|
||||
self.options_btn = ttk.Menubutton(self, text=Msg.STR["options"])
|
||||
self.options_btn.grid(column=0, row=0)
|
||||
|
||||
Tooltip(self.options_btn, Msg.TTIP["settings"], state_var=self.tooltip_state)
|
||||
|
||||
self.set_update = tk.IntVar()
|
||||
self.settings = tk.Menu(self, relief="flat")
|
||||
self.options_btn.configure(menu=self.settings, style="Toolbutton")
|
||||
self.settings.add_checkbutton(
|
||||
label=Msg.STR["disable_updates"],
|
||||
command=lambda: self.update_setting(self.set_update.get()),
|
||||
variable=self.set_update,
|
||||
)
|
||||
self.update_label = tk.StringVar()
|
||||
self.updates_lb = ttk.Label(self, textvariable=self.update_label)
|
||||
self.updates_lb.grid(column=2, row=0)
|
||||
self.updates_lb.grid_remove()
|
||||
self.update_tooltip = tk.StringVar()
|
||||
self.update_foreground = tk.StringVar(value="red")
|
||||
self.update_label.trace_add("write", self.update_label_display)
|
||||
self.update_foreground.trace_add("write", self.update_label_display)
|
||||
res = GiteaUpdate.api_down(
|
||||
AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates")
|
||||
)
|
||||
self.update_ui_for_update(res)
|
||||
|
||||
# Tooltip Menu
|
||||
self.tooltip_label = tk.StringVar()
|
||||
self.tooltip_update_label()
|
||||
self.settings.add_command(
|
||||
label=self.tooltip_label.get(), command=self.tooltips_toggle
|
||||
)
|
||||
# Label show dark or light
|
||||
self.theme_label = tk.StringVar()
|
||||
self.update_theme_label()
|
||||
self.settings.add_command(
|
||||
label=self.theme_label.get(), command=self.on_theme_toggle
|
||||
)
|
||||
|
||||
# About BTN Menu / Label
|
||||
self.about_btn = ttk.Button(
|
||||
self, text=Msg.STR["about"], style="Toolbutton", command=self.about
|
||||
)
|
||||
self.about_btn.grid(column=1, row=0)
|
||||
|
||||
self.columnconfigure(10, weight=1) # Add a column with weight to push button to the right
|
||||
|
||||
self.log_btn = ttk.Button(
|
||||
self,
|
||||
image=self.image_manager.get_icon("log_small"),
|
||||
style="Toolbutton",
|
||||
command=toggle_log_window,
|
||||
)
|
||||
self.log_btn.grid(column=11, row=0, sticky='e')
|
||||
Tooltip(self.log_btn, "Show Log", state_var=self.tooltip_state)
|
||||
|
||||
def update_label_display(self, *args):
|
||||
self.updates_lb.configure(foreground=self.update_foreground.get())
|
||||
if self.update_label.get():
|
||||
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||
else:
|
||||
self.updates_lb.grid_remove()
|
||||
|
||||
def updater(self):
|
||||
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)
|
||||
|
||||
def update_ui_for_update(self, res):
|
||||
if hasattr(self, "update_btn"):
|
||||
self.update_btn.grid_forget()
|
||||
delattr(self, "update_btn")
|
||||
|
||||
if res == "False":
|
||||
self.set_update.set(value=1)
|
||||
self.update_label.set(Msg.STR["update_search_off"])
|
||||
self.update_tooltip.set(Msg.TTIP["updates_disabled"])
|
||||
self.update_foreground.set("")
|
||||
Tooltip(self.updates_lb, self.update_tooltip.get(), state_var=self.tooltip_state)
|
||||
elif res == "No Internet Connection!":
|
||||
self.update_label.set(Msg.STR["no_server_connection"])
|
||||
self.update_foreground.set("red")
|
||||
Tooltip(self.updates_lb, Msg.TTIP["no_server_conn_tt"], state_var=self.tooltip_state)
|
||||
elif res == "No Updates":
|
||||
self.update_label.set(Msg.STR["no_updates"])
|
||||
self.update_tooltip.set(Msg.TTIP["up_to_date"])
|
||||
self.update_foreground.set("")
|
||||
Tooltip(self.updates_lb, self.update_tooltip.get(), state_var=self.tooltip_state)
|
||||
else:
|
||||
self.set_update.set(value=0)
|
||||
self.update_label.set("")
|
||||
self.update_btn = ttk.Button(
|
||||
self,
|
||||
image=self.image_manager.get_icon("settings"),
|
||||
style="Toolbutton",
|
||||
command=self.updater,
|
||||
)
|
||||
self.update_btn.grid(column=5, row=0, padx=0)
|
||||
Tooltip(self.update_btn, Msg.TTIP["install_new_version"], state_var=self.tooltip_state)
|
||||
|
||||
@staticmethod
|
||||
def about() -> None:
|
||||
MessageDialog(
|
||||
"info",
|
||||
Msg.STR["about_msg"],
|
||||
buttons=["OK", Msg.STR["goto_git"]],
|
||||
title=Msg.STR["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:
|
||||
if update_res == 1:
|
||||
ConfigManager.set("updates", "off")
|
||||
self.update_ui_for_update("False")
|
||||
else:
|
||||
ConfigManager.set("updates", "on")
|
||||
try:
|
||||
res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, "on")
|
||||
if hasattr(self, "update_btn"):
|
||||
self.update_btn.grid_forget()
|
||||
if hasattr(self, "updates_lb"):
|
||||
self.updates_lb.grid_forget()
|
||||
self.update_ui_for_update(res)
|
||||
except Exception as e:
|
||||
app_logger.log(f"Error checking for updates: {e}")
|
||||
self.update_ui_for_update("No Internet Connection!")
|
||||
|
||||
def tooltip_update_label(self) -> None:
|
||||
if self.tooltip_state.get():
|
||||
self.tooltip_label.set(Msg.STR["disable_tooltips"])
|
||||
else:
|
||||
self.tooltip_label.set(Msg.STR["enable_tooltips"])
|
||||
|
||||
def tooltips_toggle(self):
|
||||
new_bool_state = not self.tooltip_state.get()
|
||||
ConfigManager.set("tooltips", str(new_bool_state))
|
||||
self.tooltip_state.set(new_bool_state)
|
||||
self.tooltip_update_label()
|
||||
self.settings.entryconfigure(1, label=self.tooltip_label.get())
|
||||
|
||||
def update_theme_label(self) -> None:
|
||||
current_theme = ConfigManager.get("theme")
|
||||
if current_theme == "light":
|
||||
self.theme_label.set(Msg.STR["dark"])
|
||||
else:
|
||||
self.theme_label.set(Msg.STR["light"])
|
||||
|
100
ui/status_panel.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
from shared_libs.common_tools import Tooltip, ConfigManager
|
||||
from shared_libs.wp_app_config import Msg
|
||||
|
||||
|
||||
class StatusPanel(ttk.Frame):
|
||||
def __init__(self, container, tooltip_state, rename_callback, autoconnect_callback, **kwargs):
|
||||
super().__init__(container, **kwargs)
|
||||
|
||||
self.columnconfigure(1, weight=1)
|
||||
|
||||
self.tooltip_state = tooltip_state
|
||||
|
||||
# Autoconnect Frame
|
||||
autoconnect_frame = ttk.Frame(self)
|
||||
autoconnect_frame.grid(column=0, row=0, sticky="w")
|
||||
|
||||
self.selected_option = tk.IntVar()
|
||||
self.wg_autostart = ttk.Checkbutton(
|
||||
autoconnect_frame,
|
||||
text=Msg.STR["autoconnect_on"],
|
||||
variable=self.selected_option,
|
||||
command=autoconnect_callback,
|
||||
)
|
||||
self.wg_autostart.grid(column=0, row=0, pady=5,
|
||||
padx=(10, 0), sticky="ew")
|
||||
Tooltip(self.wg_autostart,
|
||||
Msg.TTIP["autostart_info"], state_var=self.tooltip_state)
|
||||
|
||||
self.autoconnect_var = tk.StringVar()
|
||||
self.autoconnect_label = ttk.Label(
|
||||
autoconnect_frame,
|
||||
textvariable=self.autoconnect_var,
|
||||
foreground="#0071ff",
|
||||
width=18,
|
||||
)
|
||||
self.autoconnect_label.config(font=("Ubuntu", 11))
|
||||
self.autoconnect_label.grid(column=1, row=0, sticky="ew", pady=5)
|
||||
|
||||
# Rename Frame
|
||||
rename_frame = ttk.Frame(self)
|
||||
rename_frame.grid(column=1, row=0, sticky="e", padx=10)
|
||||
|
||||
self.lb_rename = ttk.Entry(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.insert(0, Msg.STR["max_chars"])
|
||||
self.lb_rename.config(state="disable")
|
||||
Tooltip(self.lb_rename,
|
||||
Msg.TTIP["rename_tl_info"], state_var=self.tooltip_state)
|
||||
|
||||
self.btn_rename = ttk.Button(
|
||||
rename_frame,
|
||||
text=Msg.STR["rename"],
|
||||
state="disable",
|
||||
command=rename_callback,
|
||||
width=15,
|
||||
)
|
||||
self.btn_rename.grid(column=1, row=0, pady=10, sticky="nsew")
|
||||
|
||||
self.update_autoconnect_display()
|
||||
|
||||
def update_autoconnect_display(self):
|
||||
autostart_tunnel = ConfigManager.get("autostart")
|
||||
if autostart_tunnel and autostart_tunnel != "off":
|
||||
self.selected_option.set(1)
|
||||
self.autoconnect_var.set(autostart_tunnel)
|
||||
else:
|
||||
self.selected_option.set(0)
|
||||
self.autoconnect_var.set(Msg.STR["no_autoconnect"])
|
||||
|
||||
def get_rename_value(self):
|
||||
return self.lb_rename.get()
|
||||
|
||||
def clear_rename_entry(self):
|
||||
self.lb_rename.delete(0, tk.END)
|
||||
|
||||
def enable_controls(self, list_box_size, is_selection):
|
||||
if list_box_size > 0:
|
||||
self.wg_autostart.config(state="normal")
|
||||
Tooltip(self.wg_autostart,
|
||||
Msg.TTIP["autostart"], state_var=self.tooltip_state)
|
||||
if is_selection:
|
||||
self.lb_rename.config(state="normal")
|
||||
self.btn_rename.config(state="normal")
|
||||
self.lb_rename.delete(0, tk.END)
|
||||
Tooltip(self.lb_rename,
|
||||
Msg.TTIP["rename_tl"], state_var=self.tooltip_state)
|
||||
else:
|
||||
self.wg_autostart.config(state="disabled")
|
||||
self.lb_rename.config(state="disabled")
|
||||
self.btn_rename.config(state="disabled")
|
||||
self.lb_rename.delete(0, tk.END)
|
||||
self.lb_rename.insert(0, Msg.STR["max_chars"])
|
||||
Tooltip(self.wg_autostart,
|
||||
Msg.TTIP["autostart_info"], state_var=self.tooltip_state)
|
||||
Tooltip(self.lb_rename,
|
||||
Msg.TTIP["rename_tl_info"], state_var=self.tooltip_state)
|
73
ui/tunnel_details.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
from shared_libs.common_tools import ConfigManager
|
||||
from shared_libs.wp_app_config import Msg
|
||||
|
||||
|
||||
class TunnelDetails(ttk.Frame):
|
||||
def __init__(self, container, **kwargs):
|
||||
super().__init__(container, **kwargs)
|
||||
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
self.rowconfigure(1, weight=1)
|
||||
self.rowconfigure(2, weight=1)
|
||||
|
||||
self.active_frame = ttk.LabelFrame(self, text=Msg.STR["active_tunnel"])
|
||||
self.active_frame.grid(column=0, row=0, sticky="nsew", padx=10, pady=5)
|
||||
self.active_frame.columnconfigure(0, weight=1)
|
||||
self.active_frame.rowconfigure(0, weight=1)
|
||||
|
||||
self.interface_frame = ttk.LabelFrame(self, text=Msg.STR["interface"])
|
||||
self.interface_frame.grid(column=0, row=1, sticky="nsew", padx=10, pady=5)
|
||||
self.interface_frame.columnconfigure(0, weight=1)
|
||||
self.interface_frame.rowconfigure(0, weight=1)
|
||||
self.interface_frame.rowconfigure(1, weight=1)
|
||||
|
||||
self.peer_frame = ttk.LabelFrame(self, text=Msg.STR["peer"])
|
||||
self.peer_frame.grid(column=0, row=2, sticky="nsew", padx=10, pady=5)
|
||||
self.peer_frame.columnconfigure(0, weight=1)
|
||||
self.peer_frame.rowconfigure(0, weight=1)
|
||||
|
||||
self.str_var = tk.StringVar()
|
||||
self.add_var = tk.StringVar()
|
||||
self.dns_var = tk.StringVar()
|
||||
self.enp_var = tk.StringVar()
|
||||
|
||||
self.lb_tunnel = None
|
||||
self.color_label()
|
||||
|
||||
self.address_label = ttk.Label(self.interface_frame, textvariable=self.add_var, foreground="#0071ff")
|
||||
self.address_label.grid(column=0, row=0, sticky="nsew", padx=10, pady=(0, 20))
|
||||
self.address_label.config(font=("Ubuntu", 9))
|
||||
|
||||
self.dns_label = ttk.Label(self.interface_frame, textvariable=self.dns_var, foreground="#0071ff")
|
||||
self.dns_label.grid(column=0, row=1, sticky="nsew", padx=10, pady=(0, 20))
|
||||
self.dns_label.config(font=("Ubuntu", 9))
|
||||
|
||||
self.endpoint_label = ttk.Label(self.peer_frame, textvariable=self.enp_var, foreground="#0071ff")
|
||||
self.endpoint_label.grid(column=0, row=0, sticky="nsew", padx=10, pady=(0, 30))
|
||||
self.endpoint_label.config(font=("Ubuntu", 9))
|
||||
|
||||
def color_label(self):
|
||||
if self.lb_tunnel:
|
||||
self.lb_tunnel.destroy()
|
||||
|
||||
foreground = "yellow" if ConfigManager.get("theme") != "light" else "green"
|
||||
self.lb_tunnel = ttk.Label(self.active_frame, textvariable=self.str_var, foreground=foreground)
|
||||
self.lb_tunnel.config(font=("Ubuntu", 11, "bold"))
|
||||
self.lb_tunnel.grid(column=0, row=0, padx=10, pady=(0, 10), sticky="nsew")
|
||||
|
||||
def update_details(self, active_tunnel_name, tunnel_data):
|
||||
self.str_var.set(active_tunnel_name)
|
||||
if active_tunnel_name and tunnel_data and active_tunnel_name in tunnel_data:
|
||||
values = tunnel_data[active_tunnel_name]
|
||||
self.add_var.set(f"Address: {values.get('Address', '')}")
|
||||
self.dns_var.set(f" DNS: {values.get('DNS', '')}")
|
||||
self.enp_var.set(f"Endpoint: {values.get('Endpoint', '')}")
|
||||
else:
|
||||
self.add_var.set("")
|
||||
self.dns_var.set("")
|
||||
self.enp_var.set("")
|
||||
self.color_label()
|
57
ui/tunnel_list.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
from shared_libs.wp_app_config import Msg
|
||||
|
||||
|
||||
class TunnelList(ttk.Frame):
|
||||
def __init__(self, container, selection_callback, **kwargs):
|
||||
super().__init__(container, **kwargs)
|
||||
|
||||
self.list_frame = ttk.LabelFrame(self, text=Msg.STR["tunnels"])
|
||||
self.list_frame.grid(
|
||||
column=0, row=0, sticky="nsew", padx=(10, 0), pady=(0, 8))
|
||||
|
||||
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>>", selection_callback)
|
||||
|
||||
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.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(0, weight=1)
|
||||
self.list_frame.columnconfigure(0, weight=1)
|
||||
self.list_frame.rowconfigure(0, weight=1)
|
||||
|
||||
def populate(self, tunnels):
|
||||
self.list_box.delete(0, tk.END)
|
||||
for tunnel in tunnels:
|
||||
self.list_box.insert("end", tunnel)
|
||||
self.list_box.update()
|
||||
|
||||
def get_selected(self):
|
||||
selection_indices = self.list_box.curselection()
|
||||
if not selection_indices:
|
||||
return None
|
||||
return self.list_box.get(selection_indices[0])
|
||||
|
||||
def get_size(self):
|
||||
return self.list_box.size()
|
||||
|
||||
def set_selection(self, index):
|
||||
self.list_box.selection_clear(0, tk.END)
|
||||
self.list_box.selection_set(index)
|
||||
|
||||
def delete_selected(self):
|
||||
selection_indices = self.list_box.curselection()
|
||||
if selection_indices:
|
||||
self.list_box.delete(selection_indices[0])
|
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
"""App configuration for Wire-Py"""
|
||||
import logging
|
||||
from logger import app_logger
|
||||
from pathlib import Path
|
||||
from subprocess import CompletedProcess, run
|
||||
from typing import Dict, Any
|
||||
@@ -27,11 +27,6 @@ class AppConfig:
|
||||
consistently and perform system-level setup tasks.
|
||||
"""
|
||||
|
||||
# Logging
|
||||
LOG_DIR = Path.home() / ".local/share/lxlogs"
|
||||
Path(LOG_DIR).mkdir(parents=True, exist_ok=True)
|
||||
LOG_FILE_PATH = LOG_DIR / "wirepy.log"
|
||||
|
||||
# Base paths
|
||||
BASE_DIR: Path = Path.home()
|
||||
CONFIG_DIR: Path = BASE_DIR / ".config/wire_py"
|
||||
@@ -47,7 +42,7 @@ class AppConfig:
|
||||
"# Theme": "dark",
|
||||
"# Tooltips": True,
|
||||
"# Autostart": "off",
|
||||
"# Logfile": LOG_FILE_PATH,
|
||||
|
||||
}
|
||||
|
||||
# Updates
|
||||
@@ -59,7 +54,7 @@ class AppConfig:
|
||||
# UI configuration
|
||||
UI_CONFIG: Dict[str, Any] = {
|
||||
"window_title": "WirePy",
|
||||
"window_title2": "LogViewer",
|
||||
|
||||
"window_size": (590, 460),
|
||||
"font_family": "Ubuntu",
|
||||
"font_size": 11,
|
||||
@@ -121,18 +116,12 @@ class AppConfig:
|
||||
check=False,
|
||||
)
|
||||
if process.returncode == 0:
|
||||
logging.info(process.stdout)
|
||||
app_logger.log(process.stdout)
|
||||
|
||||
if process.stderr:
|
||||
logging.error(
|
||||
app_logger.log(
|
||||
f"{process.stderr}")
|
||||
|
||||
@classmethod
|
||||
def ensure_log(cls) -> None:
|
||||
"""Ensures that the log file exists"""
|
||||
if not cls.LOG_FILE_PATH.exists():
|
||||
cls.LOG_FILE_PATH.touch()
|
||||
|
||||
|
||||
# here is initializing the class for translation strings
|
||||
_ = Translate.setup_translations("wirepy")
|
||||
@@ -247,4 +236,4 @@ class Msg:
|
||||
"no_server_conn_tt": _("Could not connect to update server"),
|
||||
"up_to_date": _("Congratulations! Wire-Py is up to date"),
|
||||
"install_new_version": _("Click to install new version"),
|
||||
}
|
||||
}
|
||||
|