diff --git a/classes/app.py b/classes/app.py index 88b18c4..9fed808 100644 --- a/classes/app.py +++ b/classes/app.py @@ -9,7 +9,7 @@ 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, FrameErrorFirstStart, FrameErrorSavesFolder, FrameErrorUnconfigured -from classes.frames.saves import FrameSaves +from classes.frames.saves import FrameSaves, FrameSavesEmpty from classes.frames.settings import FrameSettings from classes.toplevel.welcome import ToplevelWelcome from modules.theme_titlebar import theme_title_bar @@ -25,11 +25,11 @@ class App(ThemedTk): self.__version__ = "0.1.0" resize_window(self, 610, 400) + self.resizable(False, True) + self.minsize(610, 200) self.title("Stardew Sync") - self.resizable(False, True) - sv_ttk.init_theme(self) if use_dark_mode(): @@ -63,7 +63,11 @@ class App(ThemedTk): 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_saves = requests.get(f'{configGet("address")}/saves?only_ids=True', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed")) + if self.frame_saves_saves.status_code == 200 and isinstance(self.frame_saves_saves.json(), list) is True and len(self.frame_saves_saves.json()) > 0: + self.frame_saves_object = FrameSaves(self, self.frame_saves_saves.json(), vscroll=True) + else: + self.frame_saves_object = FrameSavesEmpty(self) self.frame_saves_object.grid(column=1, row=0, sticky=NSEW) return @@ -73,12 +77,8 @@ class App(ThemedTk): 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 diff --git a/classes/frames/device.py b/classes/frames/device.py new file mode 100644 index 0000000..4821777 --- /dev/null +++ b/classes/frames/device.py @@ -0,0 +1,148 @@ +import platform +from datetime import datetime +from functools import partial +from tkinter import LEFT, NSEW, E, Misc, W, messagebox, ttk +from tkinter.messagebox import askyesno +from urllib.parse import quote, urlencode + +import requests + +from classes.custom.themed_frame import ThemedFrame +from modules.logger import logger +from modules.utils import configGet, configSet + + +class FrameDevice(ThemedFrame): + + def __init__(self, master: Misc, device_dict: str, **kwargs) -> None: + + super().__init__(master, style="Card.TFrame", **kwargs) + + 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=("SunValleyBodyStrongFont", 12, "bold"), 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"): + 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=("SunValleyBodyStrongFont", 12, "bold"), 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=("SunValleyBodyStrongFont", 12, "bold"), 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() \ No newline at end of file diff --git a/classes/frames/devices.py b/classes/frames/devices.py index 3a0d500..4ed29f9 100644 --- a/classes/frames/devices.py +++ b/classes/frames/devices.py @@ -1,159 +1,10 @@ -from datetime import datetime -from functools import partial -import platform -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 +from tkinter import NSEW, ttk -import requests from ttkthemes import ThemedTk from classes.custom.scrollable_frame import ScrollableFrame from classes.custom.themed_frame import ThemedFrame -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() +from classes.frames.device import FrameDevice class FrameDevices(ScrollableFrame): @@ -164,14 +15,8 @@ class FrameDevices(ScrollableFrame): 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() @@ -183,36 +28,6 @@ class FrameDevices(ScrollableFrame): device_frame = FrameDevice(self, device_dict=device) 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_action = partial(self.device_rename, device_frame, buttons_frame, device["name"]) - # 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 = 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 @@ -229,9 +44,6 @@ class FrameDevicesEmpty(ThemedFrame): master.title("Devices - Stardew Sync") - # self["borderwidth"] = 1 - # self["relief"] = "solid" - self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=2) diff --git a/classes/frames/save.py b/classes/frames/save.py new file mode 100644 index 0000000..307d595 --- /dev/null +++ b/classes/frames/save.py @@ -0,0 +1,37 @@ +from datetime import datetime, timezone +from tkinter import LEFT, NSEW, Misc, S, W, ttk + +from classes.custom.themed_frame import ThemedFrame + + +class FrameSave(ThemedFrame): + + def __init__(self, master: Misc, save_dict: str, **kwargs) -> None: + + super().__init__(master, style="Card.TFrame", **kwargs) + + self.grid_columnconfigure(0, weight=1) + self.grid_columnconfigure(1, weight=3) + self.grid_columnconfigure(2, weight=3) + + self.title = ttk.Label(self, text=save_dict["data"]["farmer"], font=("SunValleyBodyStrongFont", 12, "bold"), justify=LEFT, width=46) + self.title.grid(column=0, row=0, padx=9, pady=9, sticky=W) + + self.description = ttk.Label(self, text=f'Money: {save_dict["data"]["money"]}\nGame version: {save_dict["data"]["game_version"]}\nID: {save_dict["id"]}', 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) + + upload_date = datetime.utcfromtimestamp(save_dict["date"]).replace(tzinfo=timezone.utc).astimezone(tz=None) + self.last_upload = ttk.Label(self.buttons, text=f'{upload_date.strftime("%A, %d %b %Y")}\nUploaded at {upload_date.strftime("%H:%M")} by {save_dict["device"]}', font=("SunValleyBodyFont", 8), justify=LEFT, width=46) + self.last_upload.grid(column=0, row=0, sticky=W+S) + + # 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="Synchronize", style="Accent.TButton", width=11) #, command=self.button_device_delete_action) + self.button_device_delete.grid(column=1, row=0, sticky=W) \ No newline at end of file diff --git a/classes/frames/saves.py b/classes/frames/saves.py index b0c53af..cfb712c 100644 --- a/classes/frames/saves.py +++ b/classes/frames/saves.py @@ -1,11 +1,81 @@ -from tkinter import EW, NS, ttk +from os import path, walk +from tkinter import NSEW, ttk +import xmltodict from ttkthemes import ThemedTk +from classes.custom.scrollable_frame import ScrollableFrame from classes.custom.themed_frame import ThemedFrame +from classes.frames.save import FrameSave +from modules.utils import configGet -class FrameSaves(ThemedFrame): +class FrameSaves(ScrollableFrame): + + def __init__(self, master: ThemedTk, saves: list, **kwargs) -> None: + + super().__init__(master, **kwargs) + + master.title("Saves - Stardew Sync") + + self.saves = saves + self.saves_local = [] + + for subdir, dirs, files in walk(configGet("saves_location")): + try: + for dir in dirs: + with open(path.join(dir, "SaveGameInfo"), "r", encoding="utf-8") as file: + save_dict = xmltodict.parse(file.read()) + self.saves_local.append( + { + "id": int(save_dict["SaveGame"]["uniqueIDForThisGame"]), + "user": None, + "device": configGet("name"), + "date": None, + "data": { + "farmer": save_dict["Farmer"]["name"], + "money": int(save_dict["Farmer"]["money"]), + "played": int(save_dict["Farmer"]["millisecondsPlayed"]), + "save_time": int(save_dict["Farmer"]["saveTime"]), + "year": int(save_dict["Farmer"]["yearForSaveGame"]), + "season": int(save_dict["Farmer"]["seasonForSaveGame"]), + "day": int(save_dict["Farmer"]["dayOfMonthForSaveGame"]), + "game_version": save_dict["Farmer"]["gameVersion"] + }, + "file": { + "name": None, + "uuid": None, + "path": None + } + } + ) + except: + pass + + # Merge local and remote saves. + # Maybe add something that indicates availability of an + # remote update to pull or local new version to push. + + master.columnconfigure(1, weight=1) + + self.render_saves() + + def render_saves(self): + + i = 0 + for save in self.saves: + + save_frame = FrameSave(self, save_dict=save) + save_frame.grid(column=0, row=i, pady=9, padx=9, sticky=NSEW) + + i += 1 + + if i+1 != len(self.saves): + divider = ttk.Separator(self, orient="horizontal") + divider.grid(column=0, row=i+1, pady=9) + i += 1 + +class FrameSavesEmpty(ThemedFrame): def __init__(self, master: ThemedTk, **kwargs) -> None: @@ -13,13 +83,10 @@ class FrameSaves(ThemedFrame): master.title("Saves - Stardew Sync") - self["borderwidth"] = 2 - self["relief"] = "solid" + self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(0, weight=2) - self.inside_frame = ThemedFrame(self) + master.columnconfigure(1, weight=1) - for btn in range(0, 10): - ttk.Button(self.inside_frame, text="SAVES "+str(btn)).grid(column=0, row=btn, padx=9, pady=9, sticky=EW) - - self.scrollbar = ttk.Scrollbar(self.inside_frame, orient="vertical") - self.scrollbar.grid(column=1, row=0, sticky=NS) \ No newline at end of file + self.label = ttk.Label(self, text="No saves found") + self.label.grid(column=0, row=0, padx=9, pady=9) \ No newline at end of file diff --git a/main.py b/main.py index 359e0d1..c0b9443 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import locale from os import path from classes.app import App from modules.utils import jsonSave @@ -16,6 +17,8 @@ if not path.exists("config.json"): }, "config.json" ) + +locale.setlocale(locale.LC_ALL, '') if __name__ == "__main__": diff --git a/requirements.txt b/requirements.txt index a02ec55..3f83963 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ darkdetect~=0.8.0 +xmltodict~=0.13.0 ttkthemes~=3.2.2 requests~=2.28.2 sv_ttk~=2.4 \ No newline at end of file