Compare commits
2 Commits
0cdad100b6
...
dba6138aa7
Author | SHA1 | Date | |
---|---|---|---|
dba6138aa7 | |||
d0aed9e253 |
4
.idea/workspace.xml
generated
4
.idea/workspace.xml
generated
@ -6,7 +6,7 @@
|
|||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Update Translate Files">
|
<list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Update Translate Files">
|
||||||
<change afterPath="$PROJECT_DIR$/.vscode/settings.json" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/.vscode/settings.json" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/cls_mth_fc.py" beforeDir="false" afterPath="$PROJECT_DIR$/cls_mth_fc.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/common_tools.py" beforeDir="false" afterPath="$PROJECT_DIR$/common_tools.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/ssl_decrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/ssl_decrypt.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/ssl_decrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/ssl_decrypt.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/ssl_encrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/ssl_encrypt.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/ssl_encrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/ssl_encrypt.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/wirepy.py" beforeDir="false" afterPath="$PROJECT_DIR$/wirepy.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/wirepy.py" beforeDir="false" afterPath="$PROJECT_DIR$/wirepy.py" afterDir="false" />
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"keyToString": {
|
"keyToString": {
|
||||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||||
"Python.INSTALL.executor": "Run",
|
"Python.INSTALL.executor": "Run",
|
||||||
"Python.cls_mth_fc.executor": "Run",
|
"Python.common_tools.executor": "Run",
|
||||||
"Python.install.executor": "Run",
|
"Python.install.executor": "Run",
|
||||||
"Python.main.executor": "Run",
|
"Python.main.executor": "Run",
|
||||||
"Python.messagebox.executor": "Run",
|
"Python.messagebox.executor": "Run",
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +1,6 @@
|
|||||||
{
|
{
|
||||||
"workbench.settings.openDefaultSettings": true
|
"workbench.settings.openDefaultSettings": true
|
||||||
|
"workbench.startupEditor": "none"
|
||||||
|
"update.showReleaseNotes": false
|
||||||
|
"terminal.integrated.fontSize": 18
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ My standard System: Linux Mint 22 Cinnamon
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- os import in cls_mth_fc.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
|
||||||
|
|
||||||
|
Binary file not shown.
BIN
__pycache__/common_tools.cpython-312.pyc
Normal file
BIN
__pycache__/common_tools.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -169,18 +169,6 @@ class LxTools(tk.Tk):
|
|||||||
if file is not None:
|
if file is not None:
|
||||||
Path.unlink(file)
|
Path.unlink(file)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def if_tip(path: Path) -> bool:
|
|
||||||
"""
|
|
||||||
method that writes in file whether tooltip is displayed or not
|
|
||||||
"""
|
|
||||||
lines = Path(path).read_text(encoding="utf-8")
|
|
||||||
if "False\n" in lines:
|
|
||||||
tip = False
|
|
||||||
else:
|
|
||||||
tip = True
|
|
||||||
return tip
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def msg_window(image_path: Path, image_path2: Path, w_title: str, w_txt: str, txt2: Optional[str] = None,
|
def msg_window(image_path: Path, image_path2: Path, w_title: str, w_txt: str, txt2: Optional[str] = None,
|
||||||
com: Optional[str] = None) -> None:
|
com: Optional[str] = None) -> None:
|
||||||
@ -382,6 +370,82 @@ class Tunnel:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ConfigManager with caching
|
||||||
|
class ConfigManager:
|
||||||
|
"""
|
||||||
|
Universal class for managing configuration files with caching.
|
||||||
|
Can be reused in different projects.
|
||||||
|
"""
|
||||||
|
_config = None
|
||||||
|
_config_file = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def init(cls, config_file):
|
||||||
|
"""Initial the Configmanager with the given config file"""
|
||||||
|
cls._config_file = config_file
|
||||||
|
cls._config = None # Reset the cache
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls):
|
||||||
|
"""Load the config file and return the config as dict"""
|
||||||
|
if not cls._config:
|
||||||
|
try:
|
||||||
|
lines = Path(cls._config_file).read_text(encoding="utf-8").splitlines()
|
||||||
|
cls._config = {
|
||||||
|
'updates': lines[1].strip(),
|
||||||
|
'theme': lines[3].strip(),
|
||||||
|
'tooltips': lines[5].strip() == "True", # is converted here to boolean!!!
|
||||||
|
'autostart': lines[7].strip() if len(lines) > 7 else 'off'
|
||||||
|
}
|
||||||
|
except (IndexError, FileNotFoundError):
|
||||||
|
# DeDefault values in case of error
|
||||||
|
cls._config = {
|
||||||
|
'updates': 'on',
|
||||||
|
'theme': 'light',
|
||||||
|
'tooltips': "True", # Default Value as string !
|
||||||
|
'autostart': 'off'
|
||||||
|
}
|
||||||
|
return cls._config
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def save(cls):
|
||||||
|
"""Save the config to the config file"""
|
||||||
|
if cls._config:
|
||||||
|
lines = [
|
||||||
|
'# Configuration\n',
|
||||||
|
f"{cls._config['updates']}\n",
|
||||||
|
'# Theme\n',
|
||||||
|
f"{cls._config['theme']}\n",
|
||||||
|
'# Tooltips\n',
|
||||||
|
f"{str(cls._config['tooltips'])}\n",
|
||||||
|
'# Autostart\n',
|
||||||
|
f"{cls._config['autostart']}\n"
|
||||||
|
]
|
||||||
|
Path(cls._config_file).write_text(''.join(lines), encoding="utf-8")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set(cls, key, value):
|
||||||
|
"""Sets a configuration value and saves the change"""
|
||||||
|
cls.load()
|
||||||
|
cls._config[key] = value
|
||||||
|
cls.save()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, key, default=None):
|
||||||
|
"""Returns a configuration value"""
|
||||||
|
config = cls.load()
|
||||||
|
return config.get(key, default)
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeManager:
|
||||||
|
@staticmethod
|
||||||
|
def change_theme(root, theme_in_use, theme_name=None):
|
||||||
|
"""Change application theme centrally"""
|
||||||
|
root.tk.call("set_theme", theme_in_use)
|
||||||
|
if theme_in_use == theme_name:
|
||||||
|
ConfigManager.set("theme", theme_in_use)
|
||||||
|
|
||||||
|
|
||||||
class GiteaUpdate:
|
class GiteaUpdate:
|
||||||
"""
|
"""
|
||||||
Calling download requests the download URL of the running script,
|
Calling download requests the download URL of the running script,
|
||||||
@ -390,35 +454,46 @@ class GiteaUpdate:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def api_down(update_api_url: str, version: str, file: Optional[Path] = None) -> str:
|
def api_down(update_api_url: str, version: str, update_setting: str = None) -> str:
|
||||||
"""
|
"""
|
||||||
Checks for updates via API
|
Checks for updates via API
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
update_api_url: Update API URL
|
update_api_url: Update API URL
|
||||||
version: Current version
|
version: Current version
|
||||||
file: Optional - Configuration file
|
update_setting: Update setting from ConfigManager (on/off)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
New version or status message
|
New version or status message
|
||||||
"""
|
"""
|
||||||
|
# If updates are disabled, return immediately
|
||||||
|
if update_setting != "on":
|
||||||
|
return "False"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response: requests.Response = requests.get(update_api_url, timeout=10)
|
response: requests.Response = requests.get(update_api_url, timeout=10)
|
||||||
response_dict: Any = response.json()
|
response.raise_for_status() # Raise exception for HTTP errors
|
||||||
response_dict: Dict[str, Any] = response_dict[0]
|
|
||||||
with open(file, "r", encoding="utf-8") as set_f:
|
response_data = response.json()
|
||||||
set_f = set_f.read()
|
if not response_data:
|
||||||
if "on\n" in set_f:
|
return "No Updates"
|
||||||
if version[3:] != response_dict["tag_name"]:
|
|
||||||
req: str = response_dict["tag_name"]
|
latest_version = response_data[0].get("tag_name")
|
||||||
else:
|
if not latest_version:
|
||||||
req: str = "No Updates"
|
return "Invalid API Response"
|
||||||
else:
|
|
||||||
req: str = "False"
|
# Compare versions (strip 'v. ' prefix if present)
|
||||||
return req
|
current_version = version[3:] if version.startswith("v. ") else version
|
||||||
|
|
||||||
|
if current_version != latest_version:
|
||||||
|
return latest_version
|
||||||
|
else:
|
||||||
|
return "No Updates"
|
||||||
|
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
req: str = "No Internet Connection!"
|
return "No Internet Connection!"
|
||||||
return req
|
except (ValueError, KeyError, IndexError):
|
||||||
|
return "Invalid API Response"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def download(urld: str, res: str, image_path: Path = None, image_path2: Path = None, image_path3: Path = None,
|
def download(urld: str, res: str, image_path: Path = None, image_path2: Path = None, image_path3: Path = None,
|
||||||
@ -457,33 +532,44 @@ class GiteaUpdate:
|
|||||||
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], wt, msg_t)
|
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"], wt, msg_t)
|
||||||
|
|
||||||
|
|
||||||
class Tooltip:
|
class Tooltip():
|
||||||
"""
|
"""Class for Tooltip
|
||||||
class for Tooltip
|
from common_tools.py import Tooltip
|
||||||
|
|
||||||
import Tooltip
|
|
||||||
|
|
||||||
example: Tooltip(label, "Show tooltip on label")
|
example: Tooltip(label, "Show tooltip on label")
|
||||||
example: Tooltip(button, "Show tooltip on button")
|
example: Tooltip(button, "Show tooltip on button")
|
||||||
info: label and button are parent.
|
example: Tooltip(widget, "Text", state_var=tk.BooleanVar())
|
||||||
|
|
||||||
|
info: label and button are parent widgets.
|
||||||
|
NOTE: When using with state_var, pass the tk.BooleanVar object directly,
|
||||||
|
NOT its value. For example: use state_var=my_bool_var, NOT state_var=my_bool_var.get()
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, widget: Any, text: str, state_var: Optional[tk.BooleanVar] = None) -> None:
|
||||||
def __init__(self, widget: Any, text: str, tips: Optional[bool] = None) -> None:
|
"""Tooltip Class"""
|
||||||
"""
|
|
||||||
Tooltip Class
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.widget: Any = widget
|
self.widget: Any = widget
|
||||||
self.text: str = text
|
self.text: str = text
|
||||||
self.tooltip_window: Optional[Toplevel] = None
|
self.tooltip_window: Optional[Toplevel] = None
|
||||||
if tips:
|
self.state_var = state_var
|
||||||
|
|
||||||
|
# Initial binding based on current state
|
||||||
|
self.update_bindings()
|
||||||
|
|
||||||
|
# Add trace to the state_var if provided
|
||||||
|
if self.state_var is not None:
|
||||||
|
self.state_var.trace_add("write", self.update_bindings)
|
||||||
|
|
||||||
|
def update_bindings(self, *args) -> None:
|
||||||
|
"""Updates the bindings based on the current state"""
|
||||||
|
# Remove existing bindings first
|
||||||
|
self.widget.unbind("<Enter>")
|
||||||
|
self.widget.unbind("<Leave>")
|
||||||
|
|
||||||
|
# Add new bindings if tooltips are enabled
|
||||||
|
if self.state_var is None or self.state_var.get():
|
||||||
self.widget.bind("<Enter>", self.show_tooltip)
|
self.widget.bind("<Enter>", self.show_tooltip)
|
||||||
self.widget.bind("<Leave>", self.hide_tooltip)
|
self.widget.bind("<Leave>", self.hide_tooltip)
|
||||||
|
|
||||||
def show_tooltip(self, event: Optional[Any] = None) -> None:
|
def show_tooltip(self, event: Optional[Any] = None) -> None:
|
||||||
"""
|
"""Shows the tooltip"""
|
||||||
Shows the tooltip
|
|
||||||
"""
|
|
||||||
if self.tooltip_window or not self.text:
|
if self.tooltip_window or not self.text:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -491,23 +577,27 @@ class Tooltip:
|
|||||||
y: int
|
y: int
|
||||||
cx: int
|
cx: int
|
||||||
cy: int
|
cy: int
|
||||||
|
|
||||||
x, y, cx, cy = self.widget.bbox("insert")
|
x, y, cx, cy = self.widget.bbox("insert")
|
||||||
x += self.widget.winfo_rootx() + 65
|
x += self.widget.winfo_rootx() + 65
|
||||||
y += self.widget.winfo_rooty() + 40
|
y += self.widget.winfo_rooty() + 40
|
||||||
|
|
||||||
self.tooltip_window = tw = tk.Toplevel(self.widget)
|
self.tooltip_window = tw = tk.Toplevel(self.widget)
|
||||||
tw.wm_overrideredirect(True)
|
tw.wm_overrideredirect(True)
|
||||||
tw.wm_geometry(f"+{x}+{y}")
|
tw.wm_geometry(f"+{x}+{y}")
|
||||||
|
|
||||||
label: tk.Label = tk.Label(tw, text=self.text, background="lightgreen", foreground="black", relief="solid",
|
label: tk.Label = tk.Label(
|
||||||
borderwidth=1, padx=5, pady=5)
|
tw, text=self.text, background="lightgreen", foreground="black",
|
||||||
|
relief="solid", borderwidth=1, padx=5, pady=5
|
||||||
|
)
|
||||||
label.grid()
|
label.grid()
|
||||||
|
|
||||||
self.tooltip_window.after(2200, lambda: tw.destroy())
|
self.tooltip_window.after(2200, lambda: tw.destroy())
|
||||||
|
|
||||||
def hide_tooltip(self, event: Optional[Any] = None) -> None:
|
def hide_tooltip(self, event: Optional[Any] = None) -> None:
|
||||||
"""
|
"""Hides the tooltip"""
|
||||||
Hides the tooltip
|
|
||||||
"""
|
|
||||||
if self.tooltip_window:
|
if self.tooltip_window:
|
||||||
self.tooltip_window.destroy()
|
self.tooltip_window.destroy()
|
||||||
self.tooltip_window = None
|
self.tooltip_window = None
|
||||||
|
|
||||||
|
|
10
install
10
install
@ -17,7 +17,7 @@ install_file_with(){
|
|||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
sudo apt install python3-tk && \
|
sudo apt install python3-tk && \
|
||||||
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
sudo cp -fv wirepy.py start_wg.py wp_app_config.py common_tools.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
||||||
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
||||||
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
||||||
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
||||||
@ -43,7 +43,7 @@ install_arch_d(){
|
|||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
sudo pacman -S --noconfirm tk python3 python-requests && \
|
sudo pacman -S --noconfirm tk python3 python-requests && \
|
||||||
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
sudo cp -fv wirepy.py start_wg.py wp_app_config.py common_tools.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
||||||
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
||||||
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
||||||
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
||||||
@ -120,7 +120,7 @@ install(){
|
|||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
sudo dnf install python3-tkinter -y
|
sudo dnf install python3-tkinter -y
|
||||||
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
sudo cp -fv wirepy.py start_wg.py wp_app_config.py common_tools.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
||||||
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
||||||
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
||||||
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
||||||
@ -145,7 +145,7 @@ install(){
|
|||||||
rm -r ~/.config/wire_py && rm -r ~/.config/systemd
|
rm -r ~/.config/wire_py && rm -r ~/.config/systemd
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
sudo cp -fv wirepy.py start_wg.py wp_app_config.py cls_mth_fc.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
sudo cp -fv wirepy.py start_wg.py wp_app_config.py common_tools.py ssl_encrypt.py ssl_decrypt.py /usr/local/bin/ && \
|
||||||
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
sudo cp -uR lx-icons /usr/share/icons/ && sudo cp -uR TK-Themes /usr/share/ && \
|
||||||
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
sudo cp -u languages/de/*.mo /usr/share/locale/de/LC_MESSAGES/ && \
|
||||||
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
sudo cp -fv Wire-Py.desktop /usr/share/applications/ && \
|
||||||
@ -181,7 +181,7 @@ install(){
|
|||||||
|
|
||||||
remove(){
|
remove(){
|
||||||
sudo rm -f /usr/local/bin/wirepy /usr/local/bin/wirepy.py /usr/local/bin/start_wg.py \
|
sudo rm -f /usr/local/bin/wirepy /usr/local/bin/wirepy.py /usr/local/bin/start_wg.py \
|
||||||
/usr/local/bin/wp_app_config.py cls_mth_fc.py /usr/local/bin/ssl_encrypt.py /usr/local/bin/ssl_decrypt.py
|
/usr/local/bin/wp_app_config.py common_tools.py /usr/local/bin/ssl_encrypt.py /usr/local/bin/ssl_decrypt.py
|
||||||
if [ $? -ne 0 ]
|
if [ $? -ne 0 ]
|
||||||
then
|
then
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
from tkinter import filedialog, ttk
|
from tkinter import filedialog, ttk
|
||||||
from cls_mth_fc import Create, LxTools
|
from common_tools import Create, LxTools
|
||||||
from wp_app_config import AppConfig, Msg
|
from wp_app_config import AppConfig, Msg
|
||||||
import gettext
|
import gettext
|
||||||
import locale
|
import locale
|
||||||
|
@ -5,7 +5,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
from cls_mth_fc import LxTools
|
from common_tools import LxTools
|
||||||
from wp_app_config import AppConfig
|
from wp_app_config import AppConfig
|
||||||
|
|
||||||
#uname: Path = Path("/tmp/.log_user")
|
#uname: Path = Path("/tmp/.log_user")
|
||||||
|
521
wirepy.py
521
wirepy.py
@ -14,17 +14,13 @@ from pathlib import Path
|
|||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
from tkinter import TclError, filedialog, ttk
|
from tkinter import TclError, filedialog, ttk
|
||||||
|
|
||||||
from cls_mth_fc import (Create, GiteaUpdate, Tunnel, Tooltip, LxTools)
|
from common_tools import (ConfigManager, ThemeManager, Create, GiteaUpdate, Tunnel, Tooltip, LxTools)
|
||||||
from wp_app_config import AppConfig, Msg
|
from wp_app_config import AppConfig, Msg
|
||||||
|
|
||||||
LxTools.uos()
|
LxTools.uos()
|
||||||
Create.dir_and_files()
|
Create.dir_and_files()
|
||||||
Create.make_dir()
|
Create.make_dir()
|
||||||
Create.decrypt()
|
Create.decrypt()
|
||||||
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
|
|
||||||
VERSION: str = "v. 2.04.1725"
|
|
||||||
|
|
||||||
res = GiteaUpdate.api_down("https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases", VERSION, AppConfig.SETTINGS_FILE)
|
|
||||||
|
|
||||||
class Wirepy(tk.Tk):
|
class Wirepy(tk.Tk):
|
||||||
"""
|
"""
|
||||||
@ -43,13 +39,10 @@ class Wirepy(tk.Tk):
|
|||||||
self.geometry(f"{self.x_width}x{self.y_height}+{self.monitor_center_x}+{self.monitor_center_y}")
|
self.geometry(f"{self.x_width}x{self.y_height}+{self.monitor_center_x}+{self.monitor_center_y}")
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
self.rowconfigure(0, weight=1)
|
self.rowconfigure(0, weight=1)
|
||||||
self.style = ttk.Style(self)
|
|
||||||
self.tk.call("source", f"{AppConfig.SYSTEM_PATHS["tcl_path"]}/water.tcl")
|
self.tk.call("source", f"{AppConfig.SYSTEM_PATHS["tcl_path"]}/water.tcl")
|
||||||
lines = AppConfig.SETTINGS_FILE.read_text()
|
ConfigManager.init(AppConfig.SETTINGS_FILE)
|
||||||
if "light\n" in lines:
|
theme = ConfigManager.get("theme")
|
||||||
self.tk.call("set_theme", "light")
|
ThemeManager.change_theme(self, theme)
|
||||||
else:
|
|
||||||
self.tk.call("set_theme", "dark")
|
|
||||||
|
|
||||||
# Load the image file from the disk
|
# Load the image file from the disk
|
||||||
self.wg_icon = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"])
|
self.wg_icon = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_vpn"])
|
||||||
@ -57,58 +50,84 @@ class Wirepy(tk.Tk):
|
|||||||
# Set it as the window icon
|
# Set it as the window icon
|
||||||
self.iconphoto(True, self.wg_icon)
|
self.iconphoto(True, self.wg_icon)
|
||||||
|
|
||||||
tips = LxTools.if_tip(AppConfig.SETTINGS_FILE)
|
FrameWidgets(self).grid()
|
||||||
FrameWidgets(self, tips_enabled=tips).grid()
|
|
||||||
|
|
||||||
|
|
||||||
class FrameWidgets(ttk.Frame):
|
class FrameWidgets(ttk.Frame):
|
||||||
"""
|
"""
|
||||||
ttk frame class for better structure
|
ttk frame class for better structure
|
||||||
"""
|
"""
|
||||||
def __init__(self, container, tips_enabled=None, **kwargs):
|
def __init__(self, container, **kwargs):
|
||||||
super().__init__(container, **kwargs)
|
super().__init__(container, **kwargs)
|
||||||
|
|
||||||
self.tunnel = Tunnel()
|
|
||||||
self.lb_tunnel = None
|
self.lb_tunnel = None
|
||||||
self.btn_stst = None
|
self.btn_stst = None
|
||||||
self.endpoint = None
|
self.endpoint = None
|
||||||
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.wg_vpn_start = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_start"])
|
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.wg_vpn_stop = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_stop"])
|
||||||
self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"])
|
self.imp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_import"])
|
||||||
self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"])
|
self.tr_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_trash"])
|
||||||
self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"])
|
self.exp_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_export"])
|
||||||
self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"])
|
self.warning_pic = tk.PhotoImage(file=AppConfig.IMAGE_PATHS["icon_error"])
|
||||||
self.tips_enabled = tips_enabled if tips_enabled is not None else LxTools.if_tip(AppConfig.SETTINGS_FILE)
|
|
||||||
|
# StringVar-Variables initialization
|
||||||
|
self.tooltip_state = tk.BooleanVar()
|
||||||
|
# Get value from configuration
|
||||||
|
state = ConfigManager.get("tooltips")
|
||||||
|
# NOTE: ConfigManager.get("tooltips") can return either a boolean value or a string,
|
||||||
|
# depending on whether the value was loaded from the file (bool) or the default value is used (string).
|
||||||
|
# The expression 'lines[5].strip() == "True"' in ConfigManager.load() converts the string to a boolean.
|
||||||
|
# Convert to boolean and set
|
||||||
|
if isinstance(state, bool):
|
||||||
|
# If it's already a boolean, use directly
|
||||||
|
self.tooltip_state.set(state)
|
||||||
|
else:
|
||||||
|
# If it's a string or something else
|
||||||
|
self.tooltip_state.set(str(state) == "True")
|
||||||
|
|
||||||
|
self.tooltip_label = tk.StringVar() # StringVar-Variable for tooltip label for view Disabled/Enabled
|
||||||
|
self.tooltip_update_label()
|
||||||
|
self.update_label = tk.StringVar() # StringVar-Variable for update label
|
||||||
|
self.update_tooltip = tk.StringVar() # StringVar-Variable for update tooltip please not remove!
|
||||||
|
self.update_foreground = tk.StringVar(value="red")
|
||||||
|
|
||||||
# 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.configure(relief="flat")
|
||||||
self.menu_frame.grid(column=0, row=0, columnspan=4, sticky="w")
|
self.menu_frame.grid(column=0, row=0, columnspan=4, sticky="w")
|
||||||
|
|
||||||
# App Menu
|
# App Menu
|
||||||
self.version_lb = ttk.Label(self.menu_frame, text=VERSION)
|
self.version_lb = ttk.Label(self.menu_frame, text=AppConfig.VERSION)
|
||||||
self.version_lb.config(font=("Ubuntu", 11), foreground="#00c4ff")
|
self.version_lb.config(font=("Ubuntu", 11), foreground="#00c4ff")
|
||||||
self.version_lb.grid(column=0, row=0, rowspan=4, padx=10)
|
self.version_lb.grid(column=0, row=0, rowspan=4, padx=10)
|
||||||
|
|
||||||
Tooltip(self.version_lb, f"Version: {VERSION[2:]}", self.tips_enabled)
|
Tooltip(self.version_lb, f"Version: {AppConfig.VERSION[2:]}", self.tooltip_state)
|
||||||
|
|
||||||
self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options"))
|
self.options_btn = ttk.Menubutton(self.menu_frame, text=_("Options"))
|
||||||
self.options_btn.grid(column=1, columnspan=1, row=0)
|
self.options_btn.grid(column=1, columnspan=1, row=0)
|
||||||
|
|
||||||
Tooltip(self.options_btn, _("Click for Settings"), self.tips_enabled)
|
Tooltip(self.options_btn, _("Click for Settings"), self.tooltip_state)
|
||||||
|
|
||||||
set_update = tk.IntVar()
|
self.set_update = tk.IntVar()
|
||||||
set_tip = tk.BooleanVar()
|
|
||||||
self.settings = tk.Menu(self, relief="flat")
|
self.settings = tk.Menu(self, relief="flat")
|
||||||
self.options_btn.configure(menu=self.settings, style="Toolbutton")
|
self.options_btn.configure(menu=self.settings, style="Toolbutton")
|
||||||
self.settings.add_checkbutton(label=_("Disable Updates"),
|
self.settings.add_checkbutton(label=_("Disable Updates"),
|
||||||
command=lambda: self.update_setting(set_update.get()), variable=set_update)
|
command=lambda: self.update_setting(self.set_update.get()), variable=self.set_update)
|
||||||
self.settings.add_checkbutton(label=_("Disable Tooltips"),
|
|
||||||
command=lambda: self.tooltip(set_tip.get()), variable=set_tip)
|
self.updates_lb = ttk.Label(self.menu_frame)
|
||||||
self.settings.add_command(label=_("Light"), command=self.theme_change_light)
|
res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, ConfigManager.get("updates"))
|
||||||
self.settings.add_command(label=_("Dark"), command=self.theme_change_dark)
|
self.update_ui_for_update(res)
|
||||||
|
|
||||||
|
# Tooltip Menu
|
||||||
|
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
|
# About BTN Menu / Label
|
||||||
self.about_btn = ttk.Button(
|
self.about_btn = ttk.Button(
|
||||||
@ -116,49 +135,6 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.about_btn.grid(column=2, columnspan=2, row=0)
|
self.about_btn.grid(column=2, columnspan=2, row=0)
|
||||||
self.readme = tk.Menu(self)
|
self.readme = tk.Menu(self)
|
||||||
|
|
||||||
# Update and Tooltip Label
|
|
||||||
self.updates_lb = ttk.Label(self.menu_frame)
|
|
||||||
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
|
||||||
|
|
||||||
# View Checkbox to enable or disable Tooltip
|
|
||||||
if tips:
|
|
||||||
set_tip.set(value=False)
|
|
||||||
else:
|
|
||||||
set_tip.set(value=True)
|
|
||||||
|
|
||||||
# View Checkbox for enable or disable Updates
|
|
||||||
if res == "False":
|
|
||||||
set_update.set(value=1)
|
|
||||||
self.updates_lb.configure(text=_("Update search off"))
|
|
||||||
|
|
||||||
Tooltip(self.updates_lb, _("Updates you have disabled"), self.tips_enabled)
|
|
||||||
|
|
||||||
elif res == "No Internet Connection!":
|
|
||||||
self.updates_lb.configure(text=_("No Server Connection!"), foreground="red")
|
|
||||||
elif res == "No Updates":
|
|
||||||
self.updates_lb.configure(text=_("No Updates"))
|
|
||||||
|
|
||||||
Tooltip(self.updates_lb, _("Congratulations! Wire-Py is up to date"), self.tips_enabled)
|
|
||||||
|
|
||||||
else:
|
|
||||||
set_update.set(value=0)
|
|
||||||
text = f"Update {res} {_("available!")}"
|
|
||||||
|
|
||||||
# Update BTN Menu
|
|
||||||
self.update_btn = ttk.Menubutton(self.menu_frame, text=text)
|
|
||||||
self.update_btn.grid(column=4, columnspan=3, row=0, padx=0)
|
|
||||||
|
|
||||||
Tooltip(self.update_btn, _("Click to download new version"), self.tips_enabled)
|
|
||||||
|
|
||||||
self.download = tk.Menu(self, relief="flat")
|
|
||||||
|
|
||||||
self.update_btn.configure(menu=self.download, style="Toolbutton")
|
|
||||||
self.download.add_command(
|
|
||||||
label=_("Download"),
|
|
||||||
command=lambda: GiteaUpdate.download(f"https://git.ilunix.de/punix/Wire-Py/archive/{res}.zip",
|
|
||||||
res, AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"]))
|
|
||||||
|
|
||||||
# Show active Tunnel
|
|
||||||
self.a = Tunnel.active()
|
self.a = Tunnel.active()
|
||||||
|
|
||||||
# Label Frame 1
|
# Label Frame 1
|
||||||
@ -240,7 +216,7 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.btn_i = ttk.Button(self.lb_frame_btn_lbox, image=self.imp_pic, command=self.import_sl, padding=0)
|
self.btn_i = ttk.Button(self.lb_frame_btn_lbox, image=self.imp_pic, command=self.import_sl, padding=0)
|
||||||
self.btn_i.grid(column=0, row=1, padx=15, pady=8)
|
self.btn_i.grid(column=0, row=1, padx=15, pady=8)
|
||||||
|
|
||||||
Tooltip(self.btn_i, _("Click to import a Wireguard Tunnel"), self.tips_enabled)
|
Tooltip(self.btn_i, _("Click to import a Wireguard Tunnel"), self.tooltip_state)
|
||||||
|
|
||||||
# Button Trash
|
# Button Trash
|
||||||
self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=self.delete, padding=0,
|
self.btn_tr = ttk.Button(self.lb_frame_btn_lbox, image=self.tr_pic, command=self.delete, padding=0,
|
||||||
@ -248,20 +224,23 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.btn_tr.grid(column=0, row=2, padx=15, pady=8)
|
self.btn_tr.grid(column=0, row=2, padx=15, pady=8)
|
||||||
|
|
||||||
if self.l_box.size() == 0:
|
if self.l_box.size() == 0:
|
||||||
Tooltip(self.btn_tr, _("No tunnels to delete in the list"), self.tips_enabled)
|
Tooltip(self.btn_tr, _("No tunnels to delete in the list"), self.tooltip_state)
|
||||||
else:
|
else:
|
||||||
Tooltip(self.btn_tr, _("Click to delete a Wireguard Tunnel\nSelect from the list!"), self.tips_enabled)
|
Tooltip(self.btn_tr, _("Click to delete a Wireguard Tunnel\nSelect from the list!"), self.tooltip_state)
|
||||||
|
|
||||||
# Button Export
|
# Button Export
|
||||||
self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic,
|
self.btn_exp = ttk.Button(self.lb_frame_btn_lbox, image=self.exp_pic,
|
||||||
command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"], AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"], AppConfig.IMAGE_PATHS["icon_msg"],
|
command=lambda: Tunnel.export(AppConfig.IMAGE_PATHS["icon_info"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_error"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_msg"],
|
||||||
Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0)
|
Msg.STR["sel_tl"], Msg.STR["tl_first"]), padding=0)
|
||||||
|
|
||||||
self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
|
self.btn_exp.grid(column=0, row=3, padx=15, pady=8)
|
||||||
|
|
||||||
if self.l_box.size() == 0:
|
if self.l_box.size() == 0:
|
||||||
Tooltip(self.btn_exp, _("No Tunnels in List for Export"), self.tips_enabled)
|
Tooltip(self.btn_exp, _("No Tunnels in List for Export"), self.tooltip_state)
|
||||||
else:
|
else:
|
||||||
Tooltip(self.btn_exp, _("Click to export all\nWireguard Tunnel to Zipfile"), self.tips_enabled)
|
Tooltip(self.btn_exp, _("Click to export all\nWireguard Tunnel to Zipfile"), self.tooltip_state)
|
||||||
|
|
||||||
# Label Entry
|
# Label Entry
|
||||||
self.lb_rename = ttk.Entry(self.lb_frame4, width=20)
|
self.lb_rename = ttk.Entry(self.lb_frame4, width=20)
|
||||||
@ -270,9 +249,9 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.lb_rename.config(state="disable")
|
self.lb_rename.config(state="disable")
|
||||||
|
|
||||||
if self.l_box.size() != 0:
|
if self.l_box.size() != 0:
|
||||||
Tooltip(self.lb_rename, _("To rename a tunnel, you need to\nselect a tunnel from the list"), self.tips_enabled)
|
Tooltip(self.lb_rename, _("To rename a tunnel, you need to\nselect a tunnel from the list"), self.tooltip_state)
|
||||||
else:
|
else:
|
||||||
Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), self.tips_enabled)
|
Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), self.tooltip_state)
|
||||||
|
|
||||||
# Button Rename
|
# Button Rename
|
||||||
self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=self.tl_rename,
|
self.btn_rename = ttk.Button(self.lb_frame4, text=_("Rename"), state="disable", command=self.tl_rename,
|
||||||
@ -293,51 +272,108 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw")
|
self.wg_autostart.grid(column=0, row=0, pady=15, padx=15, sticky="nw")
|
||||||
|
|
||||||
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
|
if self.l_box.size() >= 1 and len(self.l_box.curselection()) >= 1:
|
||||||
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], tself.tips_enabled)
|
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state)
|
||||||
|
|
||||||
if self.l_box.size() == 0:
|
if self.l_box.size() == 0:
|
||||||
Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"], self.tips_enabled)
|
Tooltip(self.wg_autostart, Msg.TTIP["autostart_info"], self.tooltip_state)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tips_enabled)
|
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state)
|
||||||
|
|
||||||
self.on_off()
|
self.on_off()
|
||||||
|
|
||||||
|
# Update the labels based on the result
|
||||||
|
def update_ui_for_update(self, res):
|
||||||
|
"""Update UI elements based on update check result"""
|
||||||
|
if res == "False":
|
||||||
|
self.set_update.set(value=1)
|
||||||
|
self.update_label.set(_("Update search off"))
|
||||||
|
self.update_tooltip.set(_("Updates you have disabled"))
|
||||||
|
self.update_foreground.set("red")
|
||||||
|
|
||||||
@staticmethod
|
# Remove update button if it exists
|
||||||
def update_setting(update_res) -> None:
|
if hasattr(self, 'update_btn'):
|
||||||
"""
|
self.update_btn.grid_forget()
|
||||||
write off or on in file
|
|
||||||
Args:
|
# Display the label
|
||||||
update_res (int): argument that is passed contains 0 or 1
|
self.updates_lb.configure(
|
||||||
"""
|
textvariable=self.update_label,
|
||||||
if update_res == 1:
|
foreground=self.update_foreground.get()
|
||||||
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
|
)
|
||||||
lines[1] = 'off\n'
|
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||||
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
|
Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state)
|
||||||
|
|
||||||
|
elif res == "No Internet Connection!":
|
||||||
|
self.update_label.set(_("No Server Connection!"))
|
||||||
|
self.update_foreground.set("red")
|
||||||
|
|
||||||
|
# Remove update button if it exists
|
||||||
|
if hasattr(self, 'update_btn'):
|
||||||
|
self.update_btn.grid_forget()
|
||||||
|
|
||||||
|
# Display the label
|
||||||
|
self.updates_lb.configure(
|
||||||
|
textvariable=self.update_label,
|
||||||
|
foreground=self.update_foreground.get()
|
||||||
|
)
|
||||||
|
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||||
|
|
||||||
|
elif res == "No Updates":
|
||||||
|
self.update_label.set(_("No Updates"))
|
||||||
|
self.update_tooltip.set(_("Congratulations! Wire-Py is up to date"))
|
||||||
|
self.update_foreground.set("black")
|
||||||
|
|
||||||
|
# Remove update button if it exists
|
||||||
|
if hasattr(self, 'update_btn'):
|
||||||
|
self.update_btn.grid_forget()
|
||||||
|
|
||||||
|
# Display the label
|
||||||
|
self.updates_lb.configure(
|
||||||
|
textvariable=self.update_label,
|
||||||
|
foreground=self.update_foreground.get()
|
||||||
|
)
|
||||||
|
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||||
|
Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
|
self.set_update.set(value=0)
|
||||||
lines[1] = 'on\n'
|
update_text = f"Update {res} {_('available!')}"
|
||||||
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
|
|
||||||
|
|
||||||
@staticmethod
|
# Remove the label if displayed
|
||||||
def tooltip(tip) -> None:
|
self.updates_lb.grid_forget()
|
||||||
|
|
||||||
|
# Create or update the update button
|
||||||
|
if not hasattr(self, 'update_btn'):
|
||||||
|
# Create the update button if it doesn't exist yet
|
||||||
|
self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text)
|
||||||
|
self.update_btn.grid(column=4, columnspan=3, row=0, padx=0)
|
||||||
|
Tooltip(self.update_btn, _("Click to download new version"), self.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,
|
||||||
|
AppConfig.IMAGE_PATHS["icon_info"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_vpn"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_error"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_msg"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def tooltip(self, tip) -> None:
|
||||||
"""
|
"""
|
||||||
write True or False in a file
|
Aktualisiert die Tooltip-Einstellung im ConfigManager
|
||||||
Args:
|
Args:
|
||||||
tip (bool): argument that is passed contains True or False
|
tip (bool): True zum Deaktivieren, False zum Aktivieren von Tooltips
|
||||||
"""
|
"""
|
||||||
if tip:
|
# Beachten Sie die umgekehrte Logik: tip=True bedeutet Tooltips deaktivieren
|
||||||
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
|
ConfigManager.set("tooltip", not tip)
|
||||||
lines[5] = 'False\n'
|
# Aktualisieren Sie die lokale Variable für sofortige Wirkung
|
||||||
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
|
self.tooltip_state = not tip
|
||||||
|
|
||||||
else:
|
|
||||||
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
|
|
||||||
lines[5] = 'True\n'
|
|
||||||
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def about() -> None:
|
def about() -> None:
|
||||||
@ -354,27 +390,185 @@ class FrameWidgets(ttk.Frame):
|
|||||||
|
|
||||||
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn)
|
LxTools.msg_window(AppConfig.IMAGE_PATHS["icon_vpn"], AppConfig.IMAGE_PATHS["icon_vpn"], _("Info"), msg_t, _("Go to Wire-Py git"), link_btn)
|
||||||
|
|
||||||
def theme_change_light(self) -> None:
|
def update_ui_for_update_status(self, res):
|
||||||
"""
|
"""Update UI elements based on update check result"""
|
||||||
Set a light theme
|
print(f"Updating UI for result: {res}") # Debug output
|
||||||
"""
|
|
||||||
if self.tk.call("ttk::style", "theme", "use") == "water-dark":
|
|
||||||
self.tk.call("set_theme", "light")
|
|
||||||
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True) # (keepends=True) = not changed
|
|
||||||
lines[3] = 'light\n'
|
|
||||||
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
|
|
||||||
self.color_label()
|
|
||||||
|
|
||||||
def theme_change_dark(self) -> None:
|
# First, clean up any existing UI elements
|
||||||
|
if hasattr(self, 'update_btn') and self.update_btn.winfo_exists():
|
||||||
|
self.update_btn.grid_forget()
|
||||||
|
|
||||||
|
# Reset all variables to ensure fresh state
|
||||||
|
self.update_label.set("")
|
||||||
|
self.update_tooltip.set("")
|
||||||
|
self.update_foreground.set("black")
|
||||||
|
|
||||||
|
if res == "False":
|
||||||
|
self.set_update.set(value=1)
|
||||||
|
self.update_label.set(_("Update search off"))
|
||||||
|
self.update_tooltip.set(_("Updates you have disabled"))
|
||||||
|
self.update_foreground.set("red")
|
||||||
|
|
||||||
|
# Display the label
|
||||||
|
self.updates_lb.configure(
|
||||||
|
textvariable=self.update_label,
|
||||||
|
foreground=self.update_foreground.get()
|
||||||
|
)
|
||||||
|
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||||
|
Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state)
|
||||||
|
|
||||||
|
elif res == "No Internet Connection!":
|
||||||
|
self.update_label.set(_("No Server Connection!"))
|
||||||
|
self.update_foreground.set("red")
|
||||||
|
|
||||||
|
# Display the label
|
||||||
|
self.updates_lb.configure(
|
||||||
|
textvariable=self.update_label,
|
||||||
|
foreground=self.update_foreground.get()
|
||||||
|
)
|
||||||
|
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||||
|
|
||||||
|
elif res == "No Updates":
|
||||||
|
self.update_label.set(_("No Updates"))
|
||||||
|
self.update_tooltip.set(_("Congratulations! Wire-Py is up to date"))
|
||||||
|
self.update_foreground.set("black")
|
||||||
|
|
||||||
|
# Display the label
|
||||||
|
self.updates_lb.configure(
|
||||||
|
textvariable=self.update_label,
|
||||||
|
foreground=self.update_foreground.get()
|
||||||
|
)
|
||||||
|
self.updates_lb.grid(column=4, columnspan=3, row=0, padx=10)
|
||||||
|
Tooltip(self.updates_lb, self.update_tooltip.get(), self.tooltip_state)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# We have an update available
|
||||||
|
self.set_update.set(value=0)
|
||||||
|
update_text = f"Update {res} {_('available!')}"
|
||||||
|
|
||||||
|
# Hide the label if it's visible
|
||||||
|
if self.updates_lb.winfo_ismapped():
|
||||||
|
self.updates_lb.grid_forget()
|
||||||
|
|
||||||
|
# Create or update the update button
|
||||||
|
if not hasattr(self, 'update_btn') or not self.update_btn.winfo_exists():
|
||||||
|
# Create the update button if it doesn't exist yet
|
||||||
|
self.update_btn = ttk.Menubutton(self.menu_frame, text=update_text)
|
||||||
|
self.update_btn.grid(column=4, columnspan=3, row=0, padx=0)
|
||||||
|
Tooltip(self.update_btn, _("Click to download new version"), self.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,
|
||||||
|
AppConfig.IMAGE_PATHS["icon_info"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_vpn"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_error"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_msg"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Update the existing update button
|
||||||
|
self.update_btn.configure(text=update_text)
|
||||||
|
# Make sure it's visible
|
||||||
|
self.update_btn.grid(column=4, columnspan=3, row=0, padx=0)
|
||||||
|
|
||||||
|
# Update the download command
|
||||||
|
if hasattr(self, 'download'):
|
||||||
|
self.download.entryconfigure(
|
||||||
|
0, # First entry in the menu
|
||||||
|
command=lambda: GiteaUpdate.download(
|
||||||
|
f"{AppConfig.DOWNLOAD_URL}/{res}.zip",
|
||||||
|
res,
|
||||||
|
AppConfig.IMAGE_PATHS["icon_info"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_vpn"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_error"],
|
||||||
|
AppConfig.IMAGE_PATHS["icon_msg"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_setting(self, update_res) -> None:
|
||||||
|
"""write off or on in file
|
||||||
|
Args:
|
||||||
|
update_res (int): argument that is passed contains 0 or 1
|
||||||
"""
|
"""
|
||||||
Set a dark theme
|
if update_res == 1:
|
||||||
"""
|
# Disable updates
|
||||||
if not self.tk.call("ttk::style", "theme", "use") == "water-dark":
|
ConfigManager.set("updates", "off")
|
||||||
self.tk.call("set_theme", "dark")
|
# When updates are disabled, we know the result should be "False"
|
||||||
lines = Path(AppConfig.SETTINGS_FILE).read_text(encoding="utf-8").splitlines(keepends=True)
|
self.update_ui_for_update_status("False")
|
||||||
lines[3] = 'dark\n'
|
else:
|
||||||
Path(AppConfig.SETTINGS_FILE).write_text(''.join(lines), encoding="utf-8")
|
# Enable updates
|
||||||
self.color_label()
|
ConfigManager.set("updates", "on")
|
||||||
|
# When enabling updates, we need to actually check for updates
|
||||||
|
try:
|
||||||
|
# Force a fresh check by passing "on" as the update setting
|
||||||
|
res = GiteaUpdate.api_down(AppConfig.UPDATE_URL, AppConfig.VERSION, "on")
|
||||||
|
print(f"API returned: {res}") # Debug output
|
||||||
|
|
||||||
|
# Make sure UI is updated regardless of previous state
|
||||||
|
if hasattr(self, 'update_btn'):
|
||||||
|
self.update_btn.grid_forget()
|
||||||
|
if hasattr(self, 'updates_lb'):
|
||||||
|
self.updates_lb.grid_forget()
|
||||||
|
|
||||||
|
# Now update the UI with the fresh result
|
||||||
|
self.update_ui_for_update_status(res)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error checking for updates: {e}")
|
||||||
|
# Fallback to a default message if there's an error
|
||||||
|
self.update_ui_for_update_status("No Internet Connection!")
|
||||||
|
|
||||||
|
def tooltip_update_label(self) -> None:
|
||||||
|
"""Updates the tooltip menu label based on the current tooltip status"""
|
||||||
|
# Set the menu text based on the current status
|
||||||
|
if self.tooltip_state.get():
|
||||||
|
# If tooltips are enabled, the menu option should be to disable them
|
||||||
|
self.tooltip_label.set(_("Disable Tooltips"))
|
||||||
|
else:
|
||||||
|
# If tooltips are disabled, the menu option should be to enable them
|
||||||
|
self.tooltip_label.set(_("Enable Tooltips"))
|
||||||
|
|
||||||
|
|
||||||
|
def tooltips_toggle(self):
|
||||||
|
"""Toggles tooltips on/off and updates the menu label"""
|
||||||
|
# Toggle the boolean state
|
||||||
|
new_bool_state = not self.tooltip_state.get()
|
||||||
|
|
||||||
|
# Save the converted value in the configuration
|
||||||
|
ConfigManager.set("tooltips", str(new_bool_state))
|
||||||
|
print(f"Tooltips are now: {new_bool_state} in ConfigManager")
|
||||||
|
# Update the tooltip_state variable for immediate effect
|
||||||
|
self.tooltip_state.set(new_bool_state)
|
||||||
|
|
||||||
|
# Update the menu label
|
||||||
|
self.tooltip_update_label()
|
||||||
|
|
||||||
|
# Update the menu entry - find the correct index
|
||||||
|
# This assumes it's the third item (index 2) in your menu
|
||||||
|
self.settings.entryconfigure(1, label=self.tooltip_label.get())
|
||||||
|
|
||||||
|
|
||||||
|
def update_theme_label(self) -> str:
|
||||||
|
"""Update the theme label based on current theme"""
|
||||||
|
current_theme = ConfigManager.get("theme")
|
||||||
|
if current_theme == "light":
|
||||||
|
self.theme_label.set(_("Dark"))
|
||||||
|
else:
|
||||||
|
self.theme_label.set(_("Light"))
|
||||||
|
|
||||||
|
def on_theme_toggle(self) -> None:
|
||||||
|
"""Toggle between light and dark theme"""
|
||||||
|
current_theme = ConfigManager.get("theme")
|
||||||
|
new_theme = "dark" if current_theme == "light" else "light"
|
||||||
|
ThemeManager.change_theme(self, new_theme, new_theme)
|
||||||
|
self.color_label()
|
||||||
|
self.update_theme_label() # Update the theme label
|
||||||
|
# Update Menulfield
|
||||||
|
self.settings.entryconfigure(2, label=self.theme_label.get())
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -386,9 +580,9 @@ class FrameWidgets(ttk.Frame):
|
|||||||
|
|
||||||
tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
|
tl = LxTools.get_file_name(AppConfig.TEMP_DIR)
|
||||||
if len(self.tl) == 0:
|
if len(self.tl) == 0:
|
||||||
Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tips_enabled)
|
Tooltip(self.btn_stst, Msg.TTIP["empty_list"], self.tooltip_state)
|
||||||
else:
|
else:
|
||||||
Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tips_enabled)
|
Tooltip(self.btn_stst, Msg.TTIP["start_tl"], self.tooltip_state)
|
||||||
|
|
||||||
def handle_tunnel_data(self, tunnel_name: str) -> tuple[str, str, str, str | None]:
|
def handle_tunnel_data(self, tunnel_name: str) -> tuple[str, str, str, str | None]:
|
||||||
"""_summary_
|
"""_summary_
|
||||||
@ -410,8 +604,8 @@ class FrameWidgets(ttk.Frame):
|
|||||||
"""
|
"""
|
||||||
View activ Tunnel in the color green or yellow
|
View activ Tunnel in the color green or yellow
|
||||||
"""
|
"""
|
||||||
lines = AppConfig.SETTINGS_FILE.read_text()
|
if ConfigManager.get("theme") == "light":
|
||||||
if "light\n" in lines:
|
|
||||||
self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green")
|
self.lb_tunnel = ttk.Label(self, textvariable=self.str_var, foreground="green")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -428,7 +622,7 @@ class FrameWidgets(ttk.Frame):
|
|||||||
command=lambda: self.wg_switch("stop"), padding=0)
|
command=lambda: self.wg_switch("stop"), padding=0)
|
||||||
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
|
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
|
||||||
|
|
||||||
Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tips_enabled)
|
Tooltip(self.btn_stst, Msg.TTIP["stop_tl"], self.tooltip_state)
|
||||||
|
|
||||||
def reset_fields(self) -> None:
|
def reset_fields(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -504,10 +698,10 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.l_box.update()
|
self.l_box.update()
|
||||||
self.l_box.selection_set(0)
|
self.l_box.selection_set(0)
|
||||||
|
|
||||||
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tips_enabled)
|
Tooltip(self.wg_autostart, Msg.TTIP["autostart"], self.tooltip_state)
|
||||||
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tips_enabled)
|
Tooltip(self.btn_tr, Msg.TTIP["trash_tl"], self.tooltip_state)
|
||||||
Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tips_enabled)
|
Tooltip(self.btn_exp, Msg.TTIP["export_tl"], self.tooltip_state)
|
||||||
Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], self.tips_enabled)
|
Tooltip(self.btn_rename, Msg.TTIP["rename_tl"], self.tooltip_state)
|
||||||
|
|
||||||
self.lb_rename.insert(0, "Max. 12 characters!")
|
self.lb_rename.insert(0, "Max. 12 characters!")
|
||||||
self.str_var = tk.StringVar()
|
self.str_var = tk.StringVar()
|
||||||
@ -570,11 +764,11 @@ class FrameWidgets(ttk.Frame):
|
|||||||
self.wg_autostart.configure(state="disabled")
|
self.wg_autostart.configure(state="disabled")
|
||||||
self.lb_rename.configure(state="disabled")
|
self.lb_rename.configure(state="disabled")
|
||||||
Tooltip(self.wg_autostart, _("You must have at least one\ntunnel in the list,to use the autostart")
|
Tooltip(self.wg_autostart, _("You must have at least one\ntunnel in the list,to use the autostart")
|
||||||
, self.tips_enabled)
|
, self.tooltip_state)
|
||||||
|
|
||||||
Tooltip(self.btn_exp, _("No Tunnels in List for Export"), self.tips_enabled)
|
Tooltip(self.btn_exp, _("No Tunnels in List for Export"), self.tooltip_state)
|
||||||
Tooltip(self.btn_stst, _("No tunnels to start in the list"), self.tips_enabled)
|
Tooltip(self.btn_stst, _("No tunnels to start in the list"), self.tooltip_state)
|
||||||
Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), tips, )
|
Tooltip(self.lb_rename, _("To rename a tunnel, at least one must be in the list"), self.tooltip_state)
|
||||||
self.lb_rename.insert(0, _("Max. 12 characters!"))
|
self.lb_rename.insert(0, _("Max. 12 characters!"))
|
||||||
|
|
||||||
if self.a != "" and self.a == select_tl:
|
if self.a != "" and self.a == select_tl:
|
||||||
@ -727,54 +921,6 @@ class FrameWidgets(ttk.Frame):
|
|||||||
except EOFError as e:
|
except EOFError as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def activate_tunnel(self, tunnel_name):
|
|
||||||
"""Activates a tunnel after a delay"""
|
|
||||||
try:
|
|
||||||
# First check if the tunnel exists in NetworkManager
|
|
||||||
nm_connections = subprocess.run(
|
|
||||||
["nmcli", "-t", "-f", "NAME", "connection", "show"],
|
|
||||||
check=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
text=True
|
|
||||||
).stdout.strip().split('\n')
|
|
||||||
|
|
||||||
# Find the actual connection name (it might have been modified)
|
|
||||||
actual_name = None
|
|
||||||
for conn in nm_connections:
|
|
||||||
if tunnel_name in conn:
|
|
||||||
actual_name = conn
|
|
||||||
break
|
|
||||||
|
|
||||||
if actual_name:
|
|
||||||
# Use the actual connection name
|
|
||||||
subprocess.run(["nmcli", "connection", "up", actual_name],
|
|
||||||
check=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
else:
|
|
||||||
# Use the original name as fallback
|
|
||||||
subprocess.run(["nmcli", "connection", "up", tunnel_name],
|
|
||||||
check=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
|
|
||||||
# After successful activation, update the display
|
|
||||||
self.a = Tunnel.active()
|
|
||||||
self.str_var.set(self.a)
|
|
||||||
self.color_label()
|
|
||||||
|
|
||||||
# Try to load the tunnel data
|
|
||||||
try:
|
|
||||||
data = self.handle_tunnel_data(self.a)
|
|
||||||
self.init_and_report(data)
|
|
||||||
self.show_data()
|
|
||||||
self.stop()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error loading tunnel data: {e}")
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print(f"Error activating tunnel: {e}", "hier simma")
|
|
||||||
|
|
||||||
def init_and_report(self, data=None) -> None:
|
def init_and_report(self, data=None) -> None:
|
||||||
"""
|
"""
|
||||||
Displays the value address, DNS and peer in the labels
|
Displays the value address, DNS and peer in the labels
|
||||||
@ -881,7 +1027,6 @@ class FrameWidgets(ttk.Frame):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
_ = AppConfig.setup_translations()
|
_ = AppConfig.setup_translations()
|
||||||
tips = LxTools.if_tip(AppConfig.SETTINGS_FILE)
|
|
||||||
LxTools.sigi(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
|
LxTools.sigi(AppConfig.TEMP_DIR, AppConfig.USER_FILE)
|
||||||
window = Wirepy()
|
window = Wirepy()
|
||||||
"""
|
"""
|
||||||
|
@ -24,13 +24,12 @@ class AppConfig:
|
|||||||
KEYS_FILE: Path = CONFIG_DIR / "keys"
|
KEYS_FILE: Path = CONFIG_DIR / "keys"
|
||||||
AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service"
|
AUTOSTART_SERVICE: Path = Path.home() / ".config/systemd/user/wg_start.service"
|
||||||
|
|
||||||
# Default settings
|
# Updates
|
||||||
DEFAULT_SETTINGS: Dict[str, Any] = {
|
# 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year
|
||||||
"updates": "on",
|
VERSION: str = "v. 2.04.1725"
|
||||||
"theme": "light",
|
UPDATE_URL: str = "https://git.ilunix.de/api/v1/repos/punix/Wire-Py/releases"
|
||||||
"tooltip": True,
|
DOWNLOAD_URL: str = "https://git.ilunix.de/punix/Wire-Py/archive"
|
||||||
"autostart": "off"
|
|
||||||
}
|
|
||||||
|
|
||||||
# UI configuration
|
# UI configuration
|
||||||
UI_CONFIG: Dict[str, Any] = {
|
UI_CONFIG: Dict[str, Any] = {
|
||||||
|
Reference in New Issue
Block a user