Still WIP
This commit is contained in:
parent
021dbf560f
commit
cab36a0fdb
@ -1,15 +1,16 @@
|
|||||||
from os import path
|
from os import path
|
||||||
from tkinter import NW, E, N, S, W, messagebox, ttk
|
from tkinter import NSEW, NW, E, N, S, W, messagebox, ttk
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import sv_ttk
|
import sv_ttk
|
||||||
from ttkthemes import ThemedTk
|
from ttkthemes import ThemedTk
|
||||||
from classes.custom.themed_frame import ThemedFrame
|
|
||||||
|
|
||||||
from classes.frames.devices import FrameDevices
|
from classes.custom.scrollable_frame import FIT_HEIGHT, FIT_WIDTH
|
||||||
|
from classes.custom.themed_frame import ThemedFrame
|
||||||
|
from classes.frames.devices import FrameDevices, FrameDevicesEmpty
|
||||||
|
from classes.frames.errors import FrameErrorConnection, FrameErrorSavesFolder, FrameErrorUnconfigured
|
||||||
from classes.frames.saves import FrameSaves
|
from classes.frames.saves import FrameSaves
|
||||||
from classes.frames.settings import FrameSettings
|
from classes.frames.settings import FrameSettings
|
||||||
from classes.frames.errors import FrameErrorConnection, FrameErrorSavesFolder, FrameErrorUnconfigured
|
|
||||||
from classes.toplevel.welcome import ToplevelWelcome
|
from classes.toplevel.welcome import ToplevelWelcome
|
||||||
from modules.theme_titlebar import theme_title_bar
|
from modules.theme_titlebar import theme_title_bar
|
||||||
from modules.utils import configGet, get_string_mode, use_dark_mode
|
from modules.utils import configGet, get_string_mode, use_dark_mode
|
||||||
@ -21,7 +22,9 @@ class App(ThemedTk):
|
|||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.window_width = 650
|
self.__version__ = "0.1.0"
|
||||||
|
|
||||||
|
self.window_width = 600
|
||||||
self.window_height = 400
|
self.window_height = 400
|
||||||
|
|
||||||
self.screen_width = self.winfo_screenwidth()
|
self.screen_width = self.winfo_screenwidth()
|
||||||
@ -66,16 +69,30 @@ class App(ThemedTk):
|
|||||||
|
|
||||||
|
|
||||||
def frame_saves(self):
|
def frame_saves(self):
|
||||||
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.frame_saves_object = FrameSaves(self)
|
self.frame_saves_object = FrameSaves(self)
|
||||||
self.frame_saves_object.grid(column=1, row=0, sticky=N+S+W+E)
|
self.frame_saves_object.grid(column=1, row=0, sticky=NSEW)
|
||||||
return
|
return
|
||||||
|
|
||||||
def frame_devices(self):
|
def frame_devices(self):
|
||||||
self.frame_devices_object = FrameDevices(self)
|
self.grid_rowconfigure(0, weight=1)
|
||||||
self.frame_devices_object.grid(column=1, row=0, sticky=N+S+W+E)
|
self.grid_columnconfigure(1, weight=1)
|
||||||
|
self.frame_devices_devices = requests.get(f'{configGet("address")}/devices', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed"))
|
||||||
|
if self.frame_devices_devices.status_code == 200 and isinstance(self.frame_devices_devices.json(), list) is True and len(self.frame_devices_devices.json()) > 0:
|
||||||
|
self.frame_devices_object = FrameDevices(self, self.frame_devices_devices.json(), vscroll=True)
|
||||||
|
# self.frame_devices_object.resize(FIT_HEIGHT)
|
||||||
|
else:
|
||||||
|
self.frame_devices_object = FrameDevicesEmpty(self)
|
||||||
|
#self.frame_devices_object = FrameDevices(self, hscroll=False, vscroll=True)
|
||||||
|
#self.frame_devices_object.resize(FIT_WIDTH)
|
||||||
|
#self.frame_devices_object.resize(FIT_HEIGHT)
|
||||||
|
self.frame_devices_object.grid(column=1, row=0, sticky=NSEW)
|
||||||
return
|
return
|
||||||
|
|
||||||
def frame_settings(self):
|
def frame_settings(self):
|
||||||
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.frame_settings_object = FrameSettings(self)
|
self.frame_settings_object = FrameSettings(self)
|
||||||
self.frame_settings_object.grid(column=1, row=0, sticky=N+S+W+E)
|
self.frame_settings_object.grid(column=1, row=0, sticky=N+S+W+E)
|
||||||
return
|
return
|
||||||
@ -113,6 +130,8 @@ class App(ThemedTk):
|
|||||||
if configGet("address") in ["", None] or configGet("apikey") in ["", None]:
|
if configGet("address") in ["", None] or configGet("apikey") in ["", None]:
|
||||||
self.item_saves.state(["disabled"])
|
self.item_saves.state(["disabled"])
|
||||||
self.item_devices.state(["disabled"])
|
self.item_devices.state(["disabled"])
|
||||||
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.connection_error = FrameErrorUnconfigured(self)
|
self.connection_error = FrameErrorUnconfigured(self)
|
||||||
self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E)
|
self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E)
|
||||||
# messagebox.showerror(title="Configuration error", message="Your client is not properly configured.")
|
# messagebox.showerror(title="Configuration error", message="Your client is not properly configured.")
|
||||||
@ -121,6 +140,8 @@ class App(ThemedTk):
|
|||||||
if self.verify_authorization() is False:
|
if self.verify_authorization() is False:
|
||||||
self.item_saves.state(["disabled"])
|
self.item_saves.state(["disabled"])
|
||||||
self.item_devices.state(["disabled"])
|
self.item_devices.state(["disabled"])
|
||||||
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.connection_error = FrameErrorConnection(self)
|
self.connection_error = FrameErrorConnection(self)
|
||||||
self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E)
|
self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E)
|
||||||
messagebox.showerror(title="Authentication error", message="Your API key seems to be invalid.")
|
messagebox.showerror(title="Authentication error", message="Your API key seems to be invalid.")
|
||||||
@ -128,6 +149,8 @@ class App(ThemedTk):
|
|||||||
|
|
||||||
if self.verify_saves_dir() is False:
|
if self.verify_saves_dir() is False:
|
||||||
self.item_saves.state(["disabled"])
|
self.item_saves.state(["disabled"])
|
||||||
|
self.grid_rowconfigure(0, weight=1)
|
||||||
|
self.grid_columnconfigure(1, weight=1)
|
||||||
self.connection_error = FrameErrorSavesFolder(self)
|
self.connection_error = FrameErrorSavesFolder(self)
|
||||||
self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E)
|
self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E)
|
||||||
messagebox.showerror(title="Configuration error", message="Saves folder seems to be invalid.")
|
messagebox.showerror(title="Configuration error", message="Saves folder seems to be invalid.")
|
||||||
|
151
classes/custom/scrollable_frame.py
Normal file
151
classes/custom/scrollable_frame.py
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
from tkinter import NSEW, Canvas, Event, ttk
|
||||||
|
|
||||||
|
from classes.custom.themed_frame import ThemedFrame
|
||||||
|
|
||||||
|
FIT_WIDTH = "fit_width"
|
||||||
|
FIT_HEIGHT = "fit_height"
|
||||||
|
|
||||||
|
|
||||||
|
class ScrollableFrame(ThemedFrame):
|
||||||
|
"""
|
||||||
|
There is no way to scroll <tkinter.Frame> so we are
|
||||||
|
going to create a canvas and place the frame there.
|
||||||
|
Scrolling the canvas will give the illusion of scrolling
|
||||||
|
the frame
|
||||||
|
Partly taken from:
|
||||||
|
https://blog.tecladocode.com/tkinter-scrollable-frames/
|
||||||
|
https://stackoverflow.com/a/17457843/11106801
|
||||||
|
master_frame---------------------------------------------------------
|
||||||
|
| dummy_canvas----------------------------------------- y_scroll-- |
|
||||||
|
| | self--------------------------------------------- | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
| | ------------------------------------------------ | | | |
|
||||||
|
| ---------------------------------------------------- --------- |
|
||||||
|
| x_scroll--------------------------------------------- |
|
||||||
|
| | | |
|
||||||
|
| ---------------------------------------------------- |
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
"""
|
||||||
|
def __init__(self, master=None, scroll_speed:int=2, hscroll:bool=False, vscroll:bool=True, scrollbar_kwargs={}, **kwargs):
|
||||||
|
assert isinstance(scroll_speed, int), "`scroll_speed` must be an int"
|
||||||
|
self.scroll_speed = scroll_speed
|
||||||
|
|
||||||
|
self.master_frame = ThemedFrame(master)
|
||||||
|
self.master_frame.grid_rowconfigure(0, weight=1)
|
||||||
|
self.master_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
self.dummy_canvas = Canvas(self.master_frame, highlightthickness=0, **kwargs)
|
||||||
|
super().__init__(self.dummy_canvas)
|
||||||
|
|
||||||
|
# Create the 2 scrollbars
|
||||||
|
if vscroll:
|
||||||
|
self.v_scrollbar = ttk.Scrollbar(self.master_frame, orient="vertical", command=self.dummy_canvas.yview, **scrollbar_kwargs)
|
||||||
|
self.v_scrollbar.grid(row=0, column=1, sticky=NSEW)
|
||||||
|
self.dummy_canvas.configure(yscrollcommand=self.v_scrollbar.set)
|
||||||
|
if hscroll:
|
||||||
|
self.h_scrollbar = ttk.Scrollbar(self.master_frame, orient="horizontal", command=self.dummy_canvas.xview, **scrollbar_kwargs)
|
||||||
|
self.h_scrollbar.grid(row=1, column=0, sticky=NSEW)
|
||||||
|
self.dummy_canvas.configure(xscrollcommand=self.h_scrollbar.set)
|
||||||
|
|
||||||
|
# Bind to the mousewheel scrolling
|
||||||
|
self.dummy_canvas.bind_all("<MouseWheel>", self.scrolling_windows, add=True)
|
||||||
|
self.dummy_canvas.bind_all("<Button-4>", self.scrolling_linux, add=True)
|
||||||
|
self.dummy_canvas.bind_all("<Button-5>", self.scrolling_linux, add=True)
|
||||||
|
self.bind("<Configure>", self.scrollbar_scrolling, add=True)
|
||||||
|
|
||||||
|
# Place `self` inside `dummy_canvas`
|
||||||
|
self.dummy_canvas.create_window((0, 0), window=self, anchor="nw")
|
||||||
|
# Place `dummy_canvas` inside `master_frame`
|
||||||
|
self.dummy_canvas.grid(row=0, column=0, sticky=NSEW)
|
||||||
|
|
||||||
|
self.pack = self.master_frame.pack
|
||||||
|
self.grid = self.master_frame.grid
|
||||||
|
self.place = self.master_frame.place
|
||||||
|
self.pack_forget = self.master_frame.pack_forget
|
||||||
|
self.grid_forget = self.master_frame.grid_forget
|
||||||
|
self.place_forget = self.master_frame.place_forget
|
||||||
|
|
||||||
|
def scrolling_windows(self, event:Event) -> None:
|
||||||
|
assert event.delta != 0, "On Windows, `event.delta` should never be 0"
|
||||||
|
y_steps = int(-event.delta/abs(event.delta)*self.scroll_speed)
|
||||||
|
self.dummy_canvas.yview_scroll(y_steps, "units")
|
||||||
|
|
||||||
|
def scrolling_linux(self, event:Event) -> None:
|
||||||
|
y_steps = self.scroll_speed
|
||||||
|
if event.num == 4:
|
||||||
|
y_steps *= -1
|
||||||
|
self.dummy_canvas.yview_scroll(y_steps, "units")
|
||||||
|
|
||||||
|
def scrollbar_scrolling(self, event:Event) -> None:
|
||||||
|
region = list(self.dummy_canvas.bbox("all"))
|
||||||
|
region[2] = max(self.dummy_canvas.winfo_width(), region[2])
|
||||||
|
region[3] = max(self.dummy_canvas.winfo_height(), region[3])
|
||||||
|
self.dummy_canvas.configure(scrollregion=region)
|
||||||
|
|
||||||
|
def resize(self, fit:str=None, height:int=None, width:int=None) -> None:
|
||||||
|
"""
|
||||||
|
Resizes the frame to fit the widgets inside. You must either
|
||||||
|
specify (the `fit`) or (the `height` or/and the `width`) parameter.
|
||||||
|
Parameters:
|
||||||
|
fit:str `fit` can be either `FIT_WIDTH` or `FIT_HEIGHT`.
|
||||||
|
`FIT_WIDTH` makes sure that the frame's width can
|
||||||
|
fit all of the widgets. `FIT_HEIGHT` is simmilar
|
||||||
|
height:int specifies the height of the frame in pixels
|
||||||
|
width:int specifies the width of the frame in pixels
|
||||||
|
To do:
|
||||||
|
ALWAYS_FIT_WIDTH
|
||||||
|
ALWAYS_FIT_HEIGHT
|
||||||
|
"""
|
||||||
|
if height is not None:
|
||||||
|
self.dummy_canvas.config(height=height)
|
||||||
|
if width is not None:
|
||||||
|
self.dummy_canvas.config(width=width)
|
||||||
|
if fit == FIT_WIDTH:
|
||||||
|
super().update()
|
||||||
|
self.dummy_canvas.config(width=super().winfo_width())
|
||||||
|
elif fit == FIT_HEIGHT:
|
||||||
|
super().update()
|
||||||
|
self.dummy_canvas.config(height=super().winfo_height())
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknow value for the `fit` parameter.")
|
||||||
|
fit = resize
|
||||||
|
|
||||||
|
# import tkinter as tk
|
||||||
|
# from tkinter import ttk
|
||||||
|
# from classes.custom.themed_frame import ThemedFrame
|
||||||
|
|
||||||
|
# class ScrollableFrame(ThemedFrame):
|
||||||
|
|
||||||
|
# def __init__(self, container, *args, **kwargs):
|
||||||
|
|
||||||
|
# super().__init__(container, *args, **kwargs)
|
||||||
|
|
||||||
|
# self.canvas = tk.Canvas(self)
|
||||||
|
# scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
|
||||||
|
|
||||||
|
# self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
|
||||||
|
|
||||||
|
# self.scrollable_frame = ThemedFrame(self.canvas)
|
||||||
|
|
||||||
|
# self.scrollable_frame.bind(
|
||||||
|
# "<Configure>",
|
||||||
|
# lambda e: self.canvas.configure(
|
||||||
|
# scrollregion=self.canvas.bbox("all")
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
||||||
|
|
||||||
|
# self.canvas.configure(yscrollcommand=scrollbar.set)
|
||||||
|
|
||||||
|
# self.canvas.pack(side="left", fill="both", expand=True)
|
||||||
|
# scrollbar.pack(side="right", fill="y")
|
||||||
|
|
||||||
|
# def _on_mousewheel(self, event):
|
||||||
|
# shift = (event.state & 0x1) != 0
|
||||||
|
# scroll = -1 if event.delta > 0 else 1
|
||||||
|
# if shift:
|
||||||
|
# self.canvas.xview_scroll(scroll, "units")
|
||||||
|
# else:
|
||||||
|
# self.canvas.yview_scroll(scroll, "units")
|
@ -1,9 +1,11 @@
|
|||||||
from tkinter import Misc
|
from tkinter import Misc
|
||||||
import sv_ttk
|
|
||||||
from tkinter.ttk import Frame
|
from tkinter.ttk import Frame
|
||||||
|
|
||||||
|
import sv_ttk
|
||||||
|
|
||||||
from modules.utils import get_string_mode
|
from modules.utils import get_string_mode
|
||||||
|
|
||||||
|
|
||||||
class ThemedFrame(Frame):
|
class ThemedFrame(Frame):
|
||||||
|
|
||||||
def __init__(self, master: Misc, **kwargs) -> None:
|
def __init__(self, master: Misc, **kwargs) -> None:
|
||||||
|
@ -1,11 +1,101 @@
|
|||||||
from tkinter import EW, NS, ttk
|
from datetime import datetime
|
||||||
|
from functools import partial
|
||||||
|
from tkinter import LEFT, NSEW, E, W, ttk
|
||||||
|
from tkinter.messagebox import askyesno
|
||||||
|
|
||||||
|
import requests
|
||||||
from ttkthemes import ThemedTk
|
from ttkthemes import ThemedTk
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class FrameDevices(ThemedFrame):
|
class FrameDevices(ScrollableFrame):
|
||||||
|
|
||||||
|
def __init__(self, master: ThemedTk, devices: list, **kwargs) -> None:
|
||||||
|
|
||||||
|
super().__init__(master, **kwargs)
|
||||||
|
|
||||||
|
master.title("Devices - Stardew Sync")
|
||||||
|
|
||||||
|
# self["borderwidth"] = 1
|
||||||
|
# self["relief"] = "solid"
|
||||||
|
|
||||||
|
self.devices = devices
|
||||||
|
|
||||||
|
# self.grid_columnconfigure(0, weight=1)
|
||||||
|
# self.grid_rowconfigure(0, weight=2)
|
||||||
|
|
||||||
|
master.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
self.render_devices()
|
||||||
|
|
||||||
|
def render_devices(self):
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for device in self.devices:
|
||||||
|
|
||||||
|
device_frame = ThemedFrame(self, style="Card.TFrame")
|
||||||
|
device_frame.grid(column=0, row=i, pady=9, padx=9, sticky=NSEW)
|
||||||
|
|
||||||
|
# self.device_frame["borderwidth"] = 1
|
||||||
|
# self.device_frame["relief"] = "solid"
|
||||||
|
|
||||||
|
device_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
device_frame.grid_columnconfigure(1, 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.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")
|
||||||
|
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)
|
||||||
|
|
||||||
|
buttons_frame = ThemedFrame(device_frame)
|
||||||
|
buttons_frame.grid(column=0, columnspan=2, row=2, sticky=NSEW, padx=9, pady=9)
|
||||||
|
buttons_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
button_device_rename = ttk.Button(buttons_frame, text="Rename", width=11)
|
||||||
|
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 = 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)
|
||||||
|
|
||||||
|
if device["name"] == configGet("name"):
|
||||||
|
button_device_rename.state(["disabled"])
|
||||||
|
button_device_delete.state(["disabled"])
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if i+1 != len(self.devices):
|
||||||
|
divider = ttk.Separator(self, orient="horizontal")
|
||||||
|
divider.grid(column=0, row=i+1, pady=9)
|
||||||
|
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):
|
||||||
|
|
||||||
def __init__(self, master: ThemedTk, **kwargs) -> None:
|
def __init__(self, master: ThemedTk, **kwargs) -> None:
|
||||||
|
|
||||||
@ -13,11 +103,13 @@ class FrameDevices(ThemedFrame):
|
|||||||
|
|
||||||
master.title("Devices - Stardew Sync")
|
master.title("Devices - Stardew Sync")
|
||||||
|
|
||||||
self["borderwidth"] = 2
|
# self["borderwidth"] = 1
|
||||||
self["relief"] = "solid"
|
# self["relief"] = "solid"
|
||||||
|
|
||||||
for btn in range(0, 10):
|
self.grid_columnconfigure(0, weight=1)
|
||||||
ttk.Button(self, text="DEVICES "+str(btn)).grid(column=0, row=btn, padx=9, pady=9, sticky=EW)
|
self.grid_rowconfigure(0, weight=2)
|
||||||
|
|
||||||
self.scrollbar = ttk.Scrollbar(self, orient="vertical")
|
master.columnconfigure(1, weight=1)
|
||||||
self.scrollbar.grid(column=1, row=0, sticky=NS)
|
|
||||||
|
self.label = ttk.Label(self, text="No devices found")
|
||||||
|
self.label.grid(column=0, row=0, padx=9, pady=9)
|
@ -1,4 +1,5 @@
|
|||||||
from os import getenv, path
|
from os import getenv, path
|
||||||
|
import platform
|
||||||
from tkinter import N, NSEW, S, W, E, END, IntVar, StringVar, filedialog, messagebox, ttk
|
from tkinter import N, NSEW, S, W, E, END, IntVar, StringVar, filedialog, messagebox, ttk
|
||||||
|
|
||||||
from ttkthemes import ThemedTk
|
from ttkthemes import ThemedTk
|
||||||
@ -7,6 +8,7 @@ from modules.theme_titlebar import theme_title_bar
|
|||||||
|
|
||||||
from modules.utils import configGet, configSet, get_string_mode, use_dark_mode
|
from modules.utils import configGet, configSet, get_string_mode, use_dark_mode
|
||||||
from modules.logger import logger
|
from modules.logger import logger
|
||||||
|
from urllib.parse import urlencode, quote
|
||||||
import requests
|
import requests
|
||||||
import sv_ttk
|
import sv_ttk
|
||||||
|
|
||||||
@ -17,29 +19,41 @@ class FrameSettings(ThemedFrame):
|
|||||||
|
|
||||||
super().__init__(master, **kwargs)
|
super().__init__(master, **kwargs)
|
||||||
|
|
||||||
master.title("Configuration")
|
master.title("Settings - Stardew Sync")
|
||||||
|
|
||||||
self.columnconfigure(0, weight=1)
|
self.columnconfigure(0, weight=1)
|
||||||
self.columnconfigure(1, weight=3)
|
self.columnconfigure(1, weight=3)
|
||||||
|
|
||||||
master.columnconfigure(1, weight=1)
|
master.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
# Name
|
||||||
|
self.name_label = ttk.Label(self, text="Name:")
|
||||||
|
self.name_label.grid(column=0, row=0, sticky=W, padx=9, pady=9)
|
||||||
|
|
||||||
|
self.name_entry = ttk.Entry(self)
|
||||||
|
self.name_entry.grid(column=1, row=0, sticky=N+S+E+W, padx=9, pady=9)
|
||||||
|
if configGet("name") is not None:
|
||||||
|
self.name_entry.insert(0, configGet("name"))
|
||||||
|
else:
|
||||||
|
self.name_entry.insert(0, str(platform.node()))
|
||||||
|
# ====
|
||||||
|
|
||||||
# Address
|
# Address
|
||||||
self.address_label = ttk.Label(self, text="Address:")
|
self.address_label = ttk.Label(self, text="Address:")
|
||||||
self.address_label.grid(column=0, row=0, sticky=W, padx=9, pady=9)
|
self.address_label.grid(column=0, row=1, sticky=W, padx=9, pady=9)
|
||||||
|
|
||||||
self.address_entry = ttk.Entry(self)
|
self.address_entry = ttk.Entry(self)
|
||||||
self.address_entry.grid(column=1, row=0, sticky=N+S+E+W, padx=9, pady=9)
|
self.address_entry.grid(column=1, row=1, sticky=N+S+E+W, padx=9, pady=9)
|
||||||
if configGet("address") is not None:
|
if configGet("address") is not None:
|
||||||
self.address_entry.insert(0, configGet("address"))
|
self.address_entry.insert(0, configGet("address"))
|
||||||
# =======
|
# =======
|
||||||
|
|
||||||
# API Key
|
# API Key
|
||||||
self.apikey_label = ttk.Label(self, text="API key:")
|
self.apikey_label = ttk.Label(self, text="API key:")
|
||||||
self.apikey_label.grid(column=0, row=1, sticky=W, padx=9, pady=9)
|
self.apikey_label.grid(column=0, row=2, sticky=W, padx=9, pady=9)
|
||||||
|
|
||||||
self.apikey_entry = ttk.Entry(self)
|
self.apikey_entry = ttk.Entry(self)
|
||||||
self.apikey_entry.grid(column=1, row=1, sticky=N+S+E+W, padx=9, pady=9)
|
self.apikey_entry.grid(column=1, row=2, sticky=N+S+E+W, padx=9, pady=9)
|
||||||
if configGet("apikey") is not None:
|
if configGet("apikey") is not None:
|
||||||
self.apikey_entry.insert(0, configGet("apikey"))
|
self.apikey_entry.insert(0, configGet("apikey"))
|
||||||
# =======
|
# =======
|
||||||
@ -50,19 +64,19 @@ class FrameSettings(ThemedFrame):
|
|||||||
else:
|
else:
|
||||||
self.self_signed_check_bool = IntVar()
|
self.self_signed_check_bool = IntVar()
|
||||||
self.self_signed_check = ttk.Checkbutton(self, text="Allow self-signed certificates", variable=self.self_signed_check_bool)
|
self.self_signed_check = ttk.Checkbutton(self, text="Allow self-signed certificates", variable=self.self_signed_check_bool)
|
||||||
self.self_signed_check.grid(column=1, row=2, sticky=W, padx=9, pady=9)
|
self.self_signed_check.grid(column=1, row=3, sticky=W, padx=9, pady=9)
|
||||||
# ===========
|
# ===========
|
||||||
|
|
||||||
# Saves location
|
# Saves location
|
||||||
self.saves_location_label = ttk.Label(self, text="Saves location:")
|
self.saves_location_label = ttk.Label(self, text="Saves location:")
|
||||||
self.saves_location_label.grid(column=0, row=3, sticky=W, padx=9, pady=9)
|
self.saves_location_label.grid(column=0, row=4, sticky=W, padx=9, pady=9)
|
||||||
|
|
||||||
self.saves_frame = ThemedFrame(self)
|
self.saves_frame = ThemedFrame(self)
|
||||||
self.saves_frame.grid(column=1, row=3, sticky=N+S+E+W, padx=9, pady=9)
|
self.saves_frame.grid(column=1, row=4, sticky=N+S+E+W, padx=9, pady=9)
|
||||||
self.saves_frame.grid_columnconfigure(0, weight=1)
|
self.saves_frame.grid_columnconfigure(0, weight=1)
|
||||||
self.saves_frame.grid_columnconfigure(1, weight=3)
|
self.saves_frame.grid_columnconfigure(1, weight=3)
|
||||||
|
|
||||||
self.saves_location_entry = ttk.Entry(self.saves_frame, width=36)
|
self.saves_location_entry = ttk.Entry(self.saves_frame, width=30)
|
||||||
self.saves_location_entry.grid(column=0, row=0, sticky=NSEW)
|
self.saves_location_entry.grid(column=0, row=0, sticky=NSEW)
|
||||||
if configGet("saves_location") is not None:
|
if configGet("saves_location") is not None:
|
||||||
self.saves_location_entry.insert(0, configGet("saves_location"))
|
self.saves_location_entry.insert(0, configGet("saves_location"))
|
||||||
@ -87,7 +101,7 @@ class FrameSettings(ThemedFrame):
|
|||||||
# ==============
|
# ==============
|
||||||
|
|
||||||
self.buttons_frame = ThemedFrame(self)
|
self.buttons_frame = ThemedFrame(self)
|
||||||
self.buttons_frame.grid(column=0, columnspan=2, sticky=NSEW, padx=9, pady=9)
|
self.buttons_frame.grid(column=0, columnspan=2, row=6, sticky=NSEW, padx=9, pady=9)
|
||||||
self.buttons_frame.grid_columnconfigure(0, weight=1)
|
self.buttons_frame.grid_columnconfigure(0, weight=1)
|
||||||
|
|
||||||
self.validate_button = ttk.Button(self.buttons_frame, text="Validate", width=11, command=self.validate_configuration)
|
self.validate_button = ttk.Button(self.buttons_frame, text="Validate", width=11, command=self.validate_configuration)
|
||||||
@ -129,7 +143,25 @@ class FrameSettings(ThemedFrame):
|
|||||||
else:
|
else:
|
||||||
self.address_text = None
|
self.address_text = None
|
||||||
|
|
||||||
configSet(["address"], self.address_text)
|
# =========================
|
||||||
|
if self.name_entry.get().strip() == configGet("name"):
|
||||||
|
existing_device = requests.get(f'{self.address_entry.get()}/devices/{quote(configGet("name").encode("utf-8"))}', headers={"apikey": self.apikey_entry.get()}, verify=not bool(self.self_signed_check_bool.get()))
|
||||||
|
if existing_device.status_code != 200:
|
||||||
|
requests.post(f'{self.address_entry.get()}/devices?{urlencode({"name": quote(configGet("name").encode("utf-8")), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.__version__}"})}', headers={"apikey": self.apikey_entry.get()}, verify=not bool(self.self_signed_check_bool.get()))
|
||||||
|
else:
|
||||||
|
if configGet("name") is not None:
|
||||||
|
existing_device_before = requests.get(f'{self.address_entry.get()}/devices/{quote(configGet("name").encode("utf-8"))}', headers={"apikey": self.apikey_entry.get()}, verify=not bool(self.self_signed_check_bool.get()))
|
||||||
|
if existing_device_before.status_code == 200:
|
||||||
|
requests.patch(f'{self.address_entry.get()}/devices/{quote(configGet("name").encode("utf-8"))}?{urlencode({"new_name": quote(self.name_entry.get().strip().encode("utf-8")), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.__version__}"})}', headers={"apikey": self.apikey_entry.get()}, verify=not bool(self.self_signed_check_bool.get()))
|
||||||
|
else:
|
||||||
|
device_created = requests.post(f'{self.address_entry.get()}/devices?{urlencode({"name": quote(self.name_entry.get().strip().encode("utf-8")), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.__version__}"})}', headers={"apikey": self.apikey_entry.get()}, verify=not bool(self.self_signed_check_bool.get()))
|
||||||
|
if device_created.status_code != 204:
|
||||||
|
messagebox.showerror(title="Name error", message=f"Could not register device in database using name '{self.name_entry.get().strip()}' with error:\n\n{device_created.json()}")
|
||||||
|
return
|
||||||
|
# =========================
|
||||||
|
|
||||||
|
configSet(["name"], self.name_entry.get().strip())
|
||||||
|
configSet(["address"], self.address_entry.get())
|
||||||
configSet(["apikey"], self.apikey_entry.get())
|
configSet(["apikey"], self.apikey_entry.get())
|
||||||
configSet(["allow_self_signed"], bool(self.self_signed_check_bool.get()))
|
configSet(["allow_self_signed"], bool(self.self_signed_check_bool.get()))
|
||||||
configSet(["saves_location"], self.saves_location_entry.get())
|
configSet(["saves_location"], self.saves_location_entry.get())
|
||||||
@ -152,6 +184,11 @@ class FrameSettings(ThemedFrame):
|
|||||||
|
|
||||||
def validate_configuration(self):
|
def validate_configuration(self):
|
||||||
|
|
||||||
|
if self.name_entry.get().strip() == "":
|
||||||
|
logger.error(f"Name {self.name_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
|
||||||
|
|
||||||
if len(self.address_entry.get()) > 0:
|
if len(self.address_entry.get()) > 0:
|
||||||
if self.address_entry.get().endswith("/"):
|
if self.address_entry.get().endswith("/"):
|
||||||
self.address_text = self.address_entry.get()
|
self.address_text = self.address_entry.get()
|
||||||
@ -185,11 +222,15 @@ class FrameSettings(ThemedFrame):
|
|||||||
messagebox.showerror(title="Location error", message="Saves folder seems to be invalid. Please provide a valid directory path where Stardew Valley's save files (and folders) are stored.")
|
messagebox.showerror(title="Location error", message="Saves folder seems to be invalid. Please provide a valid directory path where Stardew Valley's save files (and folders) are stored.")
|
||||||
return
|
return
|
||||||
|
|
||||||
configSet(["address"], self.address_entry.get())
|
# =========================
|
||||||
configSet(["apikey"], self.apikey_entry.get())
|
if self.name_entry.get().strip() != configGet("name"):
|
||||||
configSet(["allow_self_signed"], bool(self.self_signed_check_bool.get()))
|
existing_device_after = requests.get(f'{self.address_entry.get()}/devices/{self.name_entry.get().strip()}', headers={"apikey": self.apikey_entry.get()}, verify=not bool(self.self_signed_check_bool.get()))
|
||||||
configSet(["saves_location"], self.saves_location_entry.get())
|
if existing_device_after == 200:
|
||||||
|
logger.error(f"Device with name {self.name_entry.get().strip()} already exists")
|
||||||
|
messagebox.showerror(title="Name error", message=f"Device with name '{self.name_entry.get().strip()}' already exists on the server. Please choose another name or rename that device first.")
|
||||||
|
return
|
||||||
|
# =========================
|
||||||
|
|
||||||
self.save_button.state(["!disabled"])
|
self.save_button.state(["!disabled"])
|
||||||
|
|
||||||
messagebox.showinfo(title="Configuration completed", message="Your client is now configured and ready to use!")
|
# messagebox.showinfo(title="Configuration completed", message="Your client is now configured and ready to use!")
|
Loading…
Reference in New Issue
Block a user