1.10.2124 #21

Merged
punix merged 3 commits from 1.10.2124 into main 2024-10-22 12:41:47 +02:00
5 changed files with 205 additions and 165 deletions

View File

@ -4,9 +4,13 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Add Options, Help, Update Label and Update Menubutton&#10; - Theme now separate Light and Dark&#10; - Add Own Tooltip (Class and def's) Part One"> <list default="true" id="940e1630-c825-4d4c-be80-bc11f543c122" name="Changes" comment=" - Fix a ConToDict Class when Endpoint not in Wireguard config file">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Changelog" beforeDir="false" afterPath="$PROJECT_DIR$/Changelog" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/testtheme.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/wg_func.py" beforeDir="false" afterPath="$PROJECT_DIR$/wg_func.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/wg_func.py" beforeDir="false" afterPath="$PROJECT_DIR$/wg_func.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wg_main.py" beforeDir="false" afterPath="$PROJECT_DIR$/wg_main.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -29,6 +33,10 @@
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="UPDATE_TYPE" value="REBASE" /> <option name="UPDATE_TYPE" value="REBASE" />
</component> </component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/wg_func.py" root0="SKIP_INSPECTION" />
<setting file="file://$PROJECT_DIR$/wg_main.py" root0="FORCE_HIGHLIGHTING" />
</component>
<component name="ProjectColorInfo">{ <component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 3 &quot;associatedIndex&quot;: 3
}</component> }</component>
@ -596,4 +604,20 @@
<MESSAGE value=" - Add Options, Help, Update Label and Update Menubutton &#10; - Theme now separate Light and Dark" /> <MESSAGE value=" - Add Options, Help, Update Label and Update Menubutton &#10; - Theme now separate Light and Dark" />
<option name="LAST_COMMIT_MESSAGE" value=" - Add Options, Help, Update Label and Update Menubutton &#10; - Theme now separate Light and Dark" /> <option name="LAST_COMMIT_MESSAGE" value=" - Add Options, Help, Update Label and Update Menubutton &#10; - Theme now separate Light and Dark" />
</component> </component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/wg_func.py</url>
<line>341</line>
<option name="timeStamp" value="8" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/wg_main.py</url>
<line>273</line>
<option name="timeStamp" value="11" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project> </project>

View File

@ -11,6 +11,13 @@ My standard System: Linux Mint 22 Cinnamon
- for loops with lists replaced by List Comprehensions - for loops with lists replaced by List Comprehensions
- Keeping Classes Together - Keeping Classes Together
### Added
21-10-2024
- Optimize Class. Move to wg_main Import Start/StopBTN and Tooltip
### Added ### Added
19-10-2024 19-10-2024

View File

@ -1,2 +1,25 @@
# Wire-Py # Wire-Py
Wire-Py is an easy-to-use Gui for nmcli.
Before the first use of Wire-Py, all previous tunnels with "nmcli connection delete example" should be removed.
Wire-Py can easily be imported, exported, started, popped as well as renamed and deleted.
Wire-Py runs on many distros.
Tested on Arch Linux, OpenSuse Tumbleweed,
Fedora, Debian12, Linux Mint 22 Cinnamon
With the desktops:
Xfce4, Cinnamon, Kde, and Mate, LXDE, LXQT
# Screenshots
[![wire-py.png](https://fb.ilunix.de/api/public/dl/0cx7pPYK?inline=true)](https://fb.ilunix.de/share/0cx7pPYK)
# Instruction
[![Import.png](https://fb.ilunix.de/api/public/dl/VpYEn3Gz?inline=true)](https://fb.ilunix.de/share/VpYEn3Gz)
[![Stop.png](https://fb.ilunix.de/api/public/dl/zBnbIiyD?inline=true)](https://fb.ilunix.de/share/zBnbIiyD)
[![export.png](https://fb.ilunix.de/api/public/dl/gMc4-NAj?inline=true)](https://fb.ilunix.de/share/gMc4-NAj)
[![rename.png](https://fb.ilunix.de/api/public/dl/v0y_WiqV?inline=true)](https://fb.ilunix.de/share/v0y_WiqV)

View File

@ -8,12 +8,12 @@ import zipfile
from datetime import datetime from datetime import datetime
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 ttk
import requests import requests
''' 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year ''' ''' 1 = 1. Year, 09 = Month of the Year, 2924 = Day and Year of the Year '''
version = 'v. 1.10.2024' version = 'v. 1.10.2124'
path_to_file = Path('/etc/wire_py/wg_py') path_to_file = Path('/etc/wire_py/wg_py')
path_to_file2 = Path('/etc/wire_py/settings') path_to_file2 = Path('/etc/wire_py/settings')
@ -135,25 +135,6 @@ class GreenLabel:
def rowconfigure(self, param, weight): def rowconfigure(self, param, weight):
pass pass
class StartStopBTN:
"""
Show Start and Stop Button in Label
"""
def __init__(self):
self.lb_frame_btn_lbox = None
self.wg_switch = None
self.btn_stst = None
self.wg_vpn_start = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_vpn-start.png')
self.wg_vpn_stop = tk.PhotoImage(file=r'/usr/share/icons/wp-icons/48/wg_vpn-stop.png')
def button_stop(self):
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_stop, command=self.wg_switch, padding=0)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
def button_start(self):
self.btn_stst = ttk.Button(self.lb_frame_btn_lbox, image=self.wg_vpn_start, command=self.wg_switch, padding=0)
self.btn_stst.grid(column=0, row=0, padx=5, pady=8)
class ConToDict: class ConToDict:
""" """
@ -281,117 +262,6 @@ class ListTunnels:
return tl return tl
class ImportTunnel:
"""
Import Class for Wireguard config Files.
Before importing, it is checked whether PrivateKey and PublicKey are in the file.
If True then it is checked whether the PreSharedKey is already in the key file
to avoid an import error so that no double wgconf are imported.
Thus, tunnels can be renamed without the problems arise. If False then the key is written into the file.
Furthermore, it is checked whether the name is longer than 12 characters.
If True then the name is automatically shortened to 12 characters and then imported.
If in each case false comes out, a corresponding window comes to inform the user that something is wrong.
"""
def __init__(self):
self.select_tunnel = None
self.wg_switch = None
self.btn_stst = None
self.lb_tunnel = None
self.StrVar = None
self.a = None
self.l_box = None
def wg_import_select(self):
try:
filepath = filedialog.askopenfilename(initialdir=str(_u), title='Select Wireguard config File',
filetypes=[('WG config files', '*.conf')], )
with open(filepath, 'r') as file:
read = file.read()
path_split = filepath.split('/')
path_split1 = path_split[-1]
self.a = TunnelActiv.active()
if 'PrivateKey = ' in read and 'PublicKey = 'in read and 'Endpoint =' in read:
with open(filepath, 'r') as file:
key = ConToDict.covert_to_dict(file)
pre_key = key[3]
if len(pre_key) != 0:
with open('/etc/wire_py/.keys', 'r') as readfile:
p_key = readfile.readlines()
if pre_key in p_key or pre_key + '\n' in p_key:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/wp-icons/64/error.png'
ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
wt = 'Import Error'
msg_t = 'Tunnel already available!\nPlease use another file for import'
msg_window(iw, ii, wt, msg_t)
else:
with open('/etc/wire_py/.keys', 'a') as keyfile:
keyfile.write(pre_key + '\r')
if len(path_split1) > 17:
p1 = shutil.copy(filepath, Path('/etc/wire_py/'))
path_split = path_split1[len(path_split1) - 17:]
os.rename(p1, Path('/etc/wire_py') / str(path_split))
new_conf = '/etc/wire_py/' + path_split
if self.a != '':
check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
ShowAddress.label_empty(self)
subprocess.check_output(['nmcli', 'connection', 'import', 'type',
'wireguard', 'file', new_conf], text=True)
else:
shutil.copy(filepath, Path('/etc/wire_py/'))
if self.a != '':
check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
ShowAddress.label_empty(self)
subprocess.check_output(['nmcli', 'connection', 'import', 'type',
'wireguard', 'file', filepath], text=True)
self.StrVar.set('')
self.a = TunnelActiv.active()
self.l_box.insert(0, self.a)
self.l_box.update()
self.StrVar = tk.StringVar()
self.StrVar.set(self.a)
GreenLabel.green_show_label(self)
StartStopBTN.button_stop(self)
wg_read = Path('/etc/wire_py') / str(self.a + '.conf')
with open(wg_read, 'r') as file_for_key:
data = ConToDict.covert_to_dict(file_for_key)
''' Address Label '''
ShowAddress.init_and_report(self, data)
ShowAddress.show_data(self)
check_call(['nmcli', 'con', 'mod', self.a, 'connection.autoconnect', 'no'])
Path.chmod(wg_read, 0o600)
if 'PrivateKey = ' and 'Endpoint = ' not in read:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/wp-icons/64/error.png'
ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
wt = 'Import Error'
msg_t = 'Oh... no valid Wireguard File!\nPlease select a valid Wireguard File'
msg_window(iw, ii, wt, msg_t)
except EOFError:
pass
except TypeError:
pass
except FileNotFoundError:
pass
except subprocess.CalledProcessError:
print('Tunnel exist!')
class FileHandle: class FileHandle:
""" """
This class will display the autostart label which This class will display the autostart label which
@ -527,29 +397,4 @@ class ExportTunnels:
pass pass
class MyToolTip(tk.Toplevel):
TIP_X_OFFSET = 8
TIP_Y_OFFSET = 8
AUTO_CLEAR_TIME = 10 # Millisecond. (1/100 sec.)
def __init__(self, x_pos, y_pos, message=None, auto_clear=False):
self.x_pos = x_pos
self.y_pos = y_pos
self.message = message
self.auto_clear = auto_clear
tk.Toplevel.__init__(self)
self.overrideredirect(True)
self.message_label = ttk.Label(self, compound='left', text=self.message, padding=4)
self.message_label.pack()
self.geometry("+%d+%d" % (self.x_pos + self.TIP_X_OFFSET,
self.y_pos + self.TIP_X_OFFSET))
if self.auto_clear:
self.after(self.AUTO_CLEAR_TIME, self.clear_tip)
def clear_tip(self):
"""Remove Tool-Tip"""
self.destroy()

View File

@ -1,14 +1,16 @@
#!/usr/bin/python3 #!/usr/bin/python3
import os
import shutil
import subprocess
import tkinter as tk import tkinter as tk
from select import select from pathlib import Path
from subprocess import check_call from subprocess import check_call
from tkinter import * from tkinter import *
from pathlib import Path from tkinter import filedialog, ttk
from tkinter import ttk
from wg_func import (TunnelActiv, ListTunnels, ImportTunnel, ConToDict, GreenLabel, ShowAddress, FileHandle, from wg_func import (TunnelActiv, ListTunnels, ConToDict, GreenLabel, ShowAddress, FileHandle,
ExportTunnels, OnOff, msg_window, WirePyUpdate, res, version, path_to_file2, ExportTunnels, OnOff, msg_window, WirePyUpdate, res, _u, version, path_to_file2,
path_to_file3, MyToolTip) path_to_file3)
tcl_path = Path('/usr/share/TK-Themes') tcl_path = Path('/usr/share/TK-Themes')
@ -269,6 +271,117 @@ class StartStopBTN:
self.btn_stst.bind('<Leave>', start_mouse_leave) self.btn_stst.bind('<Leave>', start_mouse_leave)
class ImportTunnel:
"""
Import Class for Wireguard config Files.
Before importing, it is checked whether PrivateKey and PublicKey are in the file.
If True then it is checked whether the PreSharedKey is already in the key file
to avoid an import error so that no double wgconf are imported.
Thus, tunnels can be renamed without the problems arise. If False then the key is written into the file.
Furthermore, it is checked whether the name is longer than 12 characters.
If True then the name is automatically shortened to 12 characters and then imported.
If in each case false comes out, a corresponding window comes to inform the user that something is wrong.
"""
def __init__(self):
self.select_tunnel = None
self.wg_switch = None
self.btn_stst = None
self.lb_tunnel = None
self.StrVar = None
self.a = None
self.l_box = None
def wg_import_select(self):
try:
filepath = filedialog.askopenfilename(initialdir=str(_u), title='Select Wireguard config File',
filetypes=[('WG config files', '*.conf')], )
with open(filepath, 'r') as file:
read = file.read()
path_split = filepath.split('/')
path_split1 = path_split[-1]
self.a = TunnelActiv.active()
if 'PrivateKey = ' in read and 'PublicKey = 'in read and 'Endpoint =' in read:
with open(filepath, 'r') as file:
key = ConToDict.covert_to_dict(file)
pre_key = key[3]
if len(pre_key) != 0:
with open('/etc/wire_py/.keys', 'r') as readfile:
p_key = readfile.readlines()
if pre_key in p_key or pre_key + '\n' in p_key:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/wp-icons/64/error.png'
ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
wt = 'Import Error'
msg_t = 'Tunnel already available!\nPlease use another file for import'
msg_window(iw, ii, wt, msg_t)
else:
with open('/etc/wire_py/.keys', 'a') as keyfile:
keyfile.write(pre_key + '\r')
if len(path_split1) > 17:
p1 = shutil.copy(filepath, Path('/etc/wire_py/'))
path_split = path_split1[len(path_split1) - 17:]
os.rename(p1, Path('/etc/wire_py') / str(path_split))
new_conf = '/etc/wire_py/' + path_split
if self.a != '':
check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
ShowAddress.label_empty(self)
subprocess.check_output(['nmcli', 'connection', 'import', 'type',
'wireguard', 'file', new_conf], text=True)
else:
shutil.copy(filepath, Path('/etc/wire_py/'))
if self.a != '':
check_call(['nmcli', 'connection', 'down', TunnelActiv.active()])
ShowAddress.label_empty(self)
subprocess.check_output(['nmcli', 'connection', 'import', 'type',
'wireguard', 'file', filepath], text=True)
self.StrVar.set('')
self.a = TunnelActiv.active()
self.l_box.insert(0, self.a)
self.l_box.update()
self.StrVar = tk.StringVar()
self.StrVar.set(self.a)
GreenLabel.green_show_label(self)
StartStopBTN.button_stop(self)
wg_read = Path('/etc/wire_py') / str(self.a + '.conf')
with open(wg_read, 'r') as file_for_key:
data = ConToDict.covert_to_dict(file_for_key)
''' Address Label '''
ShowAddress.init_and_report(self, data)
ShowAddress.show_data(self)
check_call(['nmcli', 'con', 'mod', self.a, 'connection.autoconnect', 'no'])
Path.chmod(wg_read, 0o600)
if 'PrivateKey = ' and 'Endpoint = ' not in read:
"""img_w, img_i, w_title, w_txt hand over"""
iw = r'/usr/share/icons/wp-icons/64/error.png'
ii = r'/usr/share/icons/wp-icons/48/wg_msg.png'
wt = 'Import Error'
msg_t = 'Oh... no valid Wireguard File!\nPlease select a valid Wireguard File'
msg_window(iw, ii, wt, msg_t)
except EOFError:
pass
except TypeError:
pass
except FileNotFoundError:
pass
except subprocess.CalledProcessError:
print('Tunnel exist!')
class FrameWidgets(ttk.Frame): class FrameWidgets(ttk.Frame):
def __init__(self, container, **kwargs): def __init__(self, container, **kwargs):
super().__init__(container, **kwargs) super().__init__(container, **kwargs)
@ -751,6 +864,34 @@ class FrameWidgets(ttk.Frame):
msg_window(iw, ii, wt, msg_t) msg_window(iw, ii, wt, msg_t)
class MyToolTip(tk.Toplevel):
TIP_X_OFFSET = 8
TIP_Y_OFFSET = 8
AUTO_CLEAR_TIME = 10 # Millisecond. (1/100 sec.)
def __init__(self, x_pos, y_pos, message=None, auto_clear=False):
self.x_pos = x_pos
self.y_pos = y_pos
self.message = message
self.auto_clear = auto_clear
tk.Toplevel.__init__(self)
self.overrideredirect(True)
self.message_label = ttk.Label(self, compound='left', text=self.message, padding=4)
self.message_label.pack()
self.geometry("+%d+%d" % (self.x_pos + self.TIP_X_OFFSET,
self.y_pos + self.TIP_X_OFFSET))
if self.auto_clear:
self.after(self.AUTO_CLEAR_TIME, self.clear_tip)
def clear_tip(self):
"""Remove Tool-Tip"""
self.destroy()
if __name__ == '__main__': if __name__ == '__main__':
window = MainWindow() window = MainWindow()
""" """