From 8f3b1176cd4273ba2443968f82db325afc78e910 Mon Sep 17 00:00:00 2001 From: Neuffexx Date: Wed, 13 Sep 2023 20:30:47 +0300 Subject: [PATCH 1/6] - Ensured that Presets can no longer be created with the same name - Deleting a Preset will no longer Delete all presets of the same name (but they shouldnt have this issue anymore anyways) --- ProjectFiles/DisplayKeys-IS.py | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/ProjectFiles/DisplayKeys-IS.py b/ProjectFiles/DisplayKeys-IS.py index 8397d36..ca0fbd4 100644 --- a/ProjectFiles/DisplayKeys-IS.py +++ b/ProjectFiles/DisplayKeys-IS.py @@ -598,18 +598,6 @@ def __init__(self, parent: tk.Frame, widget_id: str, label_text: str = None, lab if dropdown_tooltip: self.d_tooltip = DisplayKeys_Tooltip(self.dropdown, dropdown_tooltip) - # TODO: - # 1.) Make dropdown update previewer when changing selections. - # Simply make dropdown selections change the values in the textboxes that will be taken anyways. - # Instead of manually checking for the dropdown selection in the Process_Image Function. - # You just take whatever is in the textboxes at all times, and have all dropdown selections only, - # update the textboxes based on 'saved' values from them (this will tie in nicely with presets)! - # ----- DONE ----- - # Still need dropdown selection for the time being - # ----- ----- - # 2.) Make generic so that dropdown button provides the list of WidgetID's its responsible for. - # Will make life easier for future dropdown functions as well (namely Presets etc.). - # Textbox - Mainly used for getting user input, but can also be used as a good place to dynamically show text # Takes: Default Text Value, Tooltip Text, State if has_textbox: @@ -1015,7 +1003,12 @@ def submit_preset(self): rows = int(rows_input.spinbox.get()) cols = int(cols_input.spinbox.get()) gap = int(gap_input.spinbox.get()) - ButtonFunctions.add_preset(name=name, rows=rows, cols=cols, gap=gap) + + if not any(preset.name == name for preset in app.presets): + ButtonFunctions.add_preset(name=name, rows=rows, cols=cols, gap=gap) + else: + PopUp_Dialogue(app.window, popup_type='error', message="Preset with this Name already exists!", + buttons=[{'OK': lambda: None}]) else: PopUp_Dialogue(self.popup, popup_type='error', message="Missing a Field!", buttons=[{'OK': lambda: None}]) @@ -1040,7 +1033,7 @@ def create_add_preset(self): self.confirm_button = tk.Button(self.button_container, text=" Confirm ", command=self.button_command_destructive(lambda: self.submit_preset())) self.confirm_button.grid(sticky="nsew", row=0, column=0) self.confirm_button.rowconfigure(0, weight=1) - self.cancel_button = tk.Button(self.button_container, text=" Cancel ", command=self.button_command_destructive(lambda:None)) + self.cancel_button = tk.Button(self.button_container, text=" Cancel ", command=self.button_command_destructive(lambda: None)) self.cancel_button.grid(sticky="nsew", row=0, column=1) self.cancel_button.rowconfigure(0, weight=1) @@ -1781,6 +1774,7 @@ def delete_preset(): if preset.name == current_preset: # delete the preset that currently exists... app.presets.remove(preset) + continue ButtonFunctions.populate_property_presets_options(app.properties, app.presets, reset_selection=True) # ----- Menu Bar: ----- @@ -1966,14 +1960,7 @@ def determine_split_type(file_path: str, output_dir: str, rows: int, cols: int, # Is not of a supported image format except TypeError as error_message: - # In the future simply open a Pop-Up Window with an error message: - # |----------------------------------------| - # | File Format not supported \n | - # | Currently supported formats are: \n | - # | - Image | get_supported_types()[0] | - # | - Animated | get_supported_types()[1] | - # |----------------------------------------| - PopUp_Dialogue(app.window, 'error', f"File Format not supporte\nCurrenlty supported formats are:\n- Image | {split.get_supported_types()[0]}\n- Animated | {split.get_supported_types()[1]}") + PopUp_Dialogue(app.window, 'error', f"File Format not supporte\nCurrenlty supported formats are:\n- Static | {split.get_supported_types()[0]}\n- Animated | {split.get_supported_types()[1]}") print("Wrong File Type: ", type(error_message).__name__, str(error_message)) return None From c4565776167b86286b91e26d2e4a07c75f1e0c6c Mon Sep 17 00:00:00 2001 From: Neuffexx <63469833+Neuffexx@users.noreply.github.com> Date: Sat, 30 Sep 2023 13:44:19 +0300 Subject: [PATCH 2/6] Update README.md --- README.md | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 18e6175..ba623b0 100644 --- a/README.md +++ b/README.md @@ -7,47 +7,49 @@ _Should work for any Display Pad, as it currently doesnt interface with any of t ## Why Make it? Well I needed a way be able to quickly setup images across different Display Keys. Dont get me wrong, there are PLENTY of image splitting tools out there hosted on websites even, so you dont have to download anything. -But none of them from what I found actually allow you to adjust anything other than Rows/Columns. - -And although the Mountain Everest addition, the Display Pad, does come with functionallity to make use of the whole screen to display a single image continiously. -It certainly doesnt when you are actually using it, as of the time of writing this (27/6/2023) it only supports this functionality when using a 'lockscreen' image. +But none of them from what I found actually allow you to adjust anything other than Rows/Columns. ## How do you use it? The only thing anyone probably cares to read on here, so let me make this simple: -- Download / Setup Instructions - - Download the DisplayKeys-IS_vX.X.X file, from the latest of the [Release](https://github.com/Neuffexx/DisplayKeys-IS/releases) page +- **Download / Setup Instructions** + - Download the DisplayKeys-IS_vX.X.X file, from the latest of the [Release page](https://github.com/Neuffexx/DisplayKeys-IS/releases) - Run > You may get a warning when first launching it, just click 'Run Anyway'. > _If you feel uncomfortable to download the .exe your free to go over the single code file that is the Application to ensure its safe (ProjectFiles/DisplayKeys-IS.py). Or the action that builds & publishes it (.github/workflows/release.yml)_ -- Usage Instructions (Or watch the [Usage Demonstration Video](https://youtu.be/D6juk_5pe5Q)) - - Select an Image to split, by selecting it via a 'Browse' button or Drag and Drop it onto the Text Field. - - Select the Save Location, in either of the same two methods. (Default save location if none is entered: _Desktop_) +- **Usage Instructions (Or watch the [Usage Demonstration Video | v0.3.0](https://youtu.be/D6juk_5pe5Q))** + - Select an Image to split + ('Browse' button / Drag and Drop) + - Select the Save Location + (Default save location if none is entered: _Desktop_) - (Optionally) Set Image Splitting Parameters - `Presets:` Will Split the image in a pre-saved combo of variables - - There you will find a Dropdown with the currently existing Presets to select from - - Initially will automatically select a 'Default' profile, with a 2x6 grid, using Spacing of **_40 (pixels)_**. - - Can `add` / `edit` / `delete` presets with the buttons found beneath the Dropdown + - Dropdown with the currently existing Presets + - Automatically 'Default' profile selected. (2x6 grid, Spacing **_40 (pixels)_**) + - `add` / `edit` / `delete` preset buttons beneath the Dropdown - To Import/Export Presets, go to `(Menu Bar) File>Presets` - - `User Defined:` Will let you manually enter the amount of Rows/Columns, and the Spacing between Image-Cells. + - `User Defined:` Will let you manually input the Parameters for Splitting Image-Cells. - Click the `Split Image` button. - Settings will use defaults for any input not given. - - Just assign them in your Display Keypad's Provided software. - And you're done :D + - Just assign in your Display Keypad's software. + _And you're done :D_ ## Whats Next? Well I plan to add/improve on the following (in no particular order): - Clean Up UI - +Make Preview more accurate to final result - Pop-up Windows need polish + - Visualize what is Drag n Drop interactible when dragging into the window - Improve Image Splitting Logic - - Add Percentage option to Gap input + - Add Percentage option to Gap input + _(Will allow for aspect ratio Presets, not bound to image resolution)_ - Automate Image Assignment to Profiles - ! BaseCamp Software ! SDK has been released, looking into it -- Add GIF support, for the Previewer - (Splitting functionality already supports it) -- Add Different Previewing Modes +- Previewer + - Add GIF support, for the Previewer + _(Splitting functionality already supports it)_ + - Add Different Previewing Modes
 
@@ -65,4 +67,4 @@ Well I plan to add/improve on the following (in no particular order):
 All Example Images that I used for testing can be found [here(placeholder)]().
 > (Includes the original's used along with the split version I found work best for me, for demonstration/experimentation purposes)
 ## Documentation
-Check out the [Documentation](https://github.com/Neuffexx/DisplayKeys-IS/blob/Development/DOCUMENTATION.md) if you want to contribute or simply want to know how stuff works!
+Check out the [Documentation (WIP)](https://github.com/Neuffexx/DisplayKeys-IS/blob/Development/DOCUMENTATION.md) if you want to contribute or simply want to know how stuff works!

From 8718e246778eb696b60640e02aa2e9c95aacb60f Mon Sep 17 00:00:00 2001
From: Neuffexx 
Date: Sun, 1 Oct 2023 00:44:32 +0300
Subject: [PATCH 3/6] - Moved all app referenced paths to one location to make
 it easier/quicker to change   - Added an alternative path for Packaged &
 Local usage

---
 ProjectFiles/DisplayKeys-IS.py | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/ProjectFiles/DisplayKeys-IS.py b/ProjectFiles/DisplayKeys-IS.py
index ca0fbd4..00fd344 100644
--- a/ProjectFiles/DisplayKeys-IS.py
+++ b/ProjectFiles/DisplayKeys-IS.py
@@ -27,6 +27,21 @@
 import webbrowser
 import json
 
+####################################################################################################################
+#                                                    App Paths
+####################################################################################################################
+
+# For Local Environments
+#sys_icon_img = "./assets/images/DisplayKeys-IS.ico"
+#sys_help_img = "./assets/images/Help.png"
+#sys_preview_img = "./assets/images/Preview.png"
+
+# For Packaging
+sys_icon_img = sys._MEIPASS + "./DisplayKeys-IS.ico"
+sys_help_img = sys._MEIPASS + "./Help.png"
+sys_preview_img = sys._MEIPASS + "./Preview.png"
+
+
 ####################################################################################################################
 #                                                    App Window
 ####################################################################################################################
@@ -43,7 +58,7 @@ def __init__(self):
         print("---Creating Window---")
         self.window = tkdnd.Tk()
         self.window.title("DisplayKeys-IS")
-        icon_path = sys._MEIPASS + "./DisplayKeys-IS.ico"
+        icon_path = sys_icon_img
         self.window.iconbitmap(icon_path)
         self.window.geometry("600x600")
         self.window.resizable(False, False)
@@ -295,7 +310,7 @@ def __init__(self, parent, width, height):
         # Initialize Image
         self.width = width
         self.height = height
-        self.placeholder_path = sys._MEIPASS + "./Preview.png"
+        self.placeholder_path = sys_preview_img
         self.image_path = None
 
         # Initialize canvas
@@ -1395,7 +1410,7 @@ def __init__(self, parent: tk.Frame, row: int = 0, col: int = 0, alignment: str
                  percentage_size: int = 100, help_tooltip: str = "Placeholder Help",
                  tooltip_justification: Literal["left", "center", "right"] = "center",
                  tooltip_anchor: Literal["nw", "n", "ne", "w", "center", "e", "sw", "s", "se"] = "center"):
-        self.image = Image.open(sys._MEIPASS + "./Help.png")
+        self.image = Image.open(sys_help_img)
         new_size = int( self.image.height * (percentage_size / 100) )
         self.resized_image = ImageTk.PhotoImage( self.image.resize((new_size, new_size)))
 
@@ -1580,7 +1595,7 @@ def process_image(widget_id: str):
             image_path = get_image_widget.textbox.get() if get_image_widget.textbox.get() else None
             output_dir = get_output_widget.textbox.get() if get_output_widget.textbox.get() else None
             if not image_path:
-                image_path = sys._MEIPASS + "./Preview.png"
+                image_path = sys_preview_img
 
                 # Disable Trace temporarily to not call this function again mid-execution
                 ButtonFunctions.disable_trace(get_image_widget.textbox_var, get_image_widget.textbox_trace)

From efb1758653b13a48e2f909c4afce67d91d5c2011 Mon Sep 17 00:00:00 2001
From: Neuffexx 
Date: Tue, 3 Oct 2023 11:57:37 +0300
Subject: [PATCH 4/6] -Added Name check for Edit Preset popup

---
 ProjectFiles/DisplayKeys-IS.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/ProjectFiles/DisplayKeys-IS.py b/ProjectFiles/DisplayKeys-IS.py
index a302a1b..9c57910 100644
--- a/ProjectFiles/DisplayKeys-IS.py
+++ b/ProjectFiles/DisplayKeys-IS.py
@@ -1145,7 +1145,11 @@ def submit_preset(self):
             gap = int(self.gap_input.spinbox.get())
 
             # Save edited Preset
-            ButtonFunctions.edit_preset(current_preset=self.current_preset, new_name=name, rows=rows, cols=cols, gap=gap)
+            if not any(preset.name == name for preset in app.presets):
+                ButtonFunctions.edit_preset(current_preset=self.current_preset, new_name=name, rows=rows, cols=cols, gap=gap)
+            else:
+                PopUp_Dialogue(app.window, popup_type='error', message="Preset with this Name already exists!",
+                               buttons=[{'OK': lambda: None}])
         else:
             PopUp_Dialogue(self.popup, popup_type='error', message="Missing a Field!", buttons=[{'OK': lambda: None}])
 

From 2aaa3b8ed2706862be767086cc7f1612cb586ac1 Mon Sep 17 00:00:00 2001
From: Neuffexx 
Date: Wed, 4 Oct 2023 13:48:15 +0300
Subject: [PATCH 5/6] - Started adding documentation (docstring) to
 class/functions. To be used on the Documentation page - Added Todo's for
 preferences

---
 ProjectFiles/DisplayKeys-IS.py | 90 ++++++++++++++++++++++++++++++----
 1 file changed, 80 insertions(+), 10 deletions(-)

diff --git a/ProjectFiles/DisplayKeys-IS.py b/ProjectFiles/DisplayKeys-IS.py
index 9c57910..6b55ef1 100644
--- a/ProjectFiles/DisplayKeys-IS.py
+++ b/ProjectFiles/DisplayKeys-IS.py
@@ -91,7 +91,7 @@ def __init__(self):
                                                help_tooltip="Previewer is not 100% Accurate!\n\nPreviewer Legend:\n  - Red Lines: Image Split\n  - Red Line Thickness: Gap\n  - Black Stipped: Cell Cropping",
                                                tooltip_justification="left", tooltip_anchor="center")
         # TODO: Add Results Widget's and populate content (ie. cell resolution, % of lost pixels?, etc.)
-        #       Also check if there is any actual meaningful information that can be added.
+        #       Also check if there is any actual meaningful information that can be shown.
         #self.preview_info = self.populate_column(self.preview_frame, self.get_preview_widgets())
         #self.previewer_info_help = DisplayKeys_Help(parent=self.preview_frame, row=10, alignment="se", percentage_size=40,
         #                                       help_tooltip="Further Information on the Results!")
@@ -112,6 +112,12 @@ def __init__(self):
     # Used to populate a column(Frame) with DisplayKeys_Composite_Widget's
     @staticmethod
     def populate_column(parent, widgets):
+        """
+            Adds [DisplayKeys_Composite_Widget]'s to a parent container.
+            :param parent: The Container to fill with Widgets
+            :param widgets: The list of widgets to add to the Parent
+        """
+
         created_widgets = []
         for widget in widgets:
             created_widgets.append(DisplayKeys_Composite_Widget(parent, **widget))
@@ -120,6 +126,10 @@ def populate_column(parent, widgets):
 
     @staticmethod
     def get_properties_widgets():
+        """
+            Returns an array of [DisplayKeys_Composite_Widget]'s, used to split Images.
+        """
+
         ToolProperties = [
             {
                 "widget_id": "Credits",
@@ -243,6 +253,10 @@ def get_properties_widgets():
 
     @staticmethod
     def get_preview_widgets():
+        """
+            Returns an array of [DisplayKeys_Composit_Widgets]'s and the [DisplayKeys_Previewer].
+            Used to Preview the changes done by the Property Widgets, along with some meaningful information.
+        """
         PreviewWidgets = [
             {
                 "widget_id": "PreviewDivider",
@@ -268,8 +282,16 @@ def get_preview_widgets():
 
         return PreviewWidgets
 
+    # TODO: Create Prefernces menu
+    #       - For now only to house colour settings for the Composite widgets and application backgrounds
+    #       - In the future also for Previewer colours, etc.
     # To keep the code more encapsulated and clean
     def create_menu_bar(self):
+        """
+            Creates the Main Window Menu Bar.
+            Will house Import/Export, Settings, Preferences, Help, etc. Menus.
+        """
+
         # Main Window Menu Bar
         self.menu_bar = Menu()
         self.window.configure(menu=self.menu_bar)
@@ -302,6 +324,7 @@ def run(self):
 class DisplayKeys_Previewer:
     """
         The Widget that show's all changes done to the Image within the Application.
+
         :param parent: The Widget Container holding this Previewer.
         :param width: The Width of the Previewer Canvas.
         :param height: The Height of the Previewer Canvas.
@@ -378,10 +401,13 @@ def display_preview_image(self, image_path):
         self.drag_data["x"] = x_offset
         self.drag_data["y"] = y_offset
 
-    # This calculates an approximate version of the split_image function,
-    # to preview the Splitting and Cropping of an image provided.
-    # Also calls the 'display_preview_image' to refresh the image.
     def update_preview(self, image_path, num_rows, num_columns, gap):
+        """
+            This calculates an approximate representation of the split_image function,
+            to preview the Splitting and Cropping of an image provided.
+            Also calls the 'display_preview_image' to refresh the image.
+        """
+
         # Clear the canvas to prepare for new content
         self.canvas.delete("all")
 
@@ -472,6 +498,10 @@ def update_preview(self, image_path, num_rows, num_columns, gap):
 
     # noinspection PyTypedDict
     def start_drag(self, event):
+        """
+            Record the position of the cursor.
+        """
+
         # record the item and its location
         self.drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
         self.drag_data["x"] = event.x
@@ -479,6 +509,10 @@ def start_drag(self, event):
         #print("Start Drag Position:", event.x, event.y)
 
     def end_drag(self, event):
+        """
+            Finalized the Drag event, storing / converting position coordinates, and calculating clamping.
+        """
+
         # Save the end position of the drag
         self.image_current_position["x"], self.image_current_position["y"] = self.canvas.coords(self.preview_image)
 
@@ -536,6 +570,10 @@ def end_drag(self, event):
         ButtonFunctions.process_image("DragPreviewImage")
 
     def do_drag(self, event):
+        """
+            Update the Preview Image to match the cursor position.
+        """
+
         # compute how much the mouse has moved
         delta_x = event.x - self.drag_data["x"]
         delta_y = event.y - self.drag_data["y"]
@@ -548,6 +586,10 @@ def do_drag(self, event):
 
     # Move the preview image back to its original position
     def reset_drag(self):
+        """
+            Set the preview image back to its original position.
+        """
+
         # Reset Position / Save
         self.canvas.coords(self.preview_image, self.image_reset_position["x"], self.image_reset_position["y"])
         self.image_current_position["x"], self.image_current_position["y"] = self.canvas.coords(self.preview_image)
@@ -566,6 +608,8 @@ def reset_drag(self):
 #       with Order being purely defined by the input array.
 #       However, it will for now always be in a fixed linear centered top-to-bottom layout, maybe I will come up
 #       with a way to work around that in the future. But not a priority for now.
+# TODO: Get colours to display from Preferences menu/popup
+
 # Generic Widgets used throughout the Applications UI (ie. Labels, Textboxes, Buttons, etc.)
 class DisplayKeys_Composite_Widget(tk.Frame):
     """
@@ -706,8 +750,10 @@ def __init__(self, parent: tk.Label | tk.Entry | tk.Spinbox | tk.Button | ttk.Co
         self.parent.bind("