From 6add3ac97634a3a454e097862bce9fbd3dbb9062 Mon Sep 17 00:00:00 2001 From: profitroll Date: Tue, 24 Jan 2023 15:27:07 +0100 Subject: [PATCH] WIP: Welcome dialog added --- classes/app.py | 9 +- classes/frames/errors.py | 20 +++ classes/frames/settings.py | 96 ++++++------- classes/toplevel/welcome.py | 265 ++++++++++++++++++++++++++++++++++-- 4 files changed, 329 insertions(+), 61 deletions(-) diff --git a/classes/app.py b/classes/app.py index 02ae128..88b18c4 100644 --- a/classes/app.py +++ b/classes/app.py @@ -8,7 +8,7 @@ from ttkthemes import ThemedTk 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.errors import FrameErrorConnection, FrameErrorFirstStart, FrameErrorSavesFolder, FrameErrorUnconfigured from classes.frames.saves import FrameSaves from classes.frames.settings import FrameSettings from classes.toplevel.welcome import ToplevelWelcome @@ -119,6 +119,13 @@ class App(ThemedTk): self.item_settings = ttk.Button(self.frame_sidebar, text="Settings", width=10, command=self.frame_settings) self.item_settings.grid(column=0, row=2, sticky=W+S, padx=9, pady=9) + if configGet("first_run") is True: + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(1, weight=1) + self.connection_error = FrameErrorFirstStart(self) + self.connection_error.grid(column=1, row=0, rowspan=2, sticky=N+S+W+E) + return + if configGet("address") in ["", None] or configGet("apikey") in ["", None]: self.item_saves.state(["disabled"]) self.item_devices.state(["disabled"]) diff --git a/classes/frames/errors.py b/classes/frames/errors.py index b2cee6a..2076be1 100644 --- a/classes/frames/errors.py +++ b/classes/frames/errors.py @@ -46,6 +46,26 @@ class FrameErrorConnection(ThemedFrame): self.button_settings.grid(column=1, row=0, padx=9, pady=9) +class FrameErrorFirstStart(ThemedFrame): + + def __init__(self, master: ThemedTk, **kwargs) -> None: + + super().__init__(master, **kwargs) + + self.grid_columnconfigure(0, weight=1) + + self.grid_rowconfigure(0, weight=2) + self.grid_rowconfigure(1, weight=2) + + master.columnconfigure(1, weight=1) + + self.label = ttk.Label(self, text="Setup completed") + self.label.grid(column=0, row=0, padx=9, pady=9, sticky=S) + + self.button_settings = ttk.Button(self, text="Refresh", style="Accent.TButton", width=10, command=lambda:try_connecting(master)) + self.button_settings.grid(column=0, row=1, sticky=N) + + class FrameErrorSavesFolder(ThemedFrame): def __init__(self, master: ThemedTk, **kwargs) -> None: diff --git a/classes/frames/settings.py b/classes/frames/settings.py index eeab385..72b41f9 100644 --- a/classes/frames/settings.py +++ b/classes/frames/settings.py @@ -27,33 +27,33 @@ class FrameSettings(ThemedFrame): 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_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())) + # 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 self.address_label = ttk.Label(self, text="Address:") - self.address_label.grid(column=0, row=1, sticky=W, padx=9, pady=9) + self.address_label.grid(column=0, row=0, sticky=W, padx=9, pady=9) self.address_entry = ttk.Entry(self) - self.address_entry.grid(column=1, row=1, sticky=N+S+E+W, padx=9, pady=9) + self.address_entry.grid(column=1, row=0, sticky=N+S+E+W, padx=9, pady=9) if configGet("address") is not None: self.address_entry.insert(0, configGet("address")) # ======= # API Key self.apikey_label = ttk.Label(self, text="API key:") - self.apikey_label.grid(column=0, row=2, sticky=W, padx=9, pady=9) + self.apikey_label.grid(column=0, row=1, sticky=W, padx=9, pady=9) self.apikey_entry = ttk.Entry(self) - self.apikey_entry.grid(column=1, row=2, sticky=N+S+E+W, padx=9, pady=9) + self.apikey_entry.grid(column=1, row=1, sticky=N+S+E+W, padx=9, pady=9) if configGet("apikey") is not None: self.apikey_entry.insert(0, configGet("apikey")) # ======= @@ -64,15 +64,15 @@ class FrameSettings(ThemedFrame): else: 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.grid(column=1, row=3, sticky=W, padx=9, pady=9) + self.self_signed_check.grid(column=1, row=2, sticky=W, padx=9, pady=9) # =========== # Saves location self.saves_location_label = ttk.Label(self, text="Saves location:") - self.saves_location_label.grid(column=0, row=4, sticky=W, padx=9, pady=9) + self.saves_location_label.grid(column=0, row=3, sticky=W, padx=9, pady=9) self.saves_frame = ThemedFrame(self) - self.saves_frame.grid(column=1, row=4, sticky=N+S+E+W, padx=9, pady=9) + self.saves_frame.grid(column=1, row=3, sticky=N+S+E+W, padx=9, pady=9) self.saves_frame.grid_columnconfigure(0, weight=1) self.saves_frame.grid_columnconfigure(1, weight=3) @@ -85,9 +85,9 @@ class FrameSettings(ThemedFrame): self.saves_location_button.grid(column=1, row=0, sticky=E) # ============== - # Saves location + # Software theme self.saves_location_label = ttk.Label(self, text="Color theme:") - self.saves_location_label.grid(column=0, row=5, sticky=W, padx=9, pady=9) + self.saves_location_label.grid(column=0, row=4, sticky=W, padx=9, pady=9) if configGet("dark_mode_auto") is True: self.default_theme = "Auto " @@ -97,11 +97,11 @@ class FrameSettings(ThemedFrame): self.chosen_theme = StringVar() self.themes = ("Auto ", "Light ", "Dark ") self.saves_location_button = ttk.OptionMenu(self, self.chosen_theme, self.default_theme, *self.themes, direction="below", command=self.change_theme) - self.saves_location_button.grid(column=1, row=5, sticky=W, padx=9, pady=9) + self.saves_location_button.grid(column=1, row=4, sticky=W, padx=9, pady=9) # ============== self.buttons_frame = ThemedFrame(self) - self.buttons_frame.grid(column=0, columnspan=2, row=6, sticky=NSEW, padx=9, pady=9) + self.buttons_frame.grid(column=0, columnspan=2, row=5, sticky=NSEW, padx=9, pady=9) self.buttons_frame.grid_columnconfigure(0, weight=1) self.validate_button = ttk.Button(self.buttons_frame, text="Validate", width=11, command=self.validate_configuration) @@ -143,24 +143,24 @@ class FrameSettings(ThemedFrame): else: self.address_text = None - # ========================= - 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 - # ========================= + # # ========================= + # 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(["name"], self.name_entry.get().strip()) configSet(["address"], self.address_entry.get()) configSet(["apikey"], self.apikey_entry.get()) configSet(["allow_self_signed"], bool(self.self_signed_check_bool.get())) @@ -184,10 +184,10 @@ class FrameSettings(ThemedFrame): 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 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 self.address_entry.get().endswith("/"): @@ -222,14 +222,14 @@ 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.") return - # ========================= - if self.name_entry.get().strip() != configGet("name"): - 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())) - 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 - # ========================= + # # ========================= + # if self.name_entry.get().strip() != configGet("name"): + # 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())) + # 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"]) diff --git a/classes/toplevel/welcome.py b/classes/toplevel/welcome.py index 6d1e7d0..12266d0 100644 --- a/classes/toplevel/welcome.py +++ b/classes/toplevel/welcome.py @@ -1,5 +1,7 @@ -from os import path -from tkinter import CENTER, END, E, LEFT, NSEW, Image, IntVar, N, S, PhotoImage, Toplevel, W, messagebox, ttk +from os import getenv, path +import platform +from tkinter import CENTER, END, E, LEFT, NSEW, Image, IntVar, N, S, PhotoImage, StringVar, Toplevel, W, filedialog, messagebox, ttk +from urllib.parse import quote, urlencode import requests import sv_ttk @@ -41,8 +43,8 @@ class ToplevelWelcome(ThemedToplevel): 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["borderwidth"] = 1 + # self.window_frame["relief"] = "solid" self.window_frame.grid_columnconfigure(0, weight=1) @@ -69,22 +71,261 @@ class ToplevelWelcome(ThemedToplevel): #self.window_frame.grid_rowconfigure(2, weight=3) self.stage_label = ttk.Label(self.window_frame, text="Connection", font=("SunValleyBodyFont", 14)) - self.stage_label.grid(column=0, row=0, pady=40) + self.stage_label.grid(column=0, row=0, pady=29) self.stage_label_1 = ttk.Label(self.window_frame, text="Server address", justify=LEFT) - self.stage_label_1.grid(column=0, row=1, pady=5, padx=35, sticky=W) + self.stage_label_1.grid(column=0, row=1, padx=35, pady=5, sticky=W) self.stage_entry_1 = ttk.Entry(self.window_frame) - self.stage_entry_1.grid(column=0, row=2, pady=5, padx=35, sticky=W+E) + self.stage_entry_1.grid(column=0, row=2, padx=35, pady=5, sticky=W+E) - self.stage_label_1 = ttk.Label(self.window_frame, text="Personal API key", justify=LEFT) - self.stage_label_1.grid(column=0, row=3, pady=5, padx=35, sticky=W) + self.stage_label_2 = ttk.Label(self.window_frame, text="Personal API key", justify=LEFT) + self.stage_label_2.grid(column=0, row=3, padx=35, pady=5, sticky=W) + + self.stage_entry_2 = ttk.Entry(self.window_frame) + self.stage_entry_2.grid(column=0, row=4, padx=35, pady=5, sticky=W+E) + + self.stage_checkbox_var = IntVar() + self.stage_checkbox = ttk.Checkbutton(self.window_frame, text="Allow self-signed certificates", variable=self.stage_checkbox_var) + self.stage_checkbox.grid(column=0, row=5, sticky=W, padx=35, pady=9) + + self.stage_button = ttk.Button(self.window_frame, text="Continue", style="Accent.TButton", width=10, command=self.stage_address_validate) + self.stage_button.grid(column=0, row=6, pady=28) + + def stage_address_validate(self): + + if len(self.stage_entry_1.get()) > 0: + if self.stage_entry_1.get().endswith("/"): + self.address_text = self.stage_entry_1.get() + self.stage_entry_1.delete(0, END) + self.stage_entry_1.insert(0, self.address_text[:-1]) + + try: + requests.get(self.stage_entry_1.get()+"/check", verify=not bool(self.stage_checkbox_var.get())) + 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}") + 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") + return + except (requests.exceptions.SSLError): + logger.error(f"SSL certificate of '{self.stage_entry_1.get()}' does not seem to be valid or is self-signed") + messagebox.showerror(title="Invalid SSL", message="SSL certificate seems to be invalid or self-signed and thus won't be trusted. You can overwrite this by checking 'Allow self-signed certificates'.") + return + except Exception as exp: + logger.error(f"Could not reach '{self.stage_entry_1.get()}' due to {exp}") + messagebox.showerror(title="Connection error", message="Address entered does not seem to be reachable. Please make sure you've entered the correct API address.") + return + + response_apikey = requests.get(self.stage_entry_1.get()+"/apikey", headers={"apikey": self.stage_entry_2.get()}, verify=not bool(self.stage_checkbox_var.get())) + + if response_apikey.status_code != 200: + logger.error(f"API key seems to be invalid. API returned {response_apikey.status_code} as a status code") + messagebox.showerror(title="Invalid apikey", message="API key provided does not seem to be valid. Please check for any mistakes and if none found - take a look at the docs to learn how to generate one.") + return + + configSet(["address"], self.stage_entry_1.get()) + configSet(["apikey"], self.stage_entry_2.get()) + configSet(["allow_self_signed"], bool(self.stage_checkbox_var.get())) + + self.stage_name() + + def stage_name(self): + + for widget in self.window_frame.winfo_children(): + widget.destroy() + + self.window_frame.grid_rowconfigure(0, weight=2) + self.window_frame.grid_rowconfigure(1, weight=2) + + self.stage_label = ttk.Label(self.window_frame, text="Connection", font=("SunValleyBodyFont", 14)) + self.stage_label.grid(column=0, row=0, pady=29) + + self.divider_1 = ttk.Separator(self.window_frame) + self.divider_1.grid(column=0, row=1, pady=15) + + self.stage_label_1 = ttk.Label(self.window_frame, text="Device name", justify=LEFT) + self.stage_label_1.grid(column=0, row=2, padx=35, pady=5, sticky=W) self.stage_entry_1 = ttk.Entry(self.window_frame) - self.stage_entry_1.grid(column=0, row=4, pady=5, padx=35, sticky=W+E) + self.stage_entry_1.grid(column=0, row=3, padx=35, pady=5, sticky=W+E) + self.stage_entry_1.insert(0, str(platform.node())) - self.stage_button = ttk.Button(self.window_frame, text="Continue", style="Accent.TButton", width=10) - self.stage_button.grid(column=0, row=5, pady=40) + self.divider_2 = ttk.Separator(self.window_frame) + self.divider_2.grid(column=0, row=4, pady=40) + + self.stage_button = ttk.Button(self.window_frame, text="Continue", style="Accent.TButton", width=10, command=self.stage_name_validate) + self.stage_button.grid(column=0, row=5, pady=28) + + def stage_name_validate(self): + + if self.stage_entry_1.get().strip() == "": + logger.error(f"Name {self.stage_entry_1.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.stage_entry_1.get().strip()) + except: + logger.error(f"Name {self.stage_entry_1.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 self.stage_entry_1.get().strip() == configGet("name"): + existing_device = requests.get(f'{configGet("address")}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed")) + if existing_device.status_code != 200: + requests.post(f'{configGet("address")}/devices?{urlencode({"name": quote(configGet("name").encode("utf-8")), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.__version__}"})}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed")) + else: + if configGet("name") is not None: + existing_device_before = requests.get(f'{configGet("address")}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed")) + if existing_device_before.status_code == 200: + requests.patch(f'{configGet("address")}?{urlencode({"new_name": quote(self.stage_entry_1.get().strip().encode("utf-8")), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.__version__}"})}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed")) + else: + device_created = requests.post(f'{configGet("address")}/devices?{urlencode({"name": quote(self.stage_entry_1.get().strip().encode("utf-8")), "os": platform.system()+" "+platform.release(), "client": f"SyncTk {self.master.__version__}"})}', headers={"apikey": configGet("apikey")}, verify=not configGet("allow_self_signed")) + if device_created.status_code != 204: + messagebox.showerror(title="Name error", message=f"Could not register device in database using name '{self.stage_entry_1.get().strip()}' with error:\n\n{device_created.json()}") + return + + configSet(["name"], self.stage_entry_1.get().strip()) + + self.stage_location() + + def stage_location(self): + + for widget in self.window_frame.winfo_children(): + widget.destroy() + + self.window_frame.grid_rowconfigure(0, weight=2) + self.window_frame.grid_rowconfigure(1, weight=2) + + self.stage_label = ttk.Label(self.window_frame, text="Local saves", font=("SunValleyBodyFont", 14)) + self.stage_label.grid(column=0, row=0, pady=29) + + self.divider_1 = ttk.Separator(self.window_frame) + self.divider_1.grid(column=0, row=1, pady=15) + + + self.stage_label_1 = ttk.Label(self.window_frame, text="Save files location", justify=LEFT) + self.stage_label_1.grid(column=0, row=2, padx=35, pady=5, sticky=W) + + self.stage_frame = ThemedFrame(self.window_frame) + self.stage_frame.grid(column=0, row=3, padx=35, pady=5, sticky=W+E) + self.stage_frame.grid_columnconfigure(0, weight=2) + # self.saves_frame.grid_columnconfigure(1, weight=3) + + self.stage_entry_1 = ttk.Entry(self.stage_frame) + self.stage_entry_1.grid(column=0, row=0, sticky=W+E) + + self.divider_2 = ttk.Separator(self.stage_frame, orient="vertical") + self.divider_2.grid(column=1, row=0, padx=3) + + self.saves_location_button = ttk.Button(self.stage_frame, text="Browse", width=6, command=lambda:self.stage_location_select_location(self.stage_entry_1)) + self.saves_location_button.grid(column=2, row=0, sticky=E) + + + self.divider_3 = ttk.Separator(self.window_frame) + self.divider_3.grid(column=0, row=4, pady=40) + + self.stage_button = ttk.Button(self.window_frame, text="Continue", style="Accent.TButton", width=10, command=self.stage_location_validate) + self.stage_button.grid(column=0, row=5, pady=26) + + def stage_location_select_location(self, entry: ttk.Entry): + + if path.exists(path.join(str(getenv("APPDATA")), "Stardew Valley")): + self.path_start = path.join(str(getenv("APPDATA")), "Stardew Valley") + elif path.exists(path.join(path.expanduser("~"), ".config", "Stardew Valley")): + self.path_start = path.join(path.expanduser("~"), ".config", "Stardew Valley") + else: + self.path_start = None + + self.path_dir = filedialog.askdirectory(initialdir=self.path_start, title="Select Stardew Valley Saves folder") + + if self.path_dir != "": + entry.delete(0, END) + entry.insert(0, self.path_dir) + + def stage_location_validate(self): + + if not path.exists(self.stage_entry_1.get()): + logger.error(f"Path {self.stage_entry_1.get()} does not seem to exist") + 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 + + configSet(["saves_location"], self.stage_entry_1.get()) + + self.stage_theme() + + def stage_theme(self): + + for widget in self.window_frame.winfo_children(): + widget.destroy() + + self.window_frame.grid_rowconfigure(0, weight=2) + self.window_frame.grid_rowconfigure(1, weight=2) + + self.stage_label = ttk.Label(self.window_frame, text="Almost there", font=("SunValleyBodyFont", 14)) + self.stage_label.grid(column=0, row=0, pady=29) + + self.divider_1 = ttk.Separator(self.window_frame) + self.divider_1.grid(column=0, row=1, pady=15) + + self.stage_label_1 = ttk.Label(self.window_frame, text="Theme", justify=CENTER) + self.stage_label_1.grid(column=0, row=2, padx=35, pady=5) + + self.stage_option_menu_var = StringVar() + self.themes = ("Auto ", "Light ", "Dark ") + self.stage_option_menu = ttk.OptionMenu(self.window_frame, self.stage_option_menu_var, "Auto ", *self.themes, direction="below", command=self.change_theme) + self.stage_option_menu.grid(column=0, row=3, padx=35, pady=5) + + self.divider_2 = ttk.Separator(self.window_frame) + self.divider_2.grid(column=0, row=4, pady=40) + + self.stage_button = ttk.Button(self.window_frame, text="Continue", style="Accent.TButton", width=10, command=self.stage_theme_validate) + self.stage_button.grid(column=0, row=5, pady=28) + + def change_theme(self, *args): + + if self.stage_option_menu_var.get().strip().lower() == "auto": + self.stage_option_menu_var_real = "dark" if use_dark_mode(no_config=True) is True else "light" + else: + self.stage_option_menu_var_real = self.stage_option_menu_var.get().strip().lower() + theme_title_bar(self, self.stage_option_menu_var_real) + theme_title_bar(self.master, self.stage_option_menu_var_real) + + def stage_theme_validate(self): + + if self.stage_option_menu_var.get().strip().lower() == "auto": + configSet(["dark_mode_auto"], True) + else: + configSet(["dark_mode_auto"], False) + if self.stage_option_menu_var.get().strip().lower() == "dark": + configSet(["dark_mode"], True) + else: + configSet(["dark_mode"], False) + + self.stage_mobile_app() + + def stage_mobile_app(self): + # To Do + self.stage_completed() + + def stage_completed(self): + + for widget in self.window_frame.winfo_children(): + widget.destroy() + + self.window_frame.grid_columnconfigure(0, weight=1) + + self.welcome_pic = ImageLabel(self.window_frame) + self.welcome_pic.grid(column=0, row=0, pady=20) + self.welcome_pic.load(path.join("assets", "welcome.gif")) + + self.welcome_text = ttk.Label(self.window_frame, text="You're all set!", font=("SunValleyBodyFont", 14)) + self.welcome_text.grid(column=0, row=1, pady=10) + + self.welcome_subtext = ttk.Label(self.window_frame, text="Configuration step is completed.\nYou can now jump into app and enjoy your\nsave files synchronization", justify=CENTER) + self.welcome_subtext.grid(column=0, row=2) + + self.welcome_button = ttk.Button(self.window_frame, text="Finish", style="Accent.TButton", width=10, command=self.acknowledged) + self.welcome_button.grid(column=0, row=3, pady=20) def acknowledged(self): configSet(["first_run"], False)