From e15e337dbed8de66418cfa8b7957da502e352f60 Mon Sep 17 00:00:00 2001 From: your name Date: Fri, 11 Oct 2024 12:52:08 +0200 Subject: [PATCH] UIpdate image script --- api/woocommerce_api.py | 261 +++++++++--- config/encrypt_config.py | 143 ++++++- controller.py | 619 +++++++++++++++++++++++++++++ images/image-7.jpg | Bin 107471 -> 0 bytes main.py | 117 ++++-- main.spec | 19 +- speelgoed-config.enc | 1 + test-config.enc | 1 + ui/button_frame.py | 93 +++++ ui/frame_info.py | 60 +++ ui/images/all_products.png | Bin 0 -> 4115 bytes ui/images/cogs.png | Bin 0 -> 7070 bytes ui/images/directory.png | Bin 0 -> 4560 bytes ui/images/file.png | Bin 0 -> 5244 bytes ui/images/filters.png | Bin 0 -> 6548 bytes ui/images/house-user-solid.png | Bin 0 -> 6649 bytes ui/images/play.png | Bin 0 -> 3332 bytes ui/images/product.png | Bin 0 -> 5502 bytes ui/images/save.png | Bin 0 -> 4588 bytes ui/images/trash.png | Bin 0 -> 4127 bytes ui/images/wp_image.png | Bin 0 -> 17287 bytes ui/local_processing_tab.py | 423 -------------------- ui/{log_window.py => log_frame.py} | 14 +- ui/menu.py | 103 +++++ ui/preview_frame.py | 84 ++++ ui/settings_tab.py | 254 ++++++++++-- utils/file_operations.py | 71 ++-- utils/image_processing.py | 2 +- 28 files changed, 1655 insertions(+), 610 deletions(-) create mode 100644 controller.py delete mode 100644 images/image-7.jpg create mode 100644 speelgoed-config.enc create mode 100644 test-config.enc create mode 100644 ui/button_frame.py create mode 100644 ui/frame_info.py create mode 100644 ui/images/all_products.png create mode 100644 ui/images/cogs.png create mode 100644 ui/images/directory.png create mode 100644 ui/images/file.png create mode 100644 ui/images/filters.png create mode 100644 ui/images/house-user-solid.png create mode 100644 ui/images/play.png create mode 100644 ui/images/product.png create mode 100644 ui/images/save.png create mode 100644 ui/images/trash.png create mode 100644 ui/images/wp_image.png delete mode 100644 ui/local_processing_tab.py rename ui/{log_window.py => log_frame.py} (72%) create mode 100644 ui/menu.py create mode 100644 ui/preview_frame.py diff --git a/api/woocommerce_api.py b/api/woocommerce_api.py index 28bcb43..e88973d 100644 --- a/api/woocommerce_api.py +++ b/api/woocommerce_api.py @@ -1,24 +1,43 @@ -""" -Module for WooCommerce API interactions and image processing. -""" - import json import os import base64 import tempfile -import pprint -from tkinter import messagebox -from cryptography.fernet import Fernet import requests +from tkinter import messagebox from woocommerce import API +from cryptography.fernet import Fernet from utils.image_processing import ImageProcessor from config.encrypt_config import ConfigEncryptor from utils.file_operations import FileProcessor +import hashlib +import pprint CREDENTIALS_FILE = "credentials.json" # Hardcoded key (replace with your generated key) KEY = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" +def save_active_credential_set(active_set_name): + """ + Update the active credential set in the saved credentials file. + + Args: + active_set_name (str): The name of the active credential set. + """ + if not os.path.exists(CREDENTIALS_FILE): + return + + with open(CREDENTIALS_FILE, 'r+') as file: + data = json.load(file) + + # Find the credential set and mark it as active + for cred in data.get('credentials', []): + cred['active'] = (cred['name'] == active_set_name) + + # Rewrite the updated data back to the file + file.seek(0) + json.dump(data, file, indent=4) + file.truncate() + def save_credentials(url, consumer_key, consumer_secret, username, password): """ @@ -39,7 +58,7 @@ def save_credentials(url, consumer_key, consumer_secret, username, password): "password": password, } - ConfigEncryptor(KEY).save_credentials(credentials) + ConfigEncryptor(KEY).save_credentials(consumer_key, consumer_secret, username, password) def load_credentials(): @@ -49,37 +68,30 @@ def load_credentials(): Returns: dict: The decrypted credentials, or None if the file does not exist. """ - if not os.path.exists("config.enc"): - return None - fernet = Fernet(KEY) - with open("config.enc", "rb") as file: - encrypted = file.read() - decrypted = fernet.decrypt(encrypted).decode() - return json.loads(decrypted).get("credentials") + creds = ConfigEncryptor(KEY).load_credentials() + return creds def get_wcapi(): """ - Get a WooCommerce API client instance. + Get a WooCommerce API client instance using the active credentials. Returns: woocommerce.API: The WooCommerce API client instance, or None if credentials are missing. """ - credentials = load_credentials() - if not credentials: - messagebox.showerror( - "Error", - "No WooCommerce credentials found. Please set them in the settings.", - ) - return None + active_credentials = load_credentials() + + pprint.pprint(active_credentials) + return API( - url=credentials["url"], - consumer_key=credentials["consumer_key"], - consumer_secret=credentials["consumer_secret"], + url=active_credentials["url"], + consumer_key=active_credentials["consumer_key"], + consumer_secret=active_credentials["consumer_secret"], version="wc/v3", ) + def get_product(product_id): """ Get a WooCommerce product and download its images. @@ -95,8 +107,13 @@ def get_product(product_id): return None result = wcapi.get(f"products/{product_id}") - image_paths = {} + product = result.json() + + return product + +def get_images(product, limit = 0): + image_paths = {} if product.get("images"): images = product.get("images") @@ -116,14 +133,19 @@ def get_product(product_id): print( f"Image {index + 1}/{len(images)} downloaded and saved: {file_path}" ) + if limit and limit >= index +1: + break else: print(f"Failed to download image {index + 1}/{len(images)}") + + return image_paths + else: if product.get("name"): print(f"No images found for {product.get('name')}") else: print("No images found") - return image_paths, product + return [] def upload_image(img_path): @@ -139,6 +161,8 @@ def upload_image(img_path): with open(img_path, "rb") as img_file: data = img_file.read() file_name = os.path.basename(img_path) + file_name = file_name.replace("–", "-") + credentials = load_credentials() if not credentials: messagebox.showerror( @@ -155,7 +179,7 @@ def upload_image(img_path): "Content-Disposition": f"attachment; filename={file_name}", "Authorization": f"basic {credentials_base64.decode()}", } - + print(f"Uploading image {img_path}") try: res = requests.post(url=url, data=data, headers=headers, timeout=10) res.raise_for_status() @@ -180,6 +204,7 @@ def delete_img(image_id): Args: image_id (int): The ID of the image to delete. """ + credentials = load_credentials() if not credentials: messagebox.showerror( @@ -205,64 +230,114 @@ def delete_img(image_id): print(f"Failed to delete image with ID {image_id}. Error: {res.text}") -def update_product(image_ids, product_id): + + + + +def update_product(product_id, new_list, old_list, options): """ - Update a WooCommerce product with new image IDs. + Update the images and meta data of a WooCommerce product. Args: image_ids (list): A list of new image IDs. product_id (int): The ID of the WooCommerce product. """ + wcapi = get_wcapi() if not wcapi: return - product = wcapi.get(f"products/{product_id}").json() - product["images"] = [{"id": image_id} for image_id in image_ids] - response = wcapi.put(f"products/{product_id}", data=product) + # Prepare the data with images and meta data fields + product_data = { + "images": [{"id": image_id} for image_id in new_list], + "meta_data": [ + { + "key": "_image_processed", + "value": options['hash_string'] + }, + { + "key": "_old_image_ids", + "value": [{"id": image_id} for image_id in old_list] + } + + ] + } + + # Print product data for debugging + print(f"Updating product {product_id} with the following data:") + print(json.dumps(product_data, indent=2)) + + # Send the update request with images and meta data fields + response = wcapi.put(f"products/{product_id}", data=product_data) # Using 'json' to pass data + if response.status_code == 200: - print( - f"Product with ID {product_id} updated successfully with new image IDs.") + print(f"Product with ID {product_id} updated successfully with new image IDs and meta data.") else: - print( - f"Failed to update product with ID {product_id}. Error: {response.text}") + print(f"Failed to update product with ID {product_id}. Error: {response.text}") -def process_product_images( options): + +def process_product_images(options): """ Process images for a WooCommerce product by resizing and uploading them. Args: - product_id (int): The ID of the WooCommerce product. - name_template (str): The template for generating image filenames. - canvas_width (int): The width of the canvas for resizing images. - canvas_height (int): The height of the canvas for resizing images. + options (dict): Contains options such as product_id, name_template, canvas_width, canvas_height. """ + + # Concatenate the values into a string + hash_input = f"{options['background_color']}_{options['canvas_height']}_{options['canvas_width']}_{options['image_format']}_{options['image_size']}" + + # Create a SHA256 hash from the concatenated string + hash_object = hashlib.sha256(hash_input.encode()) + hash_string = hash_object.hexdigest() + options['hash_string'] = hash_string + pprint.pprint(hash_string) product_id = options.get("product_id") if not product_id: + print("No product ID") return - image_paths, product = get_product(product_id) + product = options.get("product") + # Check if the product meta_data contains _image_processed with the current hash + if product['meta_data']: + for meta in product['meta_data']: + if meta['key'] == '_image_processed' and meta['value'] == hash_string: + print(f"Skipping product {product_id}, already processed with the current hash.") + return + image_paths = get_images(product) if not image_paths: return + + + + with tempfile.TemporaryDirectory() as temp_output_directory: print(f"Using temporary directory: {temp_output_directory}") old_list = [] new_list = [] - + pprint.pprint ( list(image_paths.values())) + file = FileProcessor() + log = options.get("log_message", None) + + for image_id, file_path in image_paths.items(): - file = FileProcessor() - img = ImageProcessor() - output_path = file.generate_output_path(temp_output_directory, file_path, options, product) - - img.resize_image(file_path, output_path, options) - new_id = upload_image(output_path) + + processed = file.process_images([file_path], temp_output_directory, options, log, product) + + + new_id = upload_image(processed[0]) + if new_id: old_list.append(image_id) new_list.append(new_id) - update_product(new_list, product_id) + if new_list: + options["image_ids"] = new_list # Store new image IDs in options + update_product(product_id, new_list, old_list, options) # Pass new image IDs here + for old in old_list: + delete_img(old) print("Temporary files processed and uploaded successfully.") @@ -294,35 +369,95 @@ def generate_output_path( ) return os.path.join(temp_output_directory, new_filename + ext) - -def process_all_products(options): +def get_first_image_path(product): + images = get_images(product, 1) + # Loop through the dictionary + if images: + for image_id, file_path in images.items(): + print(f"Processing Image ID: {image_id}") + print(f"File Path: {file_path}") + return file_path +def get_first_image(): """ Process images for all WooCommerce products by resizing and uploading them. Args: - name_template (str): The template for generating image filenames. - canvas_width (int): The width of the canvas for resizing images. - canvas_height (int): The height of the canvas for resizing images. + options (dict): Contains options such as name_template, canvas_width, canvas_height. """ wcapi = get_wcapi() if not wcapi: return page = 1 + total_products = 0 # Initialize the counter for total products + + + products = wcapi.get("products", params={"per_page": 5, "page": page}).json() + if not products: + return + for product in products: + total_products += 1 # Update the total count + return get_first_image_path(product) + +def search_product(search): + """ + Process images for all WooCommerce products by resizing and uploading them. + + Args: + options (dict): Contains options such as name_template, canvas_width, canvas_height. + """ + wcapi = get_wcapi() + if not wcapi: + return + + page = 1 + total_products = 0 # Initialize the counter for total products + while True: - products = wcapi.get("products", params={ - "per_page": 100, "page": page}).json() + products = wcapi.get("products", params={"per_page": 100, "page": page, "search": search}).json() + if not products: + break + return products + +def process_all_products(options): + """ + Process images for all WooCommerce products by resizing and uploading them. + + Args: + options (dict): Contains options such as name_template, canvas_width, canvas_height. + """ + wcapi = get_wcapi() + if not wcapi: + return + + page = 1 + total_products = 0 # Initialize the counter for total products + + while True: + products = wcapi.get("products", params={"per_page": 100, "page": page}).json() if not products: break + product_count = len(products) # Get the count of products on the current page + + for product in products: + total_products += 1 # Update the total count options["product_id"] = product["id"] - process_product_images( - options - ) + options["product"] = product + log = options.get("log_message", None) + if log: + if product: + name = product.get("name", "") + log.log_message(f"#{total_products} Processing {name} ") # Log the product name + process_product_images(options) page += 1 + # Log the total number of products processed + log(f"Total products processed: {total_products}") + + # Show completion message messagebox.showinfo( - "Process Complete", "All product images processing is complete." + "Process Complete", f"All product images processing is complete. Total products processed: {total_products}" ) diff --git a/config/encrypt_config.py b/config/encrypt_config.py index 764e960..0af9e0d 100644 --- a/config/encrypt_config.py +++ b/config/encrypt_config.py @@ -1,5 +1,6 @@ from cryptography.fernet import Fernet import json +import os class ConfigEncryptor: @@ -9,51 +10,153 @@ class ConfigEncryptor: self.fernet = Fernet(self.key) def encrypt_config(self, data): - json_data = json.dumps(data) - encrypted_data = self.fernet.encrypt(json_data.encode()) - with open(self.filename, "wb") as encrypted_file: - encrypted_file.write(encrypted_data) - print("Credentials saved") + """ + Encrypt the given data and save it to a file. + + Args: + data (dict): The dictionary containing credentials and options to encrypt and save. + """ + try: + json_data = json.dumps(data) + encrypted_data = self.fernet.encrypt(json_data.encode()) + with open(self.filename, "wb") as encrypted_file: + encrypted_file.write(encrypted_data) + print(f"Encrypted configuration saved to {self.filename}") + except Exception as e: + print(f"Error encrypting config: {e}") def get_key(self): + """ + Return the encryption key. + + Returns: + str: The encryption key as a string. + """ return self.key.decode() def save_credentials(self, credentials): - config = self.load_config() - if not config: - config = {"credentials": {}, "options": {}} - config["credentials"] = credentials + """ + Save WooCommerce credentials to the config file, handling multiple credential sets. + + Args: + credentials (dict): Dictionary containing WooCommerce credentials. + """ + # Load the existing configuration + config = self.load_config() or {"credentials": [], "options": {}} + + # Ensure credentials is a list of dictionaries (if this is the first time saving, initialize it) + if not isinstance(config.get("credentials"), list): + config["credentials"] = [] + + # Check if the credential with the same 'name' or 'nice_name' already exists and update it + existing_credential = None + for cred in config["credentials"]: + print(credentials) + if cred.get("nice_name") == credentials.get("nice_name"): + existing_credential = cred + break + + if existing_credential: + # Update the existing credential set + existing_credential.update(credentials) + else: + # Add new credentials if they don't exist + config["credentials"].append(credentials) + + # Set 'active' flag to True for this credential and False for others + for cred in config["credentials"]: + cred['active'] = cred.get("nice_name") == credentials.get("nice_name") + + # Encrypt and save the updated config + self.encrypt_config(config) + print(f"Credentials for {credentials.get('nice_name', 'Unnamed')} saved successfully.") + + def delete_credentials(self, credentials): + """ + Save WooCommerce credentials to the config file, handling multiple credential sets. + + Args: + credentials (dict): Dictionary containing WooCommerce credentials. + """ + # Load the existing configuration + config = self.load_config() or {"credentials": [], "options": {}} + + new_config = [] + for credi in config["credentials"]: + + if credi.get("nice_name") != credentials: + new_config.append(credi) + config["credentials"] = new_config + print(config) + # Encrypt and save the updated config self.encrypt_config(config) + + def save_options(self, options): - config = self.load_config() - if not config: - config = {"credentials": {}, "options": {}} - # Ensure options only contains serializable data + """ + Save options to the config file. Filters out non-serializable data. + + Args: + options (dict): Dictionary containing options such as canvas width, height, etc. + """ + config = self.load_config() or {"credentials": {}, "options": {}} serializable_options = {k: v for k, v in options.items() if self.is_json_serializable(v)} config["options"] = serializable_options self.encrypt_config(config) def load_config(self): + """ + Load and decrypt the config file. + + Returns: + dict: Decrypted configuration data containing credentials and options, or None if file not found. + """ + if not os.path.exists(self.filename): + print(f"Config file {self.filename} not found.") + return None + try: with open(self.filename, "rb") as encrypted_file: encrypted_data = encrypted_file.read() decrypted_data = self.fernet.decrypt(encrypted_data).decode() config = json.loads(decrypted_data) - - # Filter only relevant keys - keys_to_return = ["credentials", "options"] - return {key: config[key] for key in keys_to_return if key in config} - except FileNotFoundError: + return config + except Exception as e: + print(f"Error loading or decrypting config: {e}") return None + def load_credentials(self): + """ + Load the active WooCommerce credentials from the config file. + + Returns: + dict: The active WooCommerce credentials if found, otherwise None. + """ config = self.load_config() if config: - return config.get("credentials") + # Check if credentials exist and search for the one marked as 'active' + credentials_list = config.get("credentials", []) + if isinstance(credentials_list, list): + for credentials in credentials_list: + if credentials.get("active"): + return credentials + elif isinstance(credentials_list, dict): + return credentials_list return None + @staticmethod def is_json_serializable(value): + """ + Check if a value is JSON serializable. + + Args: + value: The value to check. + + Returns: + bool: True if value is serializable, False otherwise. + """ try: json.dumps(value) return True @@ -62,7 +165,6 @@ class ConfigEncryptor: # Define your key here -# Replace with your actual key key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" if __name__ == "__main__": @@ -84,4 +186,3 @@ if __name__ == "__main__": } encryptor = ConfigEncryptor(key) encryptor.encrypt_config(config_data) - diff --git a/controller.py b/controller.py new file mode 100644 index 0000000..b6b3100 --- /dev/null +++ b/controller.py @@ -0,0 +1,619 @@ +import tempfile +import threading +from utils.file_operations import FileProcessor +from utils.image_processing import ImageProcessor +from ui.options_window import OptionsWindow +from config.encrypt_config import ConfigEncryptor +from api.woocommerce_api import get_first_image +from PIL import Image, ImageTk +from pprint import pformat +from api.woocommerce_api import process_product_images, process_all_products, search_product, get_first_image_path +import customtkinter as ctk +import os + +class AppController: + """ + The controller class for managing the overall state and interactions of the application. + """ + + def __init__(self, root): + """ + Initialize the AppController. + + Args: + root (ctk.CTk): The root CustomTkinter window. + """ + key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" + self.root = root + self.file = FileProcessor() + self.image = ImageProcessor() + self.menu_bar = None + self.local_processing_tab = None + self.settings_tab = None + self.preview_bar = None + self.log = None + self.canvas_width = 900 + self.canvas_height = 900 + self.template = "{slug}_{sku}_{width}x{height}" + self.delete_images = False + self.transparent = True + self.background_color = "#000000" + self.image_format = "AUTO" + self.image_size = "contain" + self.config = ConfigEncryptor(key) + self.type = None + self.destination_path = None + self.found_products = None + self.selected_directory = None + self.current_product = 0 + self.status = "stopped" + self.load_config() + + def load_config(self): + config = self.config.load_config() + if config: + if options := config.get("options"): + self.canvas_width = options.get("canvas_width", 900) + self.canvas_height = options.get("canvas_height", 900) + self.template = options.get("template", "{slug}_{sku}_{width}x{height}") + self.delete_images = options.get("delete_images", False) + self.transparent = options.get("transparent", True) + self.background_color = options.get("background_color", "#000000") + self.image_format = options.get("image_format", "AUTO") + self.image_size = options.get("image_size", "contain") + + def set_menu_bar(self, menu_bar): + """ + Set the MenuBar for the application. + + Args: + menu_bar (MenuBar): The MenuBar instance. + """ + self.log_message("Init menu bar") + self.menu_bar = menu_bar + + def set_log(self, log): + """ + Set the MenuBar for the application. + + Args: + menu_bar (MenuBar): The MenuBar instance. + """ + self.log = log + self.log_message("Init Logs") + + def set_local_processing_tab(self, local_processing_tab): + """ + Set the LocalProcessingTab for the application. + + Args: + local_processing_tab (LocalProcessingTab): The LocalProcessingTab instance. + """ + self.local_processing_tab = local_processing_tab + self.log_message("Init main") + + def set_preview_bar(self, preview): + """ + Set the MenuBar for the application. + + Args: + menu_bar (MenuBar): The MenuBar instance. + """ + self.preview_bar = preview + self.log_message("Init previews") + + def set_info_bar(self, info): + """ + Set the MenuBar for the application. + + Args: + menu_bar (MenuBar): The MenuBar instance. + """ + self.info_bar = info + self.log_message("Init info") + + def set_settings_tab(self, settings_tab): + """ + Set the SettingsTab for the application. + + Args: + settings_tab (SettingsTab): The SettingsTab instance. + """ + self.settings_tab = settings_tab + self.log_message("Init settings") + + def show_local_processing_tab(self): + """ + Display the local processing tab. + """ + if self.local_processing_tab: + self.log_message("Show main tab") + self.local_processing_tab.tkraise() # Make sure to raise the correct tab frame + + def show_settings_tab(self): + """ + Display the settings tab. + """ + if self.settings_tab: + self.log_message("Show settings tab") + self.settings_tab.tab.tkraise() # Make sure to raise the correct tab frame + + def show_local_processing_options(self): + """ + Display the options window in the local processing tab. + """ + if self.local_processing_tab: + self.log_message("Open options") + self.open_options_window() + + def log_message(self, obj): + """ + Log a formatted message to the log window using pprint. + + Args: + obj (object): The object to format and log. + """ + if self.log: + formatted_message = pformat(obj) + self.log.log_message(formatted_message) + + import threading + + def start_processing(self): + """ + Start the image processing based on the selected options. + """ + source = self.type + options = self.get_options() + self.log_message(f"Start import source: {source}") + self.status = "started" + self.menu_bar.start_button.configure(fg_color="red", text="Running") + + # Wrapper to process and update status after completion + def process_and_update_status(target_func, *args): + try: + # Execute the actual processing function + target_func(*args) + finally: + # Update status to 'stopped' after processing is done + self.status = "stopped" + self.menu_bar.start_button.configure(fg_color="#008000", text="Start") + self.log_message(f"Processing completed for source: {source}") + + if source == "directory": + threading.Thread( + target=process_and_update_status, args=(self.file.process_directory_with_logging, options) + ).start() + elif source == "product": + threading.Thread( + target=process_and_update_status, args=(process_product_images, options) + ).start() + elif source == "file": + threading.Thread( + target=process_and_update_status, args=(self.file.proces_single_image, options) + ).start() + elif source == "all_products": + threading.Thread( + target=process_and_update_status, args=(process_all_products, options) + ).start() + + + def update_options(self, text=None): + """ + Update the UI elements based on the selected source type. + """ + self.type = text + self.log_message(f"Update options {text}") + if text: + self.update_info(text) + # if self.local_processing_tab: + # self.local_processing_tab.product_id_button.grid_remove() + # self.local_processing_tab.product_id_entry.grid_remove() + # self.local_processing_tab.additional_name_label.grid_remove() + # self.local_processing_tab.additional_name_entry.grid_remove() + # self.local_processing_tab.browse_button.grid_remove() + # self.local_processing_tab.browse_file_button.grid_remove() + # if self.type == "directory": + # self.local_processing_tab.browse_button.grid() + # elif self.type == "file": + # self.local_processing_tab.browse_button.grid() + # elif self.type == "all_products": + # pass + # elif self.type == "wp_image": + # self.local_processing_tab.product_id_button.grid() + # self.local_processing_tab.product_id_entry.grid() + # elif self.type == "product": + # self.local_processing_tab.product_id_button.grid() + # self.local_processing_tab.product_id_entry.grid() + + self.update_previews() + + def update_previews(self, before_path=None, after_path=None): + """ + Update the image previews. + + Args: + before_path (str, optional): The path to the 'before' image. + after_path (str, optional): The path to the 'after' image. + """ + first_image_path = False + if self.status != "started": + if self.type == "all_products": + first_image_path = get_first_image() + + elif self.type == "product" and self.found_products: + first_image_path = get_first_image_path(self.found_products[self.current_product]) + else: + + print("getting first path") + first_image_path = self.file.get_first_image_path() + + if before_path : + before_img = Image.open(before_path) + before_img.thumbnail((200, 200)) + before_photo = ImageTk.PhotoImage(before_img) + self.preview_bar.before_image_label.configure(image=before_photo) + self.preview_bar.before_image_label.image = before_photo + dir_name = os.path.basename(before_path) + if len(dir_name) > 35: + dir_name = f"...{dir_name[-35:]}" + self.preview_bar.before_filename_label.configure(text=dir_name) + + if after_path: + after_img = Image.open(after_path) + after_img.thumbnail((200, 200)) + after_photo = ImageTk.PhotoImage(after_img) + self.preview_bar.after_image_label.configure(image=after_photo) + self.preview_bar.after_image_label.image = after_photo + dir_name = os.path.basename(after_path) + if len(dir_name) > 35: + dir_name = f"...{dir_name[-35:]}" + self.preview_bar.before_filename_label.configure(text=dir_name) + + if first_image_path: + with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as temp_file: + output_path = temp_file.name + self.image.resize_image( + first_image_path, output_path, self.get_options() + ) + before_img = Image.open(first_image_path) + before_img.thumbnail((200, 200)) + before_photo = ImageTk.PhotoImage(before_img) + self.preview_bar.before_image_label.configure(image=before_photo) + self.preview_bar.before_image_label.image = before_photo + + after_img = Image.open(output_path) + after_img.thumbnail((200, 200)) + after_photo = ImageTk.PhotoImage(after_img) + self.preview_bar.after_image_label.configure(image=after_photo) + self.preview_bar.after_image_label.image = after_photo + + name = self.file.generate_output_path("/",first_image_path,self.get_options()) + dir_name = os.path.basename(name) + if len(dir_name) > 35: + dir_name = f"...{dir_name[-35:]}" + self.preview_bar.after_filename_label.configure(text=dir_name) + dir_name = os.path.basename(first_image_path) + if len(dir_name) > 35: + dir_name = f"...{dir_name[-35:]}" + self.preview_bar.before_filename_label.configure(text=dir_name) + + def set_image_preview(self, image_path, label): + """ + Set the image preview for a given label. + + Args: + image_path (str): The path to the image file. + label (ctk.CTkLabel): The label to set the image on. + """ + img = Image.open(image_path) + img.thumbnail((150, 150)) + photo = ImageTk.PhotoImage(img) + label.configure(image=photo) + label.image = photo + + def browse_file_command(self): + """ + Command to browse for a file. + """ + file = self.file.browse_files() + if file: + file_name = os.path.basename(file) + if len(file_name) > 35: + file_name = f"...{file_name[-35:]}" + self.info_bar.selected_button_label.configure(text=file_name) + self.apply_options(self.get_options()) + self.update_previews() + + def browse_directory_command(self): + """ + Command to browse for a directory. + """ + directory = self.file.browse_directory() + if directory: + dir_name = os.path.basename(directory) + if len(dir_name) > 35: + dir_name = f"...{dir_name[-35:]}" + # self.browse_button.configure(text=dir_name) + self.selected_directory = directory + self.apply_options(self.get_options()) + self.update_previews() + + def browse_destination_command(self): + """ + Open directory dialog to select a destination directory. + """ + destination_path = self.file.browse_directory() + if destination_path: + self.info_bar.destination_label.configure(text=f"Destination: {destination_path}") + print(f"Selected destination: {destination_path}") + self.destination_path = destination_path + + def apply_canvas_size(self): + """ + Apply the canvas size settings and update previews. + """ + self.image.set_canvas_size(self.canvas_width, self.canvas_height) + + def apply_image_size(self): + """ + Apply the canvas size settings and update previews. + """ + self.image.set_image_size(self.image_size) + + def apply_background_color(self): + """ + Apply the canvas size settings and update previews. + """ + self.image.set_background_color(self.background_color) + + def get_options(self) -> dict: + """ + Get the current processing options. + + Returns: + dict: The current processing options. + """ + if not self.destination_path: + self.destination_path = False + product = None + product_id = 0 + if self.found_products and len(self.found_products) >= self.current_product: + product_id = self.found_products[self.current_product]['id'] + product = self.found_products[self.current_product] + options = { + # "selected_directory": self.local_processing_tab.browse_button.cget("text"), + "canvas_width": self.canvas_width, + "canvas_height": self.canvas_height, + "log_message": self.log, # Use the log method from the log_window + "format_log_message": self.log_message, + "update_previews": self.update_previews, + "product_id": product_id, + "product": product, + "template": self.template, + "delete_images": self.delete_images, + "background_color": self.background_color, + "image_format": self.image_format, + "image_size": self.image_size, + "selected_directory": self.selected_directory, + "destination_path" : self.destination_path + } + return options + + def open_options_window(self): + """ + Open the options window. + """ + current_options = { + "canvas_width": { + "type": "number", + "label": "Width:", + "default": self.canvas_width, + "min": 1, + "max": 2540, + }, + "canvas_height": { + "type": "number", + "label": "Height:", + "default": self.canvas_height, + "min": 1, + "max": 2540, + }, + "template": { + "type": "text", + "label": "Filename Template:", + "default": self.template, + }, + "delete_images": { + "type": "checkbox", + "label": "Delete image when done", + "default": self.delete_images, + }, + "background_color": { + "type": "color", + "label": "Background Color:", + "default": self.background_color, + }, + "image_format": { + "type": "dropdown", + "label": "Image Format:", + "options": ["AUTO", "JPEG", "PNG", "GIF", "DZI", "AVIF", "WEBP"], + "default": self.image_format, + }, + "image_size": { + "type": "dropdown", + "label": "Image Size:", + "options": ["contain", "cover"], + "default": self.image_size, + }, + } + + OptionsWindow(self.root, self.apply_options, current_options) + + def apply_options(self, options): + """ + Apply the selected options from the options window. + + Args: + options (dict): The options to apply. + """ + # if self.log_window: + # self.log_window.clear() # Clear the log window if it exists + self.canvas_width = options["canvas_width"] + self.canvas_height = options["canvas_height"] + self.template = options["template"] + self.delete_images = options["delete_images"] + self.background_color = options["background_color"] + self.image_size = options["image_size"] + self.image_format = options["image_format"] + self.apply_canvas_size() + self.apply_background_color() + self.apply_image_size() + key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" + self.config.save_options(self.get_options()) + self.update_previews() + + def process_product(self, input): + self.found_products = search_product(input) + if self.found_products: + count_products = len(self.found_products) + print(f"Found {count_products} products") + print(f"Current product {self.current_product}") + print(self.found_products[self.current_product]) + self.info_bar.selected_button_label.configure(text=self.found_products[self.current_product]['name'] + " (id: "+ str(self.found_products[self.current_product]['id']) +")" ) + number = self.current_product + number += 1 + text = f"Viewing product {number}/{count_products}" + self.info_bar.destination_label.configure(text=text) + self.update_previews() + return self.found_products[self.current_product] + pass + + def change_product(self, data): + self.current_product += data + if self.found_products and len(self.found_products) >= self.current_product: + count_products = len(self.found_products) + print(self.found_products[self.current_product]) + self.info_bar.selected_button_label.configure(text=self.found_products[self.current_product]['name'] + " (id: "+ str(self.found_products[self.current_product]['id'])+")" ) + number = self.current_product + number += 1 + text = f"Viewing product {number}/{count_products}" + self.info_bar.destination_label.configure(text=text) + self.update_previews() + pass + + def update_info(self, selected_option): + """ + Update the info frame based on the selected option. + + Args: + selected_option (str): The currently selected option (e.g., "product", "file", etc.). + """ + + # Clear previous description and input fields + if self.info_bar.input_field: + self.info_bar.input_field.grid_forget() + if self.info_bar.input_button: + self.info_bar.input_button.grid_forget() + if self.info_bar.next_button: + self.info_bar.next_button.grid_forget() + if self.info_bar.prev_button: + self.info_bar.prev_button.grid_forget() + if self.info_bar.destination_button: + self.info_bar.destination_button.grid_forget() + if self.info_bar.destination_label: + self.info_bar.destination_label.grid_forget() + + display_label = selected_option.replace("_", " ").title() + self.info_bar.selected_button_label.configure(text=display_label) + + # Update the description and input fields based on the selected option + if selected_option == "product": + self.info_bar.description_label.configure(text="Search") + self.info_bar.input_field = ctk.CTkEntry(self.info_bar.parent_frame) + self.info_bar.input_field.grid(row=1, column=1, columnspan=2, padx=5, pady=5, sticky="ew") + + self.info_bar.input_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Search product:", + command=lambda: self.process_product(self.info_bar.input_field.get()) + ) + self.info_bar.input_button.grid(row=1, column=3, padx=5, pady=5, sticky="ew") + + self.info_bar.next_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Next", + command=lambda: self.change_product(1) + ) + self.info_bar.next_button.grid(row=2, column=3, padx=5, pady=5, sticky="ew") + + self.info_bar.prev_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Prev", + command=lambda: self.change_product(-1) + ) + self.info_bar.prev_button.grid(row=2, column=2, padx=5, pady=5, sticky="ew") + + # Destination Directory Label (to show the selected destination) + self.info_bar.destination_label = ctk.CTkLabel( + self.info_bar.parent_frame, text="No products found" + ) + self.info_bar.destination_label.grid(row=2, column=0, columnspan=2, padx=5, pady=5, sticky="w") + + elif selected_option == "file": + self.info_bar.description_label.configure(text="Choose a file to process:") + + # Browse File Button + self.info_bar.input_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Browse File", + command=self.browse_file_command + ) + self.info_bar.input_button.grid(row=1, column=1, padx=5, pady=5, sticky="ew") + + # Destination Directory Button + self.info_bar.destination_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Select Destination", + command=self.browse_destination_command # Command to browse destination directory + ) + self.info_bar.destination_button.grid(row=2, column=1, padx=5, pady=5, sticky="ew") + + # Destination Directory Label (to show the selected destination) + self.info_bar.destination_label = ctk.CTkLabel( + self.info_bar.parent_frame, text="No destination selected" + ) + self.info_bar.destination_label.grid(row=2, column=0, padx=5, pady=5, sticky="w") + + elif selected_option == "directory": + self.info_bar.description_label.configure(text="Choose a directory to process:") + + # Browse Directory Button + self.info_bar.input_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Browse Directory", + command=self.browse_directory_command + ) + self.info_bar.input_button.grid(row=1, column=1, padx=5, pady=5, sticky="ew") + + # Destination Directory Button + self.info_bar.destination_button = ctk.CTkButton( + self.info_bar.parent_frame, + text="Select Destination", + command=self.browse_destination_command # Command to browse destination directory + ) + self.info_bar.destination_button.grid(row=2, column=1, padx=5, pady=5, sticky="ew") + + # Destination Directory Label (to show the selected destination) + self.info_bar.destination_label = ctk.CTkLabel( + self.info_bar.parent_frame, text="No destination selected" + ) + self.info_bar.destination_label.grid(row=2, column=0, padx=5, pady=5, sticky="w") + + + + def run(self): + """ + Run the main event loop. + """ + self.root.mainloop() diff --git a/images/image-7.jpg b/images/image-7.jpg deleted file mode 100644 index 504d5c9d9cd9b727d893ea63b81bdd5306e004cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107471 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%0AUncZvT9!3HBQ*TFzH7mrm% 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 z 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 z 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 DMU-Y2 diff --git a/main.py b/main.py index 1579c34..c319361 100644 --- a/main.py +++ b/main.py @@ -1,19 +1,28 @@ """ Main module for the Image Processor application. """ - +from PIL import Image import customtkinter as ctk -from ui.log_window import LogWindow -from ui.local_processing_tab import LocalProcessingTab +from ui.menu import MenuBar # Import the new MenuBar class +from ui.log_frame import LogWindow +from ui.button_frame import ButtonFrame +from ui.frame_info import InfoFrame from ui.settings_tab import SettingsTab from config.decrypt_config import ConfigDecryptor, DECRYPTION_KEY from config.encrypt_config import ConfigEncryptor +from controller import AppController + +from ui.preview_frame import PreviewFrame # Import the new PreviewFrame class + + class ImageProcessorApp: """ Main application class for the Image Processor. """ + + def __init__(self, root): """ Initialize the ImageProcessorApp. @@ -23,46 +32,82 @@ class ImageProcessorApp: """ self.root = root self.root.title("Image Processor") - self.root.geometry("480x800") - # Create menu frame at the top - menu_frame = ctk.CTkFrame(self.root) - menu_frame.pack(side="top", fill="x") + self.root.geometry("553x800") - local_processing_button = ctk.CTkButton(menu_frame, text="Local Processing", command=self.show_local_processing_tab) - local_processing_button.pack(side="left", padx=5, pady=5) + # Initialize the controller + self.controller = AppController(self.root) - settings_button = ctk.CTkButton(menu_frame, text="Settings", command=self.show_settings_tab) - settings_button.pack(side="left", padx=5, pady=5) + # Create the menu bar + self.menu_bar = MenuBar(self.root, self.controller) - # Create main frame to hold tabs and log window + # Create the main frame to hold tabs, log window, and other sections main_frame = ctk.CTkFrame(self.root) - main_frame.pack(expand=True, fill="x") + main_frame.pack(expand=True, fill="both") # Ensure the main frame expands both vertically and horizontally - self.tab_parent = ctk.CTkFrame(main_frame) - self.tab_parent.grid(row=0, column=0, sticky="nsew") + # Configure row and column to expand and fill available space + self.root.grid_rowconfigure(0, weight=1) # Ensures the frames expand vertically + self.root.grid_columnconfigure(0, weight=1) # Ensures the frames expand horizontally - self.log_frame = ctk.CTkFrame(main_frame) - self.log_frame.grid(row=1, column=0, sticky="nsew") - - main_frame.grid_rowconfigure(0, weight=1) - - main_frame.grid_columnconfigure(0, weight=1) + # Create a master frame to hold all the other frames + self.master_main_frame = ctk.CTkFrame(main_frame) + self.master_main_frame.grid(row=0, column=0, sticky="nsew") + self.master_main_frame.grid_rowconfigure(0, weight=1) + self.master_main_frame.grid_columnconfigure(0, weight=1) # Ensure full-width spanning + # Log Frame (appears at the bottom) + self.log_frame = ctk.CTkFrame(self.master_main_frame) + self.log_frame.grid(row=3, column=0, sticky="ew") # Set sticky to "ew" to expand horizontally + self.log_frame.grid_columnconfigure(0, weight=1) self.log_window = LogWindow(self.log_frame) + self.controller.set_log(self.log_window) + # Button Frame + self.button_frame = ctk.CTkFrame(self.master_main_frame, height=250) + self.button_frame.grid(row=0, column=0, sticky="nsew") # Set sticky to "ew" to expand horizontally + self.button_frame.grid_columnconfigure(0, weight=1) + self.button_frame = ButtonFrame(self.button_frame, self.controller, None) - self.local_processing_tab = LocalProcessingTab(self.tab_parent, self.log_window) - self.settings_tab = SettingsTab(self.tab_parent) + # Info Frame + self.info_frame = ctk.CTkFrame(self.master_main_frame) + self.info_frame.grid(row=1, column=0, sticky="ew") # Set sticky to "ew" to expand horizontally + self.info_frame.grid_columnconfigure(0, weight=1) + self.info_frame = InfoFrame(self.info_frame) - self.local_processing_tab.tab.grid(row=0, column=0, sticky="nsew") + # Preview Frame + self.preview_frame = ctk.CTkFrame(self.master_main_frame) + self.preview_frame.grid(row=2, column=0, sticky="nsew") # Expand both horizontally and vertically + self.preview_frame.grid_columnconfigure(0, weight=1) + self.preview_frame = PreviewFrame(self.preview_frame) # Initialize the PreviewFrame + + + + # Settings Tab + self.settings_tab = SettingsTab(main_frame, self.controller) + + # Register the tabs and preview frame with the controller + self.controller.set_local_processing_tab(self.master_main_frame) + self.controller.set_settings_tab(self.settings_tab) + self.controller.set_preview_bar(self.preview_frame) + self.controller.set_info_bar(self.info_frame) + self.controller.set_menu_bar( self.menu_bar) + # Position the tabs + self.master_main_frame.grid(row=0, column=0, sticky="nsew") # Make sure master_main_frame expands self.settings_tab.tab.grid(row=0, column=0, sticky="nsew") - self.show_local_processing_tab() + # Show the default tab (Local Processing Tab) + self.controller.update_options() + self.open_local_processing_tab() - def show_local_processing_tab(self): + def open_local_processing_tab(self): """ Show the Local Processing tab. """ - self.local_processing_tab.tab.tkraise() + self.master_main_frame.tkraise() + + def show_local_processing_options(self): + """ + Show the Local Processing tab. + """ + self.master_main_frame.open_options_window() def show_settings_tab(self): """ @@ -76,19 +121,27 @@ class ImageProcessorApp: """ self.root.mainloop() + + if __name__ == "__main__": try: decryptor = ConfigEncryptor(DECRYPTION_KEY) + # Load the active credentials config = decryptor.load_credentials() + print(config) if config: - wc_url = config["url"] - wc_consumer_key = config["consumer_key"] - wc_consumer_secret = config["consumer_secret"] - wp_username = config["username"] - wp_password = config["password"] + wc_url = config.get("url", "") + wc_consumer_key = config.get("consumer_key", "") + wc_consumer_secret = config.get("consumer_secret", "") + wp_username = config.get("username", "") + wp_password = config.get("password", "") + else: + print("No active credentials found.") except FileNotFoundError as e: print(f"File not found: {e}") + except Exception as e: + print(f"An error occurred: {e}") root = ctk.CTk() ctk.set_appearance_mode("dark") diff --git a/main.spec b/main.spec index c55496f..7618bee 100644 --- a/main.spec +++ b/main.spec @@ -1,25 +1,36 @@ +# Import necessary modules +import glob +import os # -*- mode: python ; coding: utf-8 -*- +# Collect all PNG and JPG images in the ui/images directory +image_files = [(file, "ui/images") for file in glob.glob("ui/images/*.*") if file.endswith(('.png', '.jpg', '.jpeg'))] + +block_cipher = None + a = Analysis( ['main.py'], pathex=[], binaries=[], - datas=[], + datas=image_files, hiddenimports=[], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, noarchive=False, - optimize=0, ) -pyz = PYZ(a.pure) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE( pyz, a.scripts, a.binaries, + a.zipfiles, a.datas, [], name='main', @@ -29,7 +40,7 @@ exe = EXE( upx=True, upx_exclude=[], runtime_tmpdir=None, - console=False, + disable_windowed_traceback=False, argv_emulation=False, target_arch=None, diff --git a/speelgoed-config.enc b/speelgoed-config.enc new file mode 100644 index 0000000..5a9a116 --- /dev/null +++ b/speelgoed-config.enc @@ -0,0 +1 @@ +gAAAAABm7tsOIxGHffAAnXR8MylfB10oI10SSNuhkirXHBYDu8UcAHDD3S1ADs1NEteVhFUnDZTCKe8AZuFdy5jyMPg9UzoxPL1_7Sbn5_10sfiOyOJHStm-7fx-4WmRrQyaeToAOS0fJYBe_Vg2AGndAB5vu7zPQ_X56jEmNzBzepdMcFtEUMscJB1Aud--p9QAg6KdZSZzmIks8QybBhAEqPifBKPwjxIivY_vx3l7p2lg7D7T1XfWHqufpTYqrDM_FTHy0wKnUJb_sHZCyuD2FNu9WhrqbZ-WYzHRTLEc0TJc1KoICiRHxWHwvZqiG1RCR8HBY1lXXOdYUv-1M-CtFQKD7U5xHKfFHC5wPF-Z682vL3-XJN5QfVXWQ27ua-TCilJF5NUetAXIBuxB2QjrntgugcsW3UxAZivcZk2Ux8hh12FYG3PDE-BUjg4ZK0gIuJ0LpJpUoYnz7CTmHLJ8FhWE1dBJoBAykyxoa5495DJGPuk_farzr9L3UTkfwuxOMkO7cip2rYOFgms8I0yxaBCFag3BdLLez9rIwghouwVzwgxMvHlN7toztQ9C6Dgu6e2bYimkz1JFyvFYO5YZLpX56It1yRaAgs7hwfhK5KOw9HcVEmKr4ipWXZe5rnnblW5NZ3HfsqwuPpjsg29F2g3AwlZlhc-Xl6D53g-2ydEnkhyvCePy7lZmIb_PB-MS4UzBpMFYDGfqRkoaLGNy8s0nelfmZM5i4ZKJPJGtulsIeyyU6rdmwYNf3S6f87CMNLhXYEE0 \ No newline at end of file diff --git a/test-config.enc b/test-config.enc new file mode 100644 index 0000000..1b18e48 --- /dev/null +++ b/test-config.enc @@ -0,0 +1 @@ +gAAAAABm7sewlH0ntIM9w6KP4wBUv-nUiW51UId3_8KCRSWM0OptC6n1VnAZukXHo5ZMx1fLf8OzQwWS5LWb0Cwjnauq3U2iB9Ck4DWklNoHNbE2L7T3yGPrxldSnkUU26hpYlELG5kbk5LMr55Y3JOhgsA75VKfv4X0OeAegngLYqfaV0uTA6Xw4WDz6ZDR4lBNpTfEPIPsUmEwuUNgYHCEwyRVEZXXWTK-bIaYhEAojd5Ecn5f09f9-qx_C7zd84335pa28FfrkQIn_-Ms8iosCw_ZOhh7_DRJrCfI5ursfhsbf9_Nk9QMFXPfFgpHx3zcDSslEcTvoMnVQqKgLesvwY_Mg1ISX6ZnDAWIoz0CfPD6jC0LPQXya1nFWSWHEx1FG_mebFrElDLRZO_hHLZFyrOcjuKHSwUKJq58nYUcw0hpmABzN1SUn7ofbp-XlSQpbE8DLH3OeM-WQYqPk0rQpKF19-cGNF6jxK8XzqI7cgONy3SQDEyB7ImKusxKyovF-A7TvGJcL2Nfuiz1zDODHJ771jLUfCO0Ho9GBvmJ3ZXzOn7zYCWjHc4Pt2zO6MCpbkuXVxYgXFLYaWNLE9lVxlT5JZKlKoD2Clg4Fkik1e9eVU7opWhB4DW4rLvdMgtzwCyVp5F-TnoOBsAqwHpD44Q90ZwC0m0BfRd6yt5aedYCeDZ_h5JftCz-_KvASFRXCXJpIwxOa2cStw5qoewv_Ebb5ep9-l4imWODZRVvlbHcB37iHn0ux2vAzGWkCVbV568HiqmH \ No newline at end of file diff --git a/ui/button_frame.py b/ui/button_frame.py new file mode 100644 index 0000000..d99b0c5 --- /dev/null +++ b/ui/button_frame.py @@ -0,0 +1,93 @@ +import customtkinter as ctk +from PIL import Image +from tkinter import StringVar +import os +import sys + +def resource_path(relative_path): + """ Get the absolute path to a resource, whether we're running in development or a PyInstaller package. """ + try: + # PyInstaller stores files in _MEIPASS when built + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath(".") + + return os.path.join(base_path, 'ui/images/'+ relative_path +'.png') +class ButtonFrame: + """ + Class for creating and managing the button frame. + """ + + def __init__(self, parent_frame, controller, log_window): + """ + Initialize the ButtonFrame. + + Args: + parent_frame (ctk.CTkFrame): The parent frame where the buttons will be placed. + controller (AppController): The controller to handle logic and updates. + log_window (LogWindow): The log window to display log messages. + """ + self.parent_frame = parent_frame + self.controller = controller + self.log_window = log_window + # self.log = self.log_window.log_message + + self.buttons = {"directory", "file", "wp_image", "product", "all_products"} + self.source_buttons = {} + self.selected_button = StringVar(value="") # To store the selected button + + self.setup_ui() + + def setup_ui(self): + """ + Set up the UI for the button frame. + """ + row = 0 + self.create_buttons(self.parent_frame, self.buttons, self.source_buttons, row) + + def create_buttons(self, frame, button_data, button_store, row): + """ + Create buttons from the button_data list and store them in button_store. + """ + + col_index = 0 + for label in button_data: + path = resource_path(label) + display_label = label.replace("_", " ").title() + icon_path = path + if icon_path: + icon_image = ctk.CTkImage( + light_image=Image.open(icon_path), size=(24, 24) + ) + else: + icon_image = None + + button = ctk.CTkButton( + frame, + image=icon_image, + text=display_label, + font=("Helvetica", 12, "bold"), + command=lambda l=label, s=button_store: self.set_active_button(l, s), + fg_color="#666666", + hover_color="#0f4d0f", + compound="top", + width=100, + + ) + button.grid(row=row, column=col_index,columnspan=1, padx=5, pady=5, sticky="ew") + button_store[label] = button + col_index += 1 + + def set_active_button(self, active_label, button_store): + """ + Set the clicked button to green and the rest to gray for a specific button store. + Also update the description and input fields based on the active button. + """ + if self.controller.status != "started": + for label, button in button_store.items(): + if label == active_label: + self.controller.update_options(active_label) + button.configure(fg_color="#008000") + else: + button.configure(fg_color="#666666") + diff --git a/ui/frame_info.py b/ui/frame_info.py new file mode 100644 index 0000000..52011a5 --- /dev/null +++ b/ui/frame_info.py @@ -0,0 +1,60 @@ +# info_frame.py + +import customtkinter as ctk +from tkinter import filedialog + + +class InfoFrame: + """ + Class for managing the info frame where descriptions and input fields are shown. + """ + + def __init__(self, parent_frame): + """ + Initialize the InfoFrame. + + Args: + parent_frame (ctk.CTkFrame): The parent frame for the info section. + log_window (LogWindow): The log window to display log messages. + """ + self.parent_frame = parent_frame + + self.selected_button_label = None + self.description_label = None + self.input_field = None + self.input_button = None + self.prev_button = None + self.next_button = None + self.destination_button = None + self.destination_label = None + self.setup_ui() + + def setup_ui(self): + """ + Set up the UI for the info frame. + """ + # Label to display the selected button name + self.selected_button_label = ctk.CTkLabel( + self.parent_frame, text="", font=("Helvetica", 12, "bold") + ) + self.selected_button_label.grid(row=0, column=0, columnspan=12, padx=5, pady=5, sticky="w") + + # Description label to provide info about the selected button + self.description_label = ctk.CTkLabel( + self.parent_frame, text="", font=("Helvetica", 10) + ) + self.description_label.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky="w") + + def process_product(self, product_id): + # Handle product processing logic here + self.log(f"Processing product with ID: {product_id}") + + def browse_file(self): + # Open file dialog to select a file + file_path = filedialog.askopenfilename() + + + def browse_directory(self): + # Open directory dialog to select a directory + directory_path = filedialog.askdirectory() + diff --git a/ui/images/all_products.png b/ui/images/all_products.png new file mode 100644 index 0000000000000000000000000000000000000000..f9f0c602358c1c849b4e94068ceaa9f179f19ea9 GIT binary patch literal 4115 zcmeH~c{r4N8^<3bB}+QCv`}-9RJO9jSYnhcS+YbEV>xmxL%j@R88a2hn#xXy7?~lm zWuFwCL^;XGI#grd!XV6epGWVx-mdrRyl>~f_m4Bz%rnwuK% z^X%aP0D#}{jQ%+QfDyqTz|9Fhk3-)- zEddS~=N7K5+qk#$@bdBR5ZL*XppdYL=$^g%#3lA0fd4EhB`tGE_Q+8=1*K!hl~q*L z)HM*ATG~3gNc~f%4bB)E8JnCnH8Zz3XK8J7-uA*pdk04+XO~}GFS)sUc=}wv;)nGQ zzy${3LkYyI*RF?!--x&w85JE98+R-I_MN*4iAl*RscGpM_cF7x@8{$`$jc`cJS-$r zic88WtDaQX)YjEEG*X{EZ*F<<+soEhZSAk$bo~Cd^IaFM`+ZMu-@qV!X!yg&PmIyA z@rlW)>6zKN&+`k5%%$a(FRN?oEOwMlwJ2CBhqJMPK0VX@Ek6M8sT%6*TH$)92cijG zuHw|sZs~gu@tITaC{y0an@(LC)wwUw9y) zH;SeipPxbrh6bbAg_B1=2^@@WNg~oE%G92;Hie30^jY~4b5-zXpDC(YxP%n-n6N1{WVM)?;NVS^O3*!c% z+m0H)hgF8jIodUemO$aHEV&}-bs@ELo~#a-%{%E3FFoG(I_MHG#JbuOii5{g66JQ*V=-NHLG31p zXa1RmZ0@CEOcj6%PK~Z4KqVHNk%tsak*%w0zE*s+ZZlL`j{LBy4C(3k8dw@lOZFKo^l7zwNsz}A zkkbAV2l>obvAX_9_sg~Af&>|Lh0T`aKuk&yQ;IPz16o&9W%BNi3-a1NRQLlI?$y9W zDKK79go-^*K@+PG@P9WHi8im-%j5Mzs2}2{cG*3><6@bnR_r_)JsP3$$gAv)d zq8%GoKt}@*DI3*GMw+*YtY?5R5AAEXO&ukA=19($x#mby_hSBPFt7Cf%{y+ zzsHu@Vm2gXFuJzAyUY&?cNiqBrKtcT+R4dI_XhU}Vbf z=6v}MYKGQ!i%bomt%v94{31Y9uNBYHl-s80mV7dS6wT4rlMHX|Gy!~7x94b904Ze_ zTEec6NV}I7T=i!C`pWCoi zpqtbcEV@1uI7?5S{3Qn#jdCGfu~pPiO5!XbY+O#opLV*-Y+-ev;=B zvG~Jk0j`;{q6axI_cx{#j&b#uKJ4!w4s1E0yqoCcpy+r-m`D~I*0EQ{xy8F}B znnmD-<=_D?HT1?MDI8{hPr~rt$XnsU2oE6_7IWgWp+nzzqS>+I5A33|!q>*blk^`= z7aGa9^FLI5I(fBr>b3wW$df6;b}~cZ9|K4iGQD+rec2~^PiK8ae(jOnjN<(Ge{`z8 zt#sK-lE%BA6$#<5Q^646G4)R;mHHZ2|G|3yT%joRC9!dXEby{>UeUL2X~nIxvF!4M zF=r6V_4FYjYXx5vz>LhJp zQq4<@%0{))qpGvTM>-P2@gvI%GnChX!4cO~R>{jfE{oB>-g<}}G)yFG0o z4f&I2X74JFpKad1U_1HF*};}OqN2EWrlNl^E_zJ9_9FYys*i-AhZR+rfVm0AP5^RKHly@!CHD Ds_=Re literal 0 HcmV?d00001 diff --git a/ui/images/cogs.png b/ui/images/cogs.png new file mode 100644 index 0000000000000000000000000000000000000000..1dd4f94aa5e933fbbad73fcf2dea9c9734628cf6 GIT binary patch literal 7070 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 C*3{Gh literal 0 HcmV?d00001 diff --git a/ui/images/directory.png b/ui/images/directory.png new file mode 100644 index 0000000000000000000000000000000000000000..e6cd51685c48571d07e69b9abd854d7fbef7c593 GIT binary patch literal 4560 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 literal 0 HcmV?d00001 diff --git a/ui/images/file.png b/ui/images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..2a07388e34076fa2b44f9925917fd135e878feaa GIT binary patch literal 5244 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 literal 0 HcmV?d00001 diff --git a/ui/images/house-user-solid.png b/ui/images/house-user-solid.png new file mode 100644 index 0000000000000000000000000000000000000000..52d6f91dfee424254bfd7ea5e0fb31c96380bfaf GIT binary patch literal 6649 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 M4b2TI^_(LA9}$S;p8x;= literal 0 HcmV?d00001 diff --git a/ui/images/play.png b/ui/images/play.png new file mode 100644 index 0000000000000000000000000000000000000000..d5410fe43ca65c9319c73e26df09750bab23e082 GIT binary patch literal 3332 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 l^^<-zfB*U7 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 Ifyg=WKcT*+`Tzg` literal 0 HcmV?d00001 diff --git a/ui/images/wp_image.png b/ui/images/wp_image.png new file mode 100644 index 0000000000000000000000000000000000000000..23b1d9a25025fd4ee6da59d5a10e2c2d6469efc2 GIT binary patch literal 17287 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 literal 0 HcmV?d00001 diff --git a/ui/local_processing_tab.py b/ui/local_processing_tab.py deleted file mode 100644 index 53fc6e4..0000000 --- a/ui/local_processing_tab.py +++ /dev/null @@ -1,423 +0,0 @@ -import tempfile -import threading -import customtkinter as ctk -from tkinter.scrolledtext import ScrolledText -from tkinter import StringVar, BooleanVar -from PIL import Image, ImageTk -from utils.file_operations import FileProcessor -from utils.image_processing import ImageProcessor -from api.woocommerce_api import process_product_images, process_all_products -from ui.options_window import OptionsWindow -from pprint import pformat -from config.encrypt_config import ConfigEncryptor -import os - - -class LocalProcessingTab: - """ - Class for the Local Processing Tab in the Image Processor application. - """ - - def __init__(self, tab_parent, log_window): - """ - Initialize the LocalProcessingTab. - - Args: - tab_parent (ctk.CTkFrame): The parent frame widget. - log_window (LogWindow): The log window frame. - """ - key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" - - self.log_window = log_window - self.log = self.log_window.log_message - self.tab = ctk.CTkFrame(tab_parent) - self.root = self.tab.winfo_toplevel() # Store the root window reference - self.config = ConfigEncryptor(key) - - self.canvas_width = 900 - self.canvas_height = 900 - self.template = "{slug}_{sku}_{width}x{height}" - self.delete_images = False - self.transparent = True - self.background_color = "#000000" - self.image_format = "AUTO" - self.image_size = "contain" - self.load_config() - self.source_type = StringVar(value="directory") - self.checkbox_var = BooleanVar(value=False) - self.file = FileProcessor() - self.image = ImageProcessor() - # Automatically open the options window with default options - - self.setup_ui() - self.update_options() - - def load_config(self): - config = self.config.load_config() - if config: - if options := config.get("options"): - self.canvas_width = options.get("canvas_width", 900) - self.canvas_height = options.get("canvas_height", 900) - self.template = options.get("template", "{slug}_{sku}_{width}x{height}") - self.delete_images = options.get("delete_images", False) - self.transparent = options.get("transparent", True) - self.background_color = options.get("background_color", "#000000") - self.image_format = options.get("image_format", "AUTO") - self.image_size = options.get("image_size", "contain") - - def setup_ui(self): - """ - Set up the user interface for the tab. - """ - current_row = 0 - start_options_frame = ctk.CTkFrame(self.tab, bg_color="gray30") - start_options_frame.grid(row=current_row, column=0, columnspan=6, padx=5, pady=5, sticky="ew") - - self.options_button = ctk.CTkButton( - start_options_frame, text="Options", command=self.open_options_window - ) - self.options_button.grid(row=0, column=0, columnspan=2, padx=5, pady=5, sticky="w") - - self.button_start = ctk.CTkButton( - start_options_frame, text="Start Processing", command=self.start_processing - ) - self.button_start.grid(row=0, column=2, columnspan=2, padx=5, pady=5, sticky="w") - - # Image previews section - current_row += 1 - - # Source selection section - source_frame = ctk.CTkFrame(self.tab, bg_color="gray20") - source_frame.grid(row=current_row, column=0, columnspan=6, padx=5, pady=5, sticky="ew") - - source_label = ctk.CTkLabel(source_frame, anchor="w", text="Source Type:") - source_label.grid(row=0, column=0, columnspan=6, padx=5, pady=5, sticky="w") - - self.source_dropdown = ctk.CTkComboBox( - source_frame, - variable=self.source_type, - values=["directory", "file", "wp_image", "product", "all_products"], - state="readonly", - command=self.update_options - ) - self.source_dropdown.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="w") - self.source_dropdown.bind( - "<>", lambda e: self.update_options() - ) - - self.browse_button = ctk.CTkButton( - source_frame, text="Browse directory", command=self.browse_directory_command - ) - self.browse_button.grid(row=2, column=0, columnspan=2, padx=5, pady=5, sticky="w") - - self.browse_file_button = ctk.CTkButton( - source_frame, text="Browse file", command=self.browse_file_command - ) - self.browse_file_button.grid(row=2, column=2, columnspan=2, padx=5, pady=5, sticky="w") - - self.product_id_button = ctk.CTkButton(source_frame, text="Get", width=25) - self.product_id_button.grid(row=2, column=4, columnspan=1, padx=5, pady=5, sticky="w") - - self.product_id_entry = ctk.CTkEntry(source_frame) - self.product_id_entry.grid(row=2, column=5, columnspan=2, padx=5, pady=5, sticky="w") - - self.additional_name_label = ctk.CTkLabel(source_frame, text="Add suffix:") - self.additional_name_label.grid(row=2, column=7, padx=5, pady=5, sticky="w") - - self.additional_name_entry = ctk.CTkEntry(source_frame) - self.additional_name_entry.grid(row=2, column=8, padx=5, pady=5, sticky="w") - - # Destination selection section - current_row += 1 - # destination_frame = ctk.CTkFrame(self.tab, bg_color="gray25") - # destination_frame.grid(row=current_row, column=0, columnspan=6, padx=5, pady=5, sticky="ew") - - # destination_label = ctk.CTkLabel(destination_frame, anchor="w", text="Destination Type:") - # destination_label.grid(row=0, column=0, columnspan=6, padx=5, pady=5, sticky="w") - - # self.destination_dropdown = ctk.CTkComboBox( - # destination_frame, - # variable=self.source_type, - # values=["auto", "directory", "file", "wp_image", "product"], - # state="readonly", - # command=self.update_options - # ) - # self.destination_dropdown.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="w") - - # # Start and Options section - # current_row += 1 - - preview_frame = ctk.CTkFrame(self.tab, bg_color="gray35") - preview_frame.grid(row=current_row, column=0, columnspan=6, padx=5, pady=5, sticky="ew") - - self.before_label = ctk.CTkLabel(preview_frame, text="Before:") - self.before_label.grid(row=0, column=0, padx=5, pady=5, sticky="w") - - self.after_label = ctk.CTkLabel(preview_frame, text="After:") - self.after_label.grid(row=0, column=3, padx=5, pady=5, sticky="w") - - self.before_image_label = ctk.CTkLabel(preview_frame, text="") - self.before_image_label.grid(row=1, column=0, columnspan=3, padx=5, pady=5, sticky="w") - - self.after_image_label = ctk.CTkLabel(preview_frame, text="") - self.after_image_label.grid(row=1, column=3, columnspan=3, padx=5, pady=5, sticky="w") - - # Configure grid weights to make frames span the full width - self.tab.grid_columnconfigure(0, weight=1) - source_frame.grid_columnconfigure(0, weight=1) - - start_options_frame.grid_columnconfigure(0, weight=1) - preview_frame.grid_columnconfigure(0, weight=1) - - def update_options(self, text=None): - """ - Update the UI elements based on the selected source type. - """ - self.product_id_button.grid_remove() - self.product_id_entry.grid_remove() - self.additional_name_label.grid_remove() - self.additional_name_entry.grid_remove() - self.browse_button.grid_remove() - self.browse_file_button.grid_remove() - if self.source_type.get() == "directory": - self.browse_button.grid() - elif self.source_type.get() == "product": - self.product_id_button.grid() - self.product_id_entry.grid() - elif self.source_type.get() == "file": - self.browse_file_button.grid() - self.update_previews() - - def update_previews(self, before_path=None, after_path=None): - """ - Update the image previews. - - Args: - before_path (str, optional): The path to the 'before' image. - after_path (str, optional): The path to the 'after' image. - """ - first_image_path = self.file.get_first_image_path() - if before_path and after_path: - before_img = Image.open(before_path) - before_img.thumbnail((200, 200)) - before_photo = ImageTk.PhotoImage(before_img) - self.before_image_label.configure(image=before_photo) - self.before_image_label.image = before_photo - - after_img = Image.open(after_path) - after_img.thumbnail((200, 200)) - after_photo = ImageTk.PhotoImage(after_img) - self.after_image_label.configure(image=after_photo) - self.after_image_label.image = after_photo - elif first_image_path: - with tempfile.NamedTemporaryFile( - suffix=".jpg", delete=False - ) as temp_file: - output_path = temp_file.name - self.image.resize_image( - first_image_path, output_path, self.get_options() - ) - before_img = Image.open(first_image_path) - before_img.thumbnail((200, 200)) - before_photo = ImageTk.PhotoImage(before_img) - self.before_image_label.configure(image=before_photo) - self.before_image_label.image = before_photo - - after_img = Image.open(output_path) - after_img.thumbnail((200, 200)) - after_photo = ImageTk.PhotoImage(after_img) - self.after_image_label.configure(image=after_photo) - self.after_image_label.image = after_photo - - def set_image_preview(self, image_path, label): - """ - Set the image preview for a given label. - - Args: - image_path (str): The path to the image file. - label (ctk.CTkLabel): The label to set the image on. - """ - img = Image.open(image_path) - img.thumbnail((150, 150)) - photo = ImageTk.PhotoImage(img) - label.configure(image=photo) - label.image = photo - - def browse_file_command(self): - """ - Command to browse for a file. - """ - file = self.file.browse_files() - if file: - file_name = os.path.basename(file) - if len(file_name) > 20: - file_name = f"...{file_name[-20:]}" - self.browse_file_button.configure(text=file_name) - self.apply_options(self.get_options()) - self.update_previews() - - def browse_directory_command(self): - """ - Command to browse for a directory. - """ - directory = self.file.browse_directory() - if directory: - dir_name = os.path.basename(directory) - if len(dir_name) > 20: - dir_name = f"...{dir_name[-20:]}" - self.browse_button.configure(text=dir_name) - self.apply_options(self.get_options()) - self.update_previews() - - def apply_canvas_size(self): - """ - Apply the canvas size settings and update previews. - """ - self.image.set_canvas_size(self.canvas_width, self.canvas_height) - - def apply_image_size(self): - """ - Apply the canvas size settings and update previews. - """ - self.image.set_image_size(self.image_size) - - def apply_background_color(self): - """ - Apply the canvas size settings and update previews. - """ - self.image.set_background_color(self.background_color) - - def get_options(self) -> dict: - """ - Get the current processing options. - - Returns: - dict: The current processing options. - """ - options = { - "selected_directory": self.browse_button.cget("text"), - "canvas_width": self.canvas_width, - "canvas_height": self.canvas_height, - "log_message": self.log, # Use the log method from the log_window - "format_log_message": self.pprint_log_message, - "update_previews": self.update_previews, - "product_id": self.product_id_entry.get(), - "template": self.template, - "delete_images": self.delete_images, - "background_color": self.background_color, - "image_format": self.image_format, - "image_size": self.image_size, - } - return options - - def open_options_window(self): - """ - Open the options window. - """ - current_options = { - "canvas_width": { - "type": "number", - "label": "Width:", - "default": self.canvas_width, - "min": 1, - "max": 2540, - }, - "canvas_height": { - "type": "number", - "label": "Height:", - "default": self.canvas_height, - "min": 1, - "max": 2540, - }, - "template": { - "type": "text", - "label": "Filename Template:", - "default": self.template, - }, - "delete_images": { - "type": "checkbox", - "label": "Delete image when done", - "default": self.delete_images, - }, - "background_color": { - "type": "color", - "label": "Background Color:", - "default": self.background_color - }, - "image_format": { - "type": "dropdown", - "label": "Image Format:", - "options": ["AUTO", "JPEG", "PNG", "GIF", "DZI"], - "default": self.image_format - }, - "image_size": { - "type": "dropdown", - "label": "Image Size:", - "options": ["contain", "cover"], - "default": self.image_size - } - - } - - OptionsWindow(self.root, self.apply_options, current_options) - - def apply_options(self, options): - """ - Apply the selected options from the options window. - - Args: - options (dict): The options to apply. - """ - if self.log_window: - self.log_window.clear() # Clear the log window if it exists - self.canvas_width = options["canvas_width"] - self.canvas_height = options["canvas_height"] - self.template = options["template"] - self.delete_images = options["delete_images"] - self.background_color = options["background_color"] - self.image_size = options["image_size"] - self.image_format = options["image_format"] - self.apply_canvas_size() - self.apply_background_color() - self.apply_image_size() - key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" - self.config.save_options(self.get_options()) - self.update_previews() - - def pprint_log_message(self, obj): - """ - Log a formatted message to the log window using pprint. - - Args: - obj (object): The object to format and log. - """ - formatted_message = pformat(obj) - self.log(formatted_message) - - def start_processing(self): - """ - Start the image processing based on the selected options. - """ - source = self.source_type.get() - options = self.get_options() - - if source == "directory": - threading.Thread( - target=self.file.process_directory_with_logging, args=(options,) - ).start() - elif source == "product": - threading.Thread( - target=process_product_images, - args=(options,) - ).start() - elif source == "file": - threading.Thread( - target=self.file.proces_single_image, - args=(options,) - ).start() - elif source == "all_products": - threading.Thread( - target=process_all_products, - args=(options,) - ).start() - self.update_previews() diff --git a/ui/log_window.py b/ui/log_frame.py similarity index 72% rename from ui/log_window.py rename to ui/log_frame.py index 06e7e0f..37154e1 100644 --- a/ui/log_window.py +++ b/ui/log_frame.py @@ -1,5 +1,5 @@ import customtkinter as ctk - +from datetime import datetime class LogWindow: def __init__(self, parent): self.frame = ctk.CTkFrame(parent) @@ -14,8 +14,18 @@ class LogWindow: self.log_text.configure(yscrollcommand=self.scrollbar.set) def log_message(self, message): + """ + Log a message to the log window with the current timestamp. + """ + # Get the current time in the desired format (e.g., HH:MM:SS) + current_time = datetime.now().strftime("%H:%M:%S") + + # Prepend the current time to the message + full_message = f"[{current_time}] {message}" + + # Log the message self.log_text.configure(state="normal") - self.log_text.insert(ctk.END, message + "\n") + self.log_text.insert(ctk.END, full_message + "\n") self.log_text.see(ctk.END) self.log_text.configure(state="disabled") self.log_text.update_idletasks() diff --git a/ui/menu.py b/ui/menu.py new file mode 100644 index 0000000..ee86106 --- /dev/null +++ b/ui/menu.py @@ -0,0 +1,103 @@ +from PIL import Image +import customtkinter as ctk +import os +import sys + +def resource_path(relative_path): + """ Get the absolute path to a resource, whether we're running in development or a PyInstaller package. """ + try: + # PyInstaller stores files in _MEIPASS when built + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath(".") + + return os.path.join(base_path, 'ui/images/'+ relative_path +'.png') + +class MenuBar: + def __init__(self, parent, controller): + """ + Initialize the MenuBar. + + Args: + parent (ctk.CTkFrame): The parent frame for the menu. + controller (AppController): The controller instance to manage the app. + """ + self.parent = parent + self.controller = controller + self.setup_ui() + + def setup_ui(self): + + button_width = 40 + icon_size = 24 + # Create menu frame + self.menu_frame = ctk.CTkFrame(self.parent) + self.menu_frame.pack(side="top", fill="x") + + # Create the buttons with icons + self.create_menu_button( + "house-user-solid", + "#363636", + "", + self.controller.show_local_processing_tab, + button_width, + icon_size, + ) + self.create_menu_button( + "filters", + "#363636", + "", + self.controller.show_local_processing_options, + button_width, + icon_size, + ) + self.create_menu_button( + "cogs", + "#363636", + "", + self.controller.show_settings_tab, + button_width, + icon_size, + ) + + self.start_button = self.create_menu_button( + "play", + "#008000", + "Start", + self.controller.start_processing, + button_width, + icon_size, + side="right", + ) + + + def create_menu_button( + self, icon_path, bg_color, text, command, button_width, icon_size, side="left" + ): + """ + Create a button with an icon for the menu. + + Args: + icon_path (str): Path to the icon. + command (callable): The function to call when the button is pressed. + button_width (int): The width of the button. + icon_size (int): The size of the icon. + side (str): Where to place the button ('left' or 'right'). + """ + + if icon_path: + path = resource_path(icon_path) + icon_image = ctk.CTkImage( + light_image=Image.open(path), size=(icon_size, icon_size) + ) + + button = ctk.CTkButton( + self.menu_frame, + image=icon_image, + text=text, + fg_color=bg_color, + command=command, + width=button_width, + ) + button.pack(side=side, padx=5, pady=5) + return button \ No newline at end of file diff --git a/ui/preview_frame.py b/ui/preview_frame.py new file mode 100644 index 0000000..f915609 --- /dev/null +++ b/ui/preview_frame.py @@ -0,0 +1,84 @@ +import customtkinter as ctk +from PIL import Image + +class PreviewFrame: + """ + Class to handle the preview frames (Before and After) for image processing. + """ + + def __init__(self, parent): + """ + Initialize the PreviewFrame. + + Args: + parent (ctk.CTkFrame): The parent frame where the preview frames will be placed. + """ + + self.parent = parent + self.setup_ui() + + def setup_ui(self): + """ + Set up the user interface for the preview frames. + """ + row = 0 + start_row = row + # Creating the main preview frame + preview_frame = ctk.CTkFrame(self.parent, bg_color="gray35") + preview_frame.grid(row=row, column=0, columnspan=6, padx=5, pady=5, sticky="ew") + + # Ensure the preview_frame expands to the full width + preview_frame.grid_columnconfigure(0, weight=1) + preview_frame.grid_columnconfigure(1, weight=1) + + # Creating the "Before" frame + before_frame = ctk.CTkFrame(preview_frame) + before_frame.grid(row=row, column=0, padx=5, pady=5, sticky="ew") + + # Adding "Before" label and image label to the before_frame + self.before_label = ctk.CTkLabel(before_frame, text="Before:") + self.before_label.grid(row=row, column=0, padx=5, pady=5, sticky="w") + row += 1 + self.before_image_label = ctk.CTkLabel(before_frame, text="", height=175) + self.before_image_label.grid(row=row, column=0, padx=5, pady=5, sticky="w") + row += 1 + # Adding a filename label under the "Before" image label + self.before_filename_label = ctk.CTkLabel(before_frame, text="Filename") + self.before_filename_label.grid(row=row, column=0, padx=5, pady=5, sticky="w") + + # Creating the "After" frame + after_frame = ctk.CTkFrame(preview_frame) + after_frame.grid(row=start_row, column=1, padx=5, pady=5, sticky="ew") + + # Adding "After" label and image label to the after_frame + self.after_label = ctk.CTkLabel(after_frame, text="After:") + self.after_label.grid(row=start_row, column=0, padx=5, pady=5, sticky="w") + start_row += 1 + self.after_image_label = ctk.CTkLabel(after_frame, text="", height=175) + self.after_image_label.grid(row=start_row, column=0, padx=5, pady=5, sticky="w") + start_row += 1 + # Adding a filename label under the "After" image label + self.after_filename_label = ctk.CTkLabel(after_frame, text="Filename") + self.after_filename_label.grid(row=start_row, column=0, padx=5, pady=5, sticky="w") + + # def update_before_image(self, image, filename=""): + # """ + # Update the before image and filename label. + + # Args: + # image (PIL.Image): The image to display. + # filename (str): The filename to display. + # """ + # self.before_image_label.config(image=image) + # self.before_filename_label.config(text=filename) + + # def update_after_image(self, image, filename=""): + # """ + # Update the after image and filename label. + + # Args: + # image (PIL.Image): The image to display. + # filename (str): The filename to display. + # """ + # self.after_image_label.config(image=image) + # self.after_filename_label.config(text=filename) diff --git a/ui/settings_tab.py b/ui/settings_tab.py index 8baf02a..048e727 100644 --- a/ui/settings_tab.py +++ b/ui/settings_tab.py @@ -1,58 +1,234 @@ import customtkinter as ctk -from api.woocommerce_api import save_credentials, load_credentials +from api.woocommerce_api import ( + load_credentials, + save_active_credential_set, +) +from config.encrypt_config import ConfigEncryptor +from PIL import Image, ImageTk + +import os +import sys +KEY = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" +def resource_path(relative_path): + """ Get the absolute path to a resource, whether we're running in development or a PyInstaller package. """ + try: + # PyInstaller stores files in _MEIPASS when built + base_path = sys._MEIPASS + except Exception: + base_path = os.path.abspath(".") + + return os.path.join(base_path, 'ui/images/'+ relative_path +'.png') class SettingsTab: - def __init__(self, tab_parent): + def __init__(self, tab_parent, controller): self.tab = ctk.CTkFrame(tab_parent) self.tab.grid(row=0, column=0, sticky="nsew") - self.credentials = load_credentials() + # Initialize an instance of ConfigEncryptor + self.config_encryptor = ConfigEncryptor(KEY) # Ensure you pass any required arguments in the constructor if necessary + config = self.config_encryptor.load_config() + self.credentials_list = [] + if config: + self.credentials_list = self.config_encryptor.load_config().get('credentials') + self.active_credential_set = ( + self.get_active_credential_set() + ) # Fetch active credentials + else: + self.active_credential_set = { + 'nice_name': "Default" + } self.inputs = {} self.setup_ui() - def setup_ui(self): - if self.credentials: - settings_options = { - "url": {"type": "text", "label": "WooCommerce URL:", "default": self.credentials.get('url', '')}, - "consumer_key": {"type": "text", "label": "Consumer Key:", "default": self.credentials.get('consumer_key', '')}, - "consumer_secret": {"type": "text", "label": "Consumer Secret:", "default": self.credentials.get('consumer_secret', '')}, - "username": {"type": "text", "label": "Username:", "default": self.credentials.get('username', '')}, - "password": {"type": "text", "label": "Password:", "default": self.credentials.get('password', ''), "show": "*"} - } - else: - settings_options = { - "url": {"type": "text", "label": "WooCommerce URL:", "default": ""}, - "consumer_key": {"type": "text", "label": "Consumer Key:", "default": ""}, - "consumer_secret": {"type": "text", "label": "Consumer Secret:", "default": ""}, - "username": {"type": "text", "label": "Username:", "default": ""}, - "password": {"type": "text", "label": "Password:", "default": "", "show": "*"} - } + def get_active_credential_set(self): + """Retrieve active credential set from saved data and convert to new format if needed.""" + if isinstance(self.credentials_list, list) and all( + isinstance(cred, dict) for cred in self.credentials_list + ): + for cred in self.credentials_list: + if cred.get("active", False): + return cred + return self.credentials_list[0] if self.credentials_list else None - row_index = 0 + elif isinstance(self.credentials_list, dict): + self.credentials_list = [self.convert_to_new_format(self.credentials_list)] + return self.credentials_list[0] + + elif isinstance(self.credentials_list, str): + self.credentials_list = [ + self.convert_to_new_format({"url": self.credentials_list}) + ] + return self.credentials_list[0] + + return None + + def convert_to_new_format(self, old_credential): + return { + "url": old_credential.get("url", ""), + "consumer_key": old_credential.get("consumer_key", ""), + "consumer_secret": old_credential.get("consumer_secret", ""), + "username": old_credential.get("username", ""), + "password": old_credential.get("password", ""), + "name": old_credential.get("name", "Default Credential Set"), + "nice_name": old_credential.get("nice_name", old_credential.get("url", "Unnamed Credential")), + "active": True, + } + + def setup_ui(self): + # Dropdown to select active credentials + self.credential_var = ctk.StringVar() + self.credential_var.set( + self.active_credential_set.get("nice_name", "Default") + ) + credential_options = [ + cred.get("nice_name", "Unnamed Credential") + for cred in self.credentials_list + ] + + dropdown_label = ctk.CTkLabel(self.tab, text="Select Active Credentials:") + dropdown_label.grid(row=0, column=0, padx=5, columnspan=2 , pady=5, sticky="w") + self.credential_dropdown = ctk.CTkComboBox( + self.tab, + variable=self.credential_var, + values=credential_options, + command=self.load_selected_credential, + ) + self.credential_dropdown.grid(row=0, column=2, columnspan=2, padx=5, pady=5, sticky="w") + + # Show fields for credentials + self.create_credentials_form(self.active_credential_set, row_index=1) + + icon_path = resource_path("save") + icon_image = ctk.CTkImage(light_image=Image.open(icon_path), size=(24, 24)) if icon_path else None + + save_button = ctk.CTkButton( + self.tab, width=100,fg_color="green",image=icon_image, text="Save", command=self.save_credentials + ) + save_button.grid(row=7, column=0, columnspan=1, pady=10) + + new_button = ctk.CTkButton( + self.tab, width=100, fg_color="green", image=icon_image, text="New", command=self.add_new_credential_set + ) + new_button.grid(row=7, column=1, columnspan=1, pady=10) + + # Trash icon for delete button + trash_icon_path = resource_path("trash") + trash_icon_image = ctk.CTkImage(light_image=Image.open(trash_icon_path), size=(24, 24)) if trash_icon_path else None + + delete_button = ctk.CTkButton( + self.tab, width=100, fg_color="red", image=trash_icon_image, text="Delete", command=self.delete_selected_credential + ) + delete_button.grid(row=7, column=2, columnspan=1, pady=10) + + def create_credentials_form(self, credentials, row_index): + settings_options = { + "url": { + "type": "text", + "label": "WooCommerce URL:", + "default": credentials.get("url", ""), + }, + "consumer_key": { + "type": "text", + "label": "Consumer Key:", + "default": credentials.get("consumer_key", ""), + }, + "consumer_secret": { + "type": "text", + "label": "Consumer Secret:", + "default": credentials.get("consumer_secret", ""), + }, + "username": { + "type": "text", + "label": "Username:", + "default": credentials.get("username", ""), + }, + "password": { + "type": "text", + "label": "Password:", + "default": credentials.get("password", ""), + "show": "*", + }, + "nice_name": { + "type": "text", + "label": "Nice Name:", + "default": credentials.get("nice_name", "Unnamed Credential"), + }, + } + + self.inputs = {} # Reset inputs for new credentials set for name, details in settings_options.items(): self.create_setting(name, details, row_index) row_index += 1 - save_button = ctk.CTkButton(self.tab, text="Save Credentials", command=self.save_credentials) - save_button.grid(row=row_index, column=0, columnspan=2, pady=10) - def create_setting(self, name, details, row_index): - """ - Create a setting based on its type. - """ lbl = ctk.CTkLabel(self.tab, text=details["label"]) - lbl.grid(row=row_index, column=0, padx=5, pady=5, sticky="w") + lbl.grid(row=row_index, column=0,columnspan=2, padx=5, pady=5, sticky="w") - if details["type"] == "text": - entry = ctk.CTkEntry(self.tab, show=details.get("show", None)) - entry.insert(0, details["default"]) - entry.grid(row=row_index, column=1, padx=5, pady=5, sticky="w") - self.inputs[name] = entry + entry = ctk.CTkEntry(self.tab, show=details.get("show", None)) + entry.insert(0, details["default"]) + entry.grid(row=row_index, column=2,columnspan=2, padx=5, pady=5, sticky="w") + self.inputs[name] = entry + + def load_selected_credential(self, selected_name): + for cred in self.credentials_list: + if cred.get("nice_name", "Unnamed Credential") == selected_name: + self.active_credential_set = cred + self.create_credentials_form(self.active_credential_set, row_index=1) + break def save_credentials(self): - save_credentials( - self.inputs["url"].get(), - self.inputs["consumer_key"].get(), - self.inputs["consumer_secret"].get(), - self.inputs["username"].get(), - self.inputs["password"].get() + credentials = { + "url": self.inputs["url"].get(), + "consumer_key": self.inputs["consumer_key"].get(), + "consumer_secret": self.inputs["consumer_secret"].get(), + "username": self.inputs["username"].get(), + "password": self.inputs["password"].get(), + "name": self.inputs["nice_name"].get(), + "nice_name": self.inputs["nice_name"].get(), + "active": True, + } + + ConfigEncryptor(KEY).save_credentials(credentials) + save_active_credential_set(credentials["name"]) + + self.credentials_list.append(credentials) + self.credential_dropdown.configure( + values=[cred.get("nice_name", "Unnamed Credential") for cred in self.credentials_list] + ) + + def add_new_credential_set(self): + self.active_credential_set = { + "url": "", + "consumer_key": "", + "consumer_secret": "", + "username": "", + "password": "", + "name": "New Credential Set", + "nice_name": "New Credential Set", + "active": False, + } + self.create_credentials_form(self.active_credential_set, row_index=1) + self.credential_var.set(self.active_credential_set["nice_name"]) + + def delete_selected_credential(self): + selected_name = self.credential_var.get() + + # Find and remove the selected credential from the list + self.credentials_list = [ + cred for cred in self.credentials_list if cred.get("nice_name") != selected_name + ] + + # Save updated credentials list to storage + ConfigEncryptor(KEY).delete_credentials(selected_name) + + # Update the dropdown and form after deletion + if self.credentials_list: + self.active_credential_set = self.credentials_list[0] # Load first available credential + self.credential_var.set(self.active_credential_set["nice_name"]) + self.create_credentials_form(self.active_credential_set, row_index=1) + else: + self.active_credential_set = {} + self.create_credentials_form({}, row_index=1) # Clear form if no credentials are left + + self.credential_dropdown.configure( + values=[cred.get("nice_name", "Unnamed Credential") for cred in self.credentials_list] ) diff --git a/utils/file_operations.py b/utils/file_operations.py index 45c4d60..45358e1 100644 --- a/utils/file_operations.py +++ b/utils/file_operations.py @@ -44,17 +44,16 @@ class FileProcessor: if self.selected_file: return self.selected_file return None - if not self.selected_directory: - return None - - for root, dirs, files in os.walk(self.selected_directory): - if "ProcessedImages" in dirs: - dirs.remove("ProcessedImages") - for file in files: - if file.lower().endswith( - (".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif") - ): - return os.path.join(root, file) + if self.selected_directory: + + for root, dirs, files in os.walk(self.selected_directory): + if "ProcessedImages" in dirs: + dirs.remove("ProcessedImages") + for file in files: + if file.lower().endswith( + (".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif") + ): + return os.path.join(root, file) return None def log_message(self, message, log=None): @@ -66,7 +65,7 @@ class FileProcessor: log (function, optional): The log function to use. Defaults to None. """ if log: - log(message) + log.log_message(message) else: print(message) @@ -77,6 +76,8 @@ class FileProcessor: Args: options (dict): Processing options. """ + if options.get("selected_directory"): + self.selected_directory = options.get("selected_directory") if not self.selected_directory: messagebox.showwarning( "No Directory", "Please select a directory.") @@ -85,8 +86,12 @@ class FileProcessor: self.log_message( f"Processing started for directory: {self.selected_directory}", log ) - - output_directory = self.create_output_directory(log) + self.log_message( + options, log + ) + output_directory = options.get('destination_path') + if not output_directory: + output_directory = self.create_output_directory(log) image_paths = self.collect_image_paths(log) self.process_images(image_paths, output_directory, options, log) @@ -106,7 +111,7 @@ class FileProcessor: str: The path to the output directory. """ output_directory = os.path.join( - self.selected_directory, "ProcessedImages") + self.selected_directory) if os.path.exists(output_directory): shutil.rmtree(output_directory) self.log_message("Existing directory removed.", log) @@ -138,7 +143,7 @@ class FileProcessor: self.log_message(f"Total images found: {len(image_paths)}", log) return image_paths - def process_images(self, image_paths, output_directory, options, log): + def process_images(self, image_paths, output_directory, options, log, product = None): """ Process each image by resizing and saving it to the output directory. @@ -147,31 +152,45 @@ class FileProcessor: output_directory (str): The path to the output directory. options (dict): Processing options. log (function): The log function to use. + + Returns: + list: A list of output image paths. """ from utils.image_processing import ImageProcessor + processed_images = [] image = ImageProcessor() image.set_background_color(options.get("background_color", "transparent")) image.set_image_size(options.get("image_size", "contain")) + image.set_canvas_size( options.get("canvas_width"), options.get("canvas_height")) format = options.get("image_format") + for file_path in image_paths: - # output_path = os.path.join( - # output_directory, os.path.relpath( - # file_path, self.selected_directory) - # ) - output_path = self.generate_output_path(output_directory, file_path, options) + output_path = self.generate_output_path(output_directory, file_path, options, product) + previews = options.get("update_previews") + previews(file_path) os.makedirs(os.path.dirname(output_path), exist_ok=True) - self.log_message(f"Running: {file_path}", log) + log.log_message(f"Running: {file_path}") + # Check if the image is JPG and set background color accordingly + if file_path.lower().endswith(".jpg") or file_path.lower().endswith(".jpeg"): + image.set_background_color("white") + else: + image.set_background_color(options.get("background_color", "transparent")) + if format == "DZI": DZI(file_path, output_path, options) else: - image.resize_image( - file_path, output_path, options - ) + image.resize_image(file_path, output_path, options) + # Collect the processed output path + processed_images.append(output_path) + previews(None, output_path) if os.path.exists(file_path) and options.get("delete_images", False): self.log_message(f"Removing: {file_path}", log) os.remove(file_path) self.log_message(f"Processed: {file_path}", log) + + return processed_images + def proces_single_image(self, options): """ @@ -229,3 +248,5 @@ class FileProcessor: return os.path.join(output_directory, new_filename + ".jpg") elif imgf == "DZI": return os.path.join(output_directory, new_filename + ".dzi") + elif imgf == "WEBP": + return os.path.join(output_directory, new_filename + ".webp") diff --git a/utils/image_processing.py b/utils/image_processing.py index b2e3581..305b23f 100644 --- a/utils/image_processing.py +++ b/utils/image_processing.py @@ -119,7 +119,7 @@ class ImageProcessor: log (function, optional): The log function to use. Defaults to None. """ if log: - log(message) + log.log_message(message) else: print(message)