Edit and remove devices from menu now
This commit is contained in:
parent
6add3ac976
commit
3666db25c6
10
classes/custom/messagebox.py
Normal file
10
classes/custom/messagebox.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# from typing import Union
|
||||||
|
# from ttkthemes import ThemedTk
|
||||||
|
|
||||||
|
# from classes.custom.toplevel_dialog import ToplevelDialog
|
||||||
|
|
||||||
|
|
||||||
|
# def askyesno(parent: ThemedTk, title: Union[str, None], message: Union[str, None]):
|
||||||
|
|
||||||
|
# toplevel = ToplevelDialog(parent, length=250, width=150)
|
||||||
|
# toplevel.grab_set()
|
39
classes/custom/toplevel_dialog.py
Normal file
39
classes/custom/toplevel_dialog.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# import sv_ttk
|
||||||
|
# from classes.custom.themed_frame import ThemedFrame
|
||||||
|
|
||||||
|
# from classes.custom.themed_toplevel import ThemedToplevel
|
||||||
|
# from modules.theme_titlebar import theme_title_bar
|
||||||
|
# from modules.utils import resize_window, set_icon, use_dark_mode
|
||||||
|
|
||||||
|
|
||||||
|
# class ToplevelDialog(ThemedToplevel):
|
||||||
|
|
||||||
|
# def __init__(self, parent, length: int, width: int):
|
||||||
|
|
||||||
|
# super().__init__(parent)
|
||||||
|
|
||||||
|
# resize_window(self, length, width)
|
||||||
|
|
||||||
|
# self.overrideredirect(True)
|
||||||
|
|
||||||
|
# self.resizable(False, False)
|
||||||
|
|
||||||
|
# sv_ttk.init_theme(self)
|
||||||
|
|
||||||
|
# if use_dark_mode():
|
||||||
|
# theme_title_bar(self, mode="dark")
|
||||||
|
# self.update()
|
||||||
|
|
||||||
|
# set_icon(self)
|
||||||
|
|
||||||
|
# self.focus_set()
|
||||||
|
|
||||||
|
# self.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# self.window_frame = ThemedFrame(self)
|
||||||
|
# self.window_frame.grid(column=0, row=0, sticky=NSEW)
|
||||||
|
|
||||||
|
# # self.window_frame["borderwidth"] = 1
|
||||||
|
# # self.window_frame["relief"] = "solid"
|
||||||
|
|
||||||
|
# self.window_frame.grid_columnconfigure(0, weight=1)
|
@ -1,14 +1,159 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from tkinter import LEFT, NSEW, E, W, ttk
|
import platform
|
||||||
from tkinter.messagebox import askyesno
|
from tkinter import LEFT, NSEW, E, W, Misc, StringVar, ttk
|
||||||
|
from tkinter import messagebox
|
||||||
|
from tkinter.messagebox import askyesno, showinfo
|
||||||
|
from tkinter.simpledialog import askstring
|
||||||
|
from urllib.parse import quote, urlencode
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from ttkthemes import ThemedTk
|
from ttkthemes import ThemedTk
|
||||||
|
|
||||||
from classes.custom.scrollable_frame import ScrollableFrame
|
from classes.custom.scrollable_frame import ScrollableFrame
|
||||||
from classes.custom.themed_frame import ThemedFrame
|
from classes.custom.themed_frame import ThemedFrame
|
||||||
from modules.utils import configGet
|
from modules.utils import configGet, configSet
|
||||||
|
from modules.logger import logger
|
||||||
|
|
||||||
|
|
||||||
|
class FrameDevice(ThemedFrame):
|
||||||
|
|
||||||
|
def __init__(self, master: Misc, device_dict: str, **kwargs) -> None:
|
||||||
|
|
||||||
|
super().__init__(master, style="Card.TFrame", **kwargs)
|
||||||
|
|
||||||
|
self["borderwidth"] = 1
|
||||||
|
self["relief"] = "solid"
|
||||||
|
|
||||||
|
self.grid_columnconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=3)
|
||||||
|
self.grid_columnconfigure(2, weight=3)
|
||||||
|
|
||||||
|
self.name = device_dict["name"]
|
||||||
|
|
||||||
|
self.title = ttk.Label(self, text=self.name, font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
|
||||||
|
self.title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
|
last_upload = "N/A" if device_dict["last_save"] == 0 else datetime.utcfromtimestamp(device_dict["last_save"]).strftime("%d.%m.%Y %H:%M")
|
||||||
|
self.description = ttk.Label(self, text=f'OS: {device_dict["os"]}\nClient: {device_dict["client"]}\nLast upload: {last_upload}', width=46)
|
||||||
|
self.description.grid(column=0, row=1, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
|
self.buttons = ThemedFrame(self)
|
||||||
|
self.buttons.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9)
|
||||||
|
self.buttons.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
self.button_device_rename_action = partial(self.rename)
|
||||||
|
self.button_device_rename = ttk.Button(self.buttons, text="Rename", width=11, command=self.button_device_rename_action)
|
||||||
|
self.button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
|
||||||
|
|
||||||
|
self.button_device_delete_action = partial(self.delete)
|
||||||
|
self.button_device_delete = ttk.Button(self.buttons, text="Delete", style="Accent.TButton", width=11, command=self.button_device_delete_action)
|
||||||
|
self.button_device_delete.grid(column=1, row=0, sticky=W)
|
||||||
|
|
||||||
|
if self.name == configGet("name"):
|
||||||
|
# button_device_rename.state(["disabled"])
|
||||||
|
self.button_device_delete.state(["disabled"])
|
||||||
|
|
||||||
|
def rename(self):
|
||||||
|
|
||||||
|
self.rename_entry = ttk.Entry(self, font=("SunValleyBodyFont", 12), justify=LEFT, width=27)
|
||||||
|
self.rename_entry.insert(0, self.name)
|
||||||
|
self.rename_entry.grid(column=0, row=0, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
|
button_device_cancel_action = partial(self.rename_cancel)
|
||||||
|
button_device_cancel = ttk.Button(self.buttons, text="Cancel", width=11, command=button_device_cancel_action)
|
||||||
|
button_device_cancel.grid(column=0, row=0, padx=9, sticky=E)
|
||||||
|
|
||||||
|
button_device_save_action = partial(self.rename_verify)
|
||||||
|
button_device_save = ttk.Button(self.buttons, text="Save", style="Accent.TButton", width=11, command=button_device_save_action)
|
||||||
|
button_device_save.grid(column=1, row=0, sticky=W)
|
||||||
|
|
||||||
|
def rename_verify(self):
|
||||||
|
|
||||||
|
self.name_before = configGet("name")
|
||||||
|
|
||||||
|
if (self.rename_entry.get().strip() == "") or ("?" in self.rename_entry.get().strip()) or ("/" in self.rename_entry.get().strip()):
|
||||||
|
logger.error(f"Name {self.rename_entry.get().strip()} is not a valid name")
|
||||||
|
messagebox.showerror(title="Name error", message="Provided device name is not valid. Please provide a valid one.")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
quote(self.rename_entry.get().strip())
|
||||||
|
except:
|
||||||
|
logger.error(f"Name {self.rename_entry.get().strip()} is not a valid name")
|
||||||
|
messagebox.showerror(title="Name error", message="Provided device name is not valid. Please provide a valid one.")
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_device_before = requests.get(f'{configGet("address")}/devices/{self.name}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
|
||||||
|
if existing_device_before.status_code == 200:
|
||||||
|
response = requests.patch(f'{configGet("address")}/devices/{self.name}?{urlencode({"new_name": self.rename_entry.get().strip(), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.master.master.master.__version__}"})}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
|
||||||
|
if response.status_code != 204:
|
||||||
|
logger.error(f"Name {self.rename_entry.get().strip()} could not be set because server returned {response.status_code}")
|
||||||
|
messagebox.showerror(title="Name error", message=f"Provided device name is not valid.\n\nServer response: {response.json()}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
logger.error(f"Tried to rename {self.name} into {self.rename_entry.get().strip()} but server returned {existing_device_before.status_code}")
|
||||||
|
messagebox.showerror(title="Rename error", message="It seems like this device no longer exists.")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.name = self.rename_entry.get().strip()
|
||||||
|
|
||||||
|
for widget in self.winfo_children():
|
||||||
|
if isinstance(widget, ttk.Entry):
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
device_title = ttk.Label(self, text=self.name, font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
|
||||||
|
device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
|
button_device_rename_action = partial(self.rename)
|
||||||
|
button_device_rename = ttk.Button(self.buttons, text="Rename", width=11, command=button_device_rename_action)
|
||||||
|
button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
|
||||||
|
|
||||||
|
button_device_delete_action = partial(self.delete)
|
||||||
|
button_device_delete = ttk.Button(self.buttons, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
|
||||||
|
button_device_delete.grid(column=1, row=0, sticky=W)
|
||||||
|
|
||||||
|
if self.name_before == configGet("name"):
|
||||||
|
configSet(["name"], self.name)
|
||||||
|
|
||||||
|
if self.name == configGet("name"):
|
||||||
|
button_device_delete.state(["disabled"])
|
||||||
|
|
||||||
|
def rename_cancel(self):
|
||||||
|
|
||||||
|
for widget in self.winfo_children():
|
||||||
|
if isinstance(widget, ttk.Entry):
|
||||||
|
widget.destroy()
|
||||||
|
|
||||||
|
device_title = ttk.Label(self, text=self.name, font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
|
||||||
|
device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
|
button_device_rename_action = partial(self.rename)
|
||||||
|
button_device_rename = ttk.Button(self.buttons, text="Rename", width=11, command=button_device_rename_action)
|
||||||
|
button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
|
||||||
|
|
||||||
|
button_device_delete_action = partial(self.delete)
|
||||||
|
button_device_delete = ttk.Button(self.buttons, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
|
||||||
|
button_device_delete.grid(column=1, row=0, sticky=W)
|
||||||
|
|
||||||
|
if self.name == configGet("name"):
|
||||||
|
button_device_delete.state(["disabled"])
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
|
||||||
|
decision = askyesno(title="Device removal", message=f"You are about to remove the device '{self.name}' and this will also remove all the save files uploaded by this device. Are you sure you want to continue?")
|
||||||
|
|
||||||
|
if decision is False:
|
||||||
|
return
|
||||||
|
|
||||||
|
requests.delete(f'{configGet("address")}/devices/{self.name}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
|
||||||
|
|
||||||
|
for k in range(len(self.master.devices)):
|
||||||
|
if self.master.devices[k]["name"] == self.name:
|
||||||
|
del self.master.devices[k]
|
||||||
|
break
|
||||||
|
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
class FrameDevices(ScrollableFrame):
|
class FrameDevices(ScrollableFrame):
|
||||||
@ -36,37 +181,38 @@ class FrameDevices(ScrollableFrame):
|
|||||||
i = 0
|
i = 0
|
||||||
for device in self.devices:
|
for device in self.devices:
|
||||||
|
|
||||||
device_frame = ThemedFrame(self, style="Card.TFrame")
|
device_frame = FrameDevice(self, device_dict=device)
|
||||||
device_frame.grid(column=0, row=i, pady=9, padx=9, sticky=NSEW)
|
device_frame.grid(column=0, row=i, pady=9, padx=9, sticky=NSEW)
|
||||||
|
|
||||||
# self.device_frame["borderwidth"] = 1
|
# self.device_frame["borderwidth"] = 1
|
||||||
# self.device_frame["relief"] = "solid"
|
# self.device_frame["relief"] = "solid"
|
||||||
|
|
||||||
device_frame.grid_columnconfigure(0, weight=1)
|
# device_frame.grid_columnconfigure(0, weight=1)
|
||||||
device_frame.grid_columnconfigure(1, weight=3)
|
# device_frame.grid_columnconfigure(1, weight=3)
|
||||||
device_frame.grid_columnconfigure(2, weight=3)
|
# device_frame.grid_columnconfigure(2, weight=3)
|
||||||
|
|
||||||
device_title = ttk.Label(device_frame, text=device["name"], font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
|
# device_title = ttk.Label(device_frame, text=device["name"], font=("SunValleyBodyFont", 12), justify=LEFT, width=46)
|
||||||
device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
|
# device_title.grid(column=0, row=0, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
last_upload = "N/A" if device["last_save"] == 0 else datetime.utcfromtimestamp(device["last_save"]).strftime("%d.%m.%Y %H:%M")
|
# last_upload = "N/A" if device["last_save"] == 0 else datetime.utcfromtimestamp(device["last_save"]).strftime("%d.%m.%Y %H:%M")
|
||||||
device_description = ttk.Label(device_frame, text=f'OS: {device["os"]}\nClient: {device["client"]}\nLast upload: {last_upload}', width=46)
|
# device_description = ttk.Label(device_frame, text=f'OS: {device["os"]}\nClient: {device["client"]}\nLast upload: {last_upload}', width=46)
|
||||||
device_description.grid(column=0, row=1, padx=9, pady=9, sticky=W)
|
# device_description.grid(column=0, row=1, padx=9, pady=9, sticky=W)
|
||||||
|
|
||||||
buttons_frame = ThemedFrame(device_frame)
|
# buttons_frame = ThemedFrame(device_frame)
|
||||||
buttons_frame.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9)
|
# buttons_frame.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9)
|
||||||
buttons_frame.grid_columnconfigure(0, weight=1)
|
# buttons_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
button_device_rename = ttk.Button(buttons_frame, text="Rename", width=11)
|
# button_device_rename_action = partial(self.device_rename, device_frame, buttons_frame, device["name"])
|
||||||
button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
|
# button_device_rename = ttk.Button(buttons_frame, text="Rename", width=11, command=button_device_rename_action)
|
||||||
|
# button_device_rename.grid(column=0, row=0, padx=9, sticky=E)
|
||||||
|
|
||||||
button_device_delete_action = partial(self.device_delete, device["name"])
|
# button_device_delete_action = partial(self.device_delete, device["name"])
|
||||||
button_device_delete = ttk.Button(buttons_frame, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
|
# button_device_delete = ttk.Button(buttons_frame, text="Delete", style="Accent.TButton", width=11, command=button_device_delete_action)
|
||||||
button_device_delete.grid(column=1, row=0, sticky=W)
|
# button_device_delete.grid(column=1, row=0, sticky=W)
|
||||||
|
|
||||||
if device["name"] == configGet("name"):
|
# if device["name"] == configGet("name"):
|
||||||
# button_device_rename.state(["disabled"])
|
# # button_device_rename.state(["disabled"])
|
||||||
button_device_delete.state(["disabled"])
|
# button_device_delete.state(["disabled"])
|
||||||
|
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
@ -75,26 +221,6 @@ class FrameDevices(ScrollableFrame):
|
|||||||
divider.grid(column=0, row=i+1, pady=9)
|
divider.grid(column=0, row=i+1, pady=9)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def device_delete(self, name: str):
|
|
||||||
|
|
||||||
decision = askyesno(title="Device removal", message=f"You are about to remove the device '{name}' and this will also remove all the save files uploaded by this device. Are you sure you want to continue?")
|
|
||||||
|
|
||||||
if decision is False:
|
|
||||||
return
|
|
||||||
|
|
||||||
requests.delete(f'{configGet("address")}/devices/{name}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
|
|
||||||
|
|
||||||
for k in range(len(self.devices)):
|
|
||||||
print(k)
|
|
||||||
if self.devices[k]["name"] == name:
|
|
||||||
del self.devices[k]
|
|
||||||
break
|
|
||||||
|
|
||||||
for widget in self.winfo_children():
|
|
||||||
widget.destroy()
|
|
||||||
|
|
||||||
self.render_devices()
|
|
||||||
|
|
||||||
class FrameDevicesEmpty(ThemedFrame):
|
class FrameDevicesEmpty(ThemedFrame):
|
||||||
|
|
||||||
def __init__(self, master: ThemedTk, **kwargs) -> None:
|
def __init__(self, master: ThemedTk, **kwargs) -> None:
|
||||||
|
@ -101,7 +101,11 @@ class ToplevelWelcome(ThemedToplevel):
|
|||||||
self.stage_entry_1.insert(0, self.address_text[:-1])
|
self.stage_entry_1.insert(0, self.address_text[:-1])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
requests.get(self.stage_entry_1.get()+"/check", verify=not bool(self.stage_checkbox_var.get()))
|
response_check = requests.get(self.stage_entry_1.get()+"/check", verify=not bool(self.stage_checkbox_var.get()))
|
||||||
|
if response_check.status_code != 200:
|
||||||
|
logger.error(f"Could not connect to '{self.stage_entry_1.get()}' because it returned {response_check.status_code}")
|
||||||
|
messagebox.showerror(title="Connection error", message=f"Server response is {response_check.status_code}. Check if your API server and it's reverse proxy (if there's one) properly configured and then try again.")
|
||||||
|
return
|
||||||
except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema, requests.exceptions.MissingSchema) as exp:
|
except (requests.exceptions.InvalidURL, requests.exceptions.InvalidSchema, requests.exceptions.MissingSchema) as exp:
|
||||||
logger.error(f"Could not validate '{self.stage_entry_1.get()}' due to {exp}")
|
logger.error(f"Could not validate '{self.stage_entry_1.get()}' due to {exp}")
|
||||||
messagebox.showerror(title="Invalid address", message="Address entered does not seem to be correct one. Please provide API address starting with http:// or https:// \n\nFor example:\n- https://your-api.com:8043\n- https://your-api.com")
|
messagebox.showerror(title="Invalid address", message="Address entered does not seem to be correct one. Please provide API address starting with http:// or https:// \n\nFor example:\n- https://your-api.com:8043\n- https://your-api.com")
|
||||||
|
Loading…
Reference in New Issue
Block a user