before frames update

This commit is contained in:
2024-07-14 19:50:15 +02:00
parent d7b8f65627
commit 13bcc98cac
9 changed files with 400 additions and 330 deletions

View File

@@ -55,7 +55,7 @@ def load_credentials():
with open("config.enc", "rb") as file: with open("config.enc", "rb") as file:
encrypted = file.read() encrypted = file.read()
decrypted = fernet.decrypt(encrypted).decode() decrypted = fernet.decrypt(encrypted).decode()
return json.loads(decrypted) return json.loads(decrypted).get("credentials")
def get_wcapi(): def get_wcapi():

View File

@@ -13,6 +13,7 @@ class ConfigEncryptor:
encrypted_data = self.fernet.encrypt(json_data.encode()) encrypted_data = self.fernet.encrypt(json_data.encode())
with open(self.filename, "wb") as encrypted_file: with open(self.filename, "wb") as encrypted_file:
encrypted_file.write(encrypted_data) encrypted_file.write(encrypted_data)
print("Credentials saved")
def get_key(self): def get_key(self):
return self.key.decode() return self.key.decode()
@@ -38,7 +39,11 @@ class ConfigEncryptor:
with open(self.filename, "rb") as encrypted_file: with open(self.filename, "rb") as encrypted_file:
encrypted_data = encrypted_file.read() encrypted_data = encrypted_file.read()
decrypted_data = self.fernet.decrypt(encrypted_data).decode() decrypted_data = self.fernet.decrypt(encrypted_data).decode()
return json.loads(decrypted_data) 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: except FileNotFoundError:
return None return None

BIN
images/image-7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

73
main.py
View File

@@ -2,14 +2,12 @@
Main module for the Image Processor application. Main module for the Image Processor application.
""" """
import tkinter as tk import customtkinter as ctk
from tkinter import ttk
import customtkinter
from ui.log_window import LogWindow from ui.log_window import LogWindow
from ui.local_processing_tab import LocalProcessingTab from ui.local_processing_tab import LocalProcessingTab
from ui.settings_tab import SettingsTab from ui.settings_tab import SettingsTab
from config.decrypt_config import ConfigDecryptor, DECRYPTION_KEY from config.decrypt_config import ConfigDecryptor, DECRYPTION_KEY
from config.encrypt_config import ConfigEncryptor
class ImageProcessorApp: class ImageProcessorApp:
""" """
@@ -21,42 +19,69 @@ class ImageProcessorApp:
Initialize the ImageProcessorApp. Initialize the ImageProcessorApp.
Args: Args:
root (tk.Tk): The root Tkinter window. root (ctk.CTk): The root CustomTkinter window.
""" """
self.root = root self.root = root
self.root.title("Image Processor") self.root.title("Image Processor")
self.root.geometry("500x450") self.root.geometry("520x600")
self.tab_parent = ttk.Notebook(self.root) # Create menu frame at the top
self.log_window = None menu_frame = ctk.CTkFrame(self.root)
menu_frame.pack(side="top", fill="x")
self.local_processing_tab = LocalProcessingTab( local_processing_button = ctk.CTkButton(menu_frame, text="Local Processing", command=self.show_local_processing_tab)
self.tab_parent, "Local Processing", self.open_log_window local_processing_button.pack(side="left", padx=5, pady=5)
)
self.settings_tab = SettingsTab(self.tab_parent, "Settings")
self.tab_parent.pack(expand=True, fill="both") settings_button = ctk.CTkButton(menu_frame, text="Settings", command=self.show_settings_tab)
settings_button.pack(side="left", padx=5, pady=5)
def open_log_window(self): # Create main frame to hold tabs and log window
main_frame = ctk.CTkFrame(self.root)
main_frame.pack(expand=True, fill="both")
self.tab_parent = ctk.CTkFrame(main_frame)
self.tab_parent.grid(row=0, column=0, sticky="nsew")
self.log_frame = ctk.CTkFrame(main_frame)
self.log_frame.grid(row=1, column=0, sticky="nsew")
main_frame.grid_rowconfigure(0, weight=3)
main_frame.grid_rowconfigure(1, weight=1)
main_frame.grid_columnconfigure(0, weight=1)
self.log_window = LogWindow(self.log_frame)
self.local_processing_tab = LocalProcessingTab(self.tab_parent, self.log_window)
self.settings_tab = SettingsTab(self.tab_parent)
self.local_processing_tab.tab.grid(row=0, column=0, sticky="nsew")
self.settings_tab.tab.grid(row=0, column=0, sticky="nsew")
self.show_local_processing_tab()
def show_local_processing_tab(self):
""" """
Open the log window. If it already exists, bring it to the front. Show the Local Processing tab.
""" """
if self.log_window is None or not self.log_window.winfo_exists(): self.local_processing_tab.tab.tkraise()
self.log_window = LogWindow(self.root)
else: def show_settings_tab(self):
self.log_window.lift() """
Show the Settings tab.
"""
self.settings_tab.tab.tkraise()
def run(self): def run(self):
""" """
Run the Tkinter main loop. Run the CustomTkinter main loop.
""" """
self.root.mainloop() self.root.mainloop()
if __name__ == "__main__": if __name__ == "__main__":
try: try:
decryptor = ConfigDecryptor(DECRYPTION_KEY) decryptor = ConfigEncryptor(DECRYPTION_KEY)
config = decryptor.decrypt() config = decryptor.load_config()
wc_url = config["url"] wc_url = config["url"]
wc_consumer_key = config["consumer_key"] wc_consumer_key = config["consumer_key"]
wc_consumer_secret = config["consumer_secret"] wc_consumer_secret = config["consumer_secret"]
@@ -65,6 +90,8 @@ if __name__ == "__main__":
except FileNotFoundError as e: except FileNotFoundError as e:
print(f"File not found: {e}") print(f"File not found: {e}")
root = customtkinter.CTk() root = ctk.CTk()
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
app = ImageProcessorApp(root) app = ImageProcessorApp(root)
app.run() app.run()

View File

@@ -4,40 +4,39 @@ Module for the Local Processing Tab in the Image Processor application.
import tempfile import tempfile
import threading import threading
import tkinter as tk import customtkinter as ctk
from tkinter import ttk
from tkinter.scrolledtext import ScrolledText from tkinter.scrolledtext import ScrolledText
from tkinter import Tk, Label, Button, Entry, Toplevel, StringVar, BooleanVar from tkinter import StringVar, BooleanVar
from PIL import Image, ImageTk from PIL import Image, ImageTk
from utils.file_operations import FileProcessor from utils.file_operations import FileProcessor
from utils.image_processing import ImageProcessor from utils.image_processing import ImageProcessor
from api.woocommerce_api import process_product_images, process_all_products from api.woocommerce_api import process_product_images, process_all_products
from ui.options_window import OptionsWindow from ui.options_window import OptionsWindow
from pprint import pformat, pprint from pprint import pformat
from config.encrypt_config import ConfigEncryptor from config.encrypt_config import ConfigEncryptor
import os
class LocalProcessingTab: class LocalProcessingTab:
""" """
Class for the Local Processing Tab in the Image Processor application. Class for the Local Processing Tab in the Image Processor application.
""" """
def __init__(self, tab_parent, text, log): def __init__(self, tab_parent, log_window):
""" """
Initialize the LocalProcessingTab. Initialize the LocalProcessingTab.
Args: Args:
tab_parent (ttk.Notebook): The parent notebook widget. tab_parent (ctk.CTkFrame): The parent frame widget.
text (str): The text to display on the tab. log_window (LogWindow): The log window frame.
log (function): The function to log messages.
""" """
key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=" key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI="
self.log = log self.log_window = log_window
self.tab = ttk.Frame(tab_parent) 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.root = self.tab.winfo_toplevel() # Store the root window reference
tab_parent.add(self.tab, text=text)
self.config = ConfigEncryptor(key) self.config = ConfigEncryptor(key)
self.log_window = None
self.canvas_width = 900 self.canvas_width = 900
self.canvas_height = 900 self.canvas_height = 900
@@ -48,7 +47,7 @@ class LocalProcessingTab:
self.image_format = "AUTO" self.image_format = "AUTO"
self.image_size = "contain" self.image_size = "contain"
self.load_config() self.load_config()
self.source_type = StringVar(value="local") self.source_type = StringVar(value="directory")
self.checkbox_var = BooleanVar(value=False) self.checkbox_var = BooleanVar(value=False)
self.file = FileProcessor() self.file = FileProcessor()
self.image = ImageProcessor() self.image = ImageProcessor()
@@ -58,8 +57,8 @@ class LocalProcessingTab:
self.update_options() self.update_options()
def load_config(self): def load_config(self):
config = self.config.load_config() config = self.config.load_config()
print(config)
if config: if config:
if options := config.get("options"): if options := config.get("options"):
self.canvas_width = options.get("canvas_width", 900) self.canvas_width = options.get("canvas_width", 900)
@@ -71,125 +70,126 @@ class LocalProcessingTab:
self.image_format = options.get("image_format", "AUTO") self.image_format = options.get("image_format", "AUTO")
self.image_size = options.get("image_size", "contain") self.image_size = options.get("image_size", "contain")
def create_log_window(self):
"""
Create and display the log window.
"""
if self.log_window and Toplevel.winfo_exists(self.log_window):
return
self.log_window = Toplevel()
self.log_window.title("Processing Log")
self.log_text = ScrolledText(
self.log_window, state="disabled", wrap="word", height=20, width=80
)
self.log_text.pack(expand=True, fill="both")
def log_message(self, message):
"""
Log a message to the log window.
Args:
message (str): The message to log.
"""
if self.log_window:
self.log_text.config(state="normal")
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
self.log_text.config(state="disabled")
self.log_text.update_idletasks()
def setup_ui(self): def setup_ui(self):
""" """
Set up the user interface for the tab. Set up the user interface for the tab.
""" """
# Source selection section current_row = 0
self.source_label = Label(self.tab, text="Source Type:")
self.source_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.source_dropdown = ttk.Combobox( # Source selection section
self.source_label = ctk.CTkLabel(self.tab, anchor="w", width=500, text="Source Type:")
self.source_label.grid(row=current_row, column=0, columnspan=6, padx=5, pady=5, sticky="w")
current_row += 1
self.source_dropdown = ctk.CTkComboBox(
self.tab, self.tab,
textvariable=self.source_type, variable=self.source_type,
values=["local", "product", "all_products"], values=["directory", "file", "wp_image", "product", "all_products"],
state="readonly", state="readonly",
command=self.update_options
) )
self.source_dropdown.grid(row=1, column=0, padx=5, pady=5, sticky="w") self.source_dropdown.grid(row=current_row, column=0, columnspan=2, padx=5, pady=5, sticky="w")
self.source_dropdown.bind( self.source_dropdown.bind(
"<<ComboboxSelected>>", lambda e: self.update_options() "<<ComboboxSelected>>", lambda e: self.update_options()
) )
self.browse_button = ttk.Button(
self.tab, text="Browse", command=self.browse_directory_command
)
self.browse_button.grid(row=2, column=0, padx=5, pady=5, sticky="w")
# WooCommerce Product ID section
self.product_id_label = Label(self.tab, text="Product ID:")
self.product_id_label.grid(row=2, column=0, padx=5, pady=5, sticky="w")
self.product_id_entry = Entry(self.tab)
self.product_id_entry.grid(row=3, column=0, padx=5, pady=5, sticky="w")
# SKU section
self.additional_name_label = Label(self.tab, text="Add suffix:")
self.additional_name_label.grid(
row=2, column=1, padx=5, pady=5, sticky="w")
self.additional_name_entry = Entry(self.tab)
self.additional_name_entry.grid(
row=2, column=2, padx=5, pady=5, sticky="w")
# Options button # Options button
self.options_button = ttk.Button( self.options_button = ctk.CTkButton(
self.tab, text="Options", command=self.open_options_window self.tab, text="Options", command=self.open_options_window
) )
self.options_button.grid(row=2, column=3, columnspan=2, padx=5, pady=5, sticky="w") self.options_button.grid(row=current_row, column=4, columnspan=2, padx=5, pady=5, sticky="w")
self.button_start = Button( current_row += 1
self.button_start = ctk.CTkButton(
self.tab, text="Start Processing", command=self.start_processing self.tab, text="Start Processing", command=self.start_processing
) )
self.button_start.grid( self.button_start.grid(
row=1, column=3, columnspan=2, padx=5, pady=5, sticky="w" row=current_row, column=4, columnspan=2, padx=5, pady=5, sticky="w"
) )
self.browse_button = ctk.CTkButton(
self.tab, text="Browse directory", command=self.browse_directory_command
)
self.browse_button.grid(row=current_row, column=0, columnspan=2, padx=5, pady=5, sticky="w")
self.browse_file_button = ctk.CTkButton(
self.tab, text="Browse file", command=self.browse_file_command
)
self.browse_file_button.grid(row=current_row, column=0, columnspan=2, padx=5, pady=5, sticky="w")
# WooCommerce Product ID section
self.product_id_button = ctk.CTkButton(self.tab, text="Get", width=25)
self.product_id_button.grid(row=2, column=2, columnspan=1, padx=5, pady=5, sticky="w")
self.product_id_entry = ctk.CTkEntry(self.tab)
self.product_id_entry.grid(row=current_row, column=0, columnspan=2,padx=5, pady=5, sticky="w")
# SKU section
self.additional_name_label = ctk.CTkLabel(self.tab, text="Add suffix:")
self.additional_name_label.grid(
row=current_row, column=1, padx=5, pady=5, sticky="w")
self.additional_name_entry = ctk.CTkEntry(self.tab)
self.additional_name_entry.grid(
row=current_row, column=2, padx=5, pady=5, sticky="w")
current_row += 1
# Destination selection section
self.destionation_label = ctk.CTkLabel(self.tab, anchor="w", width=500, text="Destination Type:")
self.destionation_label.grid(row=current_row, column=0, columnspan=6, padx=5, pady=5, sticky="w")
current_row += 1
self.destionation_dropdown = ctk.CTkComboBox(
self.tab,
variable=self.source_type,
values=["auto", "directory", "file", "wp_image", "product"],
state="readonly",
command=self.update_options
)
self.destionation_dropdown.grid(row=current_row, column=0, columnspan=2, padx=5, pady=5, sticky="w")
self.destionation_dropdown.bind(
"<<ComboboxSelected>>", lambda e: self.update_options()
)
current_row += 1
# Image previews # Image previews
self.before_label = Label(self.tab, text="Before:") self.before_label = ctk.CTkLabel(self.tab, text="Before:")
self.before_label.grid(row=5, column=0, padx=5, pady=5, sticky="w") self.before_label.grid(row=current_row, column=0, padx=5, pady=5, sticky="w")
self.before_image_label = Label(self.tab)
self.before_image_label.grid(
row=6, column=0, padx=5, pady=5, sticky="w")
self.after_label = Label(self.tab, text="After:") self.after_label = ctk.CTkLabel(self.tab, text="After:")
self.after_label.grid(row=5, column=1, padx=5, pady=5, sticky="w") self.after_label.grid(row=current_row, column=3, padx=5, pady=5, sticky="w")
self.after_image_label = Label(self.tab)
current_row += 1
self.after_image_label = ctk.CTkLabel(self.tab, text="")
self.after_image_label.grid( self.after_image_label.grid(
row=6, column=1, padx=5, pady=5, sticky="w") row=current_row, column=3, columnspan=3, padx=5, pady=5, sticky="w")
def update_options(self): self.before_image_label = ctk.CTkLabel(self.tab, text="")
self.before_image_label.grid(
row=current_row, column=0, columnspan=3, padx=5, pady=5, sticky="w")
def update_options(self, text=None):
""" """
Update the UI elements based on the selected source type. Update the UI elements based on the selected source type.
""" """
if self.source_type.get() == "local": 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() self.browse_button.grid()
self.product_id_label.grid_remove()
self.product_id_entry.grid_remove()
self.additional_name_label.grid_remove()
self.additional_name_entry.grid_remove()
elif self.source_type.get() == "product": elif self.source_type.get() == "product":
self.browse_button.grid_remove() self.product_id_button.grid()
self.product_id_label.grid()
self.product_id_entry.grid() self.product_id_entry.grid()
self.additional_name_label.grid_remove() elif self.source_type.get() == "file":
self.additional_name_entry.grid_remove() self.browse_file_button.grid()
else:
self.browse_button.grid_remove()
self.product_id_label.grid_remove()
self.product_id_entry.grid_remove()
self.product_id_label.grid()
self.product_id_entry.grid()
self.additional_name_label.grid_remove()
self.additional_name_entry.grid_remove()
self.update_previews() self.update_previews()
def update_previews(self, before_path=None, after_path=None): def update_previews(self, before_path=None, after_path=None):
@@ -200,21 +200,22 @@ class LocalProcessingTab:
before_path (str, optional): The path to the 'before' image. before_path (str, optional): The path to the 'before' image.
after_path (str, optional): The path to the 'after' image. after_path (str, optional): The path to the 'after' image.
""" """
first_image_path = self.file.get_first_image_path()
if not before_path and not first_image_path:
first_image_path = "images/image-7.jpg" # Set the path to your image here
if before_path and after_path: if before_path and after_path:
before_img = Image.open(before_path) before_img = Image.open(before_path)
before_img.thumbnail((150, 150)) before_img.thumbnail((200, 200))
before_photo = ImageTk.PhotoImage(before_img) before_photo = ImageTk.PhotoImage(before_img)
self.before_image_label.config(image=before_photo) self.before_image_label.configure(image=before_photo)
self.before_image_label.image = before_photo self.before_image_label.image = before_photo
after_img = Image.open(after_path) after_img = Image.open(after_path)
after_img.thumbnail((150, 150)) after_img.thumbnail((200, 200))
after_photo = ImageTk.PhotoImage(after_img) after_photo = ImageTk.PhotoImage(after_img)
self.after_image_label.config(image=after_photo) self.after_image_label.configure(image=after_photo)
self.after_image_label.image = after_photo self.after_image_label.image = after_photo
else: elif first_image_path:
first_image_path = self.file.get_first_image_path()
if first_image_path:
with tempfile.NamedTemporaryFile( with tempfile.NamedTemporaryFile(
suffix=".jpg", delete=False suffix=".jpg", delete=False
) as temp_file: ) as temp_file:
@@ -223,15 +224,15 @@ class LocalProcessingTab:
first_image_path, output_path, self.get_options() first_image_path, output_path, self.get_options()
) )
before_img = Image.open(first_image_path) before_img = Image.open(first_image_path)
before_img.thumbnail((150, 150)) before_img.thumbnail((200, 200))
before_photo = ImageTk.PhotoImage(before_img) before_photo = ImageTk.PhotoImage(before_img)
self.before_image_label.config(image=before_photo) self.before_image_label.configure(image=before_photo)
self.before_image_label.image = before_photo self.before_image_label.image = before_photo
after_img = Image.open(output_path) after_img = Image.open(output_path)
after_img.thumbnail((150, 150)) after_img.thumbnail((200, 200))
after_photo = ImageTk.PhotoImage(after_img) after_photo = ImageTk.PhotoImage(after_img)
self.after_image_label.config(image=after_photo) self.after_image_label.configure(image=after_photo)
self.after_image_label.image = after_photo self.after_image_label.image = after_photo
def set_image_preview(self, image_path, label): def set_image_preview(self, image_path, label):
@@ -240,24 +241,41 @@ class LocalProcessingTab:
Args: Args:
image_path (str): The path to the image file. image_path (str): The path to the image file.
label (Label): The label to set the image on. label (ctk.CTkLabel): The label to set the image on.
""" """
img = Image.open(image_path) img = Image.open(image_path)
img.thumbnail((150, 150)) img.thumbnail((150, 150))
photo = ImageTk.PhotoImage(img) photo = ImageTk.PhotoImage(img)
label.config(image=photo) label.configure(image=photo)
label.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): def browse_directory_command(self):
""" """
Command to browse for a directory. Command to browse for a directory.
""" """
directory = self.file.browse_directory() directory = self.file.browse_directory()
if directory: if directory:
self.browse_button.config(text=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.apply_options(self.get_options())
self.update_previews() self.update_previews()
def apply_canvas_size(self): def apply_canvas_size(self):
""" """
Apply the canvas size settings and update previews. Apply the canvas size settings and update previews.
@@ -276,7 +294,6 @@ class LocalProcessingTab:
""" """
self.image.set_background_color(self.background_color) self.image.set_background_color(self.background_color)
def get_options(self) -> dict: def get_options(self) -> dict:
""" """
Get the current processing options. Get the current processing options.
@@ -288,7 +305,7 @@ class LocalProcessingTab:
"selected_directory": self.browse_button.cget("text"), "selected_directory": self.browse_button.cget("text"),
"canvas_width": self.canvas_width, "canvas_width": self.canvas_width,
"canvas_height": self.canvas_height, "canvas_height": self.canvas_height,
"log_message": self.log_message, "log_message": self.log, # Use the log method from the log_window
"format_log_message": self.pprint_log_message, "format_log_message": self.pprint_log_message,
"update_previews": self.update_previews, "update_previews": self.update_previews,
"product_id": self.product_id_entry.get(), "product_id": self.product_id_entry.get(),
@@ -359,8 +376,7 @@ class LocalProcessingTab:
options (dict): The options to apply. options (dict): The options to apply.
""" """
if self.log_window: if self.log_window:
self.log_window.destroy() self.log_window.clear() # Clear the log window if it exists
self.log_window = None
self.canvas_width = options["canvas_width"] self.canvas_width = options["canvas_width"]
self.canvas_height = options["canvas_height"] self.canvas_height = options["canvas_height"]
self.template = options["template"] self.template = options["template"]
@@ -383,17 +399,16 @@ class LocalProcessingTab:
obj (object): The object to format and log. obj (object): The object to format and log.
""" """
formatted_message = pformat(obj) formatted_message = pformat(obj)
self.log_message(formatted_message) self.log(formatted_message)
def start_processing(self): def start_processing(self):
""" """
Start the image processing based on the selected options. Start the image processing based on the selected options.
""" """
self.create_log_window()
source = self.source_type.get() source = self.source_type.get()
options = self.get_options() options = self.get_options()
if source == "local": if source == "directory":
threading.Thread( threading.Thread(
target=self.file.process_directory_with_logging, args=(options,) target=self.file.process_directory_with_logging, args=(options,)
).start() ).start()
@@ -402,6 +417,11 @@ class LocalProcessingTab:
target=process_product_images, target=process_product_images,
args=(options,) args=(options,)
).start() ).start()
elif source == "file":
threading.Thread(
target=self.file.proces_single_image,
args=(options,)
).start()
elif source == "all_products": elif source == "all_products":
threading.Thread( threading.Thread(
target=process_all_products, target=process_all_products,

View File

@@ -1,18 +1,37 @@
from tkinter import Toplevel, Text import customtkinter as ctk
from pprint import pprint
class LogWindow(Toplevel): class LogWindow:
def __init__(self, master=None, **kwargs): def __init__(self, parent):
super().__init__(master, **kwargs) self.frame = ctk.CTkFrame(parent)
self.title("Log Window") self.frame.pack(expand=True, fill="both")
self.geometry("500x300")
self.text = Text(self)
self.text.pack(expand=True, fill="both")
self.protocol("WM_DELETE_WINDOW", self.hide)
def log(self, message): self.log_text = ctk.CTkTextbox(self.frame, state="disabled", wrap="word", height=10)
self.text.insert("end", pprint(message) + "\n") self.log_text.pack(side="left", expand=True, fill="both")
self.text.see("end")
def hide(self): self.scrollbar = ctk.CTkScrollbar(self.frame, command=self.log_text.yview)
self.withdraw() self.scrollbar.pack(side="right", fill="y")
self.log_text.configure(yscrollcommand=self.scrollbar.set)
def log_message(self, message):
self.log_text.configure(state="normal")
self.log_text.insert(ctk.END, message + "\n")
self.log_text.see(ctk.END)
self.log_text.configure(state="disabled")
self.log_text.update_idletasks()
def clear(self):
self.log_text.configure(state="normal")
self.log_text.delete('1.0', ctk.END)
self.log_text.configure(state="disabled")
# Example usage
if __name__ == "__main__":
root = ctk.CTk()
log_window = LogWindow(root)
# Example log messages
log_window.log_message("This is a test message.")
log_window.log_message("Another test message.")
root.mainloop()

View File

@@ -1,7 +1,8 @@
import tkinter as tk import customtkinter as ctk
from tkinter import ttk, colorchooser, messagebox from tkinter import colorchooser, messagebox
from pprint import pprint
class OptionsWindow(tk.Toplevel):
class OptionsWindow(ctk.CTkToplevel):
def __init__(self, parent, apply_callback, current_options): def __init__(self, parent, apply_callback, current_options):
super().__init__(parent) super().__init__(parent)
self.title("Options") self.title("Options")
@@ -12,6 +13,8 @@ class OptionsWindow(tk.Toplevel):
self.inputs = {} self.inputs = {}
self.setup_ui() self.setup_ui()
self.attributes('-topmost', True) # Ensure the window stays on top
self.lift() # Bring the window to the front
def setup_ui(self): def setup_ui(self):
""" """
@@ -19,6 +22,14 @@ class OptionsWindow(tk.Toplevel):
""" """
self.row_index = 0 self.row_index = 0
for name, details in self.options.items(): for name, details in self.options.items():
self.create_option(name, details)
self.create_apply_button()
def create_option(self, name, details):
"""
Create an option based on its type.
"""
if details["type"] == "number": if details["type"] == "number":
self.add_number_input( self.add_number_input(
name, name,
@@ -38,8 +49,6 @@ class OptionsWindow(tk.Toplevel):
elif details["type"] == "color": elif details["type"] == "color":
self.add_color_picker(name, details["label"], details["default"]) self.add_color_picker(name, details["label"], details["default"])
self.create_apply_button()
def add_number_input(self, name, label, default, min_val, max_val): def add_number_input(self, name, label, default, min_val, max_val):
""" """
Add a number input field. Add a number input field.
@@ -51,10 +60,10 @@ class OptionsWindow(tk.Toplevel):
min_val (int): The minimum value. min_val (int): The minimum value.
max_val (int): The maximum value. max_val (int): The maximum value.
""" """
lbl = tk.Label(self, text=label) lbl = ctk.CTkLabel(self, text=label)
lbl.grid(row=self.row_index, columnspan=1,column=0, padx=5, pady=5, sticky="w") lbl.grid(row=self.row_index, columnspan=1, column=0, padx=5, pady=5, sticky="w")
entry = tk.Entry(self) entry = ctk.CTkEntry(self)
entry.insert(0, str(default)) entry.insert(0, str(default))
entry.grid(row=self.row_index, columnspan=2, column=1, padx=5, pady=5, sticky="w") entry.grid(row=self.row_index, columnspan=2, column=1, padx=5, pady=5, sticky="w")
@@ -75,10 +84,10 @@ class OptionsWindow(tk.Toplevel):
label (str): The label for the input field. label (str): The label for the input field.
default (str): The default value. default (str): The default value.
""" """
lbl = tk.Label(self, text=label) lbl = ctk.CTkLabel(self, text=label)
lbl.grid(row=self.row_index, column=0, padx=5, pady=5, sticky="w") lbl.grid(row=self.row_index, column=0, padx=5, pady=5, sticky="w")
entry = tk.Entry(self) entry = ctk.CTkEntry(self)
entry.insert(0, default) entry.insert(0, default)
entry.grid(row=self.row_index, columnspan=2, column=1, padx=5, pady=5, sticky="w") entry.grid(row=self.row_index, columnspan=2, column=1, padx=5, pady=5, sticky="w")
@@ -94,8 +103,8 @@ class OptionsWindow(tk.Toplevel):
label (str): The label for the input field. label (str): The label for the input field.
default (bool): The default value. default (bool): The default value.
""" """
var = tk.BooleanVar(value=default) var = ctk.BooleanVar(value=default)
chk = tk.Checkbutton(self, text=label, variable=var) chk = ctk.CTkCheckBox(self, text=label, variable=var)
chk.grid(row=self.row_index, column=0, chk.grid(row=self.row_index, column=0,
columnspan=2, padx=5, pady=5, sticky="w") columnspan=2, padx=5, pady=5, sticky="w")
@@ -112,32 +121,24 @@ class OptionsWindow(tk.Toplevel):
options (list): The list of options. options (list): The list of options.
default (str): The default value. default (str): The default value.
""" """
lbl = tk.Label(self, text=label) lbl = ctk.CTkLabel(self, text=label)
lbl.grid(row=self.row_index, column=0, padx=5, pady=5, sticky="w") lbl.grid(row=self.row_index, column=0, padx=5, pady=5, sticky="w")
combo = ttk.Combobox(self, values=options, state="readonly") combo = ctk.CTkComboBox(self, values=options, state="readonly")
combo.set(default) combo.set(default)
combo.grid(row=self.row_index,columnspan=2, column=1, padx=5, pady=5, sticky="w") combo.grid(row=self.row_index, columnspan=2, column=1, padx=5, pady=5, sticky="w")
self.inputs[name] = {"type": "dropdown", "widget": combo} self.inputs[name] = {"type": "dropdown", "widget": combo}
self.row_index += 1 self.row_index += 1
def check_transparent(self, var, color_entry, pick_button, color_preview): def pick_color(self, button):
if var.get(): self.attributes('-topmost', False) # Temporarily disable topmost to allow colorchooser to be on top
color_entry.config(state="disabled") color_code = colorchooser.askcolor(parent=self, title="Choose color")[1]
pick_button.config(state="disabled") self.attributes('-topmost', True) # Re-enable topmost for this window
color_preview.config(bg="white")
else:
color_entry.config(state="normal")
pick_button.config(state="normal")
color_preview.config(bg=color_entry.get())
def pick_color(self, color_entry, color_preview):
color_code = colorchooser.askcolor(title="Choose color")[1]
if color_code: if color_code:
color_entry.delete(0, tk.END) button.configure(fg_color=color_code)
color_entry.insert(0, color_code) self.inputs[button.name]["color"] = color_code
color_preview.config(bg=color_code)
def add_color_picker(self, name, label, default): def add_color_picker(self, name, label, default):
""" """
@@ -148,36 +149,32 @@ class OptionsWindow(tk.Toplevel):
label (str): The label for the color picker. label (str): The label for the color picker.
default (str): The default color. default (str): The default color.
""" """
if default == "transparent": lbl = ctk.CTkLabel(self, text=label)
default = "#ffffff"
var = tk.BooleanVar(value=True)
else:
var = tk.BooleanVar(value=False)
lbl = tk.Label(self, text=label)
lbl.grid(row=self.row_index, column=0, padx=5, pady=5, sticky="w") lbl.grid(row=self.row_index, column=0, padx=5, pady=5, sticky="w")
color_preview = tk.Label(self, bg=default, width=2, height=1) color_button = ctk.CTkButton(self, text="", width=30, command=lambda: self.pick_color(color_button))
color_preview.grid(row=self.row_index, column=1, padx=5, pady=5, sticky="w") color_button.name = name
color_button.configure(fg_color=default)
color_button.grid(row=self.row_index, column=1, padx=5, pady=5, sticky="w")
color_entry = tk.Entry(self) chk_var = ctk.BooleanVar(value=(default == "transparent"))
color_entry.insert(0, default) chk = ctk.CTkCheckBox(self, text="Transparent", variable=chk_var, command=lambda: self.check_transparent(chk_var, color_button))
color_entry.grid(row=self.row_index, column=2, padx=5, pady=5, sticky="w") chk.grid(row=self.row_index, column=2, padx=5, pady=5, sticky="w")
pick_button = tk.Button(self, text="Pick", command=lambda: self.pick_color(color_entry, color_preview)) self.inputs[name] = {"type": "color", "button": color_button, "transparent_var": chk_var, "color": default}
pick_button.grid(row=self.row_index, column=3, padx=5, pady=5, sticky="w")
chk = tk.Checkbutton(self, text="Transparent", variable=var, command=lambda: self.check_transparent(var, color_entry, pick_button, color_preview))
chk.grid(row=self.row_index, column=4, padx=5, pady=5, sticky="w")
self.inputs[name] = {"type": "color", "entry": color_entry, "transparent_var": var}
self.row_index += 1 self.row_index += 1
def check_transparent(self, var, button):
if var.get():
button.configure(state="disabled", fg_color="#ffffff")
else:
button.configure(state="normal")
def create_apply_button(self): def create_apply_button(self):
""" """
Create the apply button. Create the apply button.
""" """
apply_button = tk.Button( apply_button = ctk.CTkButton(
self, text="Apply", command=self.apply_options) self, text="Apply", command=self.apply_options)
apply_button.grid(row=self.row_index, column=0, columnspan=2, pady=10) apply_button.grid(row=self.row_index, column=0, columnspan=2, pady=10)
@@ -205,10 +202,10 @@ class OptionsWindow(tk.Toplevel):
elif details["type"] == "dropdown": elif details["type"] == "dropdown":
options[name] = details["widget"].get() options[name] = details["widget"].get()
elif details["type"] == "color": elif details["type"] == "color":
if "value" in details: if details["transparent_var"].get():
options[name] = details["value"]
else:
options[name] = "transparent" options[name] = "transparent"
else:
options[name] = details["color"]
self.apply_callback(options) self.apply_callback(options)
self.destroy() self.destroy()
@@ -222,33 +219,7 @@ class OptionsWindow(tk.Toplevel):
condition (function): The condition function that returns a boolean. condition (function): The condition function that returns a boolean.
""" """
if condition(): if condition():
if self.inputs[name]["type"] == "number": self.create_option(name, self.inputs[name])
self.add_number_input(
name,
self.inputs[name]["label"],
self.inputs[name]["default"],
self.inputs[name]["min"],
self.inputs[name]["max"],
)
elif self.inputs[name]["type"] == "text":
self.add_text_input(
name, self.inputs[name]["label"], self.inputs[name]["default"]
)
elif self.inputs[name]["type"] == "checkbox":
self.add_checkbox(
name, self.inputs[name]["label"], self.inputs[name]["default"]
)
elif self.inputs[name]["type"] == "dropdown":
self.add_dropdown(
name,
self.inputs[name]["label"],
self.inputs[name]["options"],
self.inputs[name]["default"],
)
elif self.inputs[name]["type"] == "color":
self.add_color_picker(
name, self.inputs[name]["label"], self.inputs[name]["default"]
)
# Example usage # Example usage
@@ -256,7 +227,7 @@ if __name__ == "__main__":
def apply_options(options): def apply_options(options):
print(options) print(options)
root = tk.Tk() root = ctk.CTk()
current_options = { current_options = {
"canvas_width": {"type": "number", "label": "Width:", "default": 900, "min": 1, "max": 2540}, "canvas_width": {"type": "number", "label": "Width:", "default": 900, "min": 1, "max": 2540},
"canvas_height": {"type": "number", "label": "Height:", "default": 900, "min": 1, "max": 2540}, "canvas_height": {"type": "number", "label": "Height:", "default": 900, "min": 1, "max": 2540},

View File

@@ -1,60 +1,49 @@
import tkinter as tk import customtkinter as ctk
from tkinter import ttk
from api.woocommerce_api import save_credentials, load_credentials from api.woocommerce_api import save_credentials, load_credentials
class SettingsTab: class SettingsTab:
def __init__(self, tab_parent, text): def __init__(self, tab_parent):
self.tab = ttk.Frame(tab_parent) self.tab = ctk.CTkFrame(tab_parent)
tab_parent.add(self.tab, text=text) self.tab.grid(row=0, column=0, sticky="nsew")
self.credentials = load_credentials() self.credentials = load_credentials()
self.inputs = {}
self.setup_ui() self.setup_ui()
def setup_ui(self): def setup_ui(self):
url_label = tk.Label(self.tab, text="WooCommerce URL:") settings_options = {
url_label.pack(pady=5) "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": "*"}
}
self.url_entry = tk.Entry(self.tab) row_index = 0
self.url_entry.insert(0, self.credentials.get('url', '')) for name, details in settings_options.items():
self.url_entry.pack(pady=5) self.create_setting(name, details, row_index)
row_index += 1
consumer_key_label = tk.Label(self.tab, text="Consumer Key:") save_button = ctk.CTkButton(self.tab, text="Save Credentials", command=self.save_credentials)
consumer_key_label.pack(pady=5) save_button.grid(row=row_index, column=0, columnspan=2, pady=10)
self.consumer_key_entry = tk.Entry(self.tab) def create_setting(self, name, details, row_index):
self.consumer_key_entry.insert(0, self.credentials.get('consumer_key', '')) """
self.consumer_key_entry.pack(pady=5) 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")
consumer_secret_label = tk.Label(self.tab, text="Consumer Secret:") if details["type"] == "text":
consumer_secret_label.pack(pady=5) entry = ctk.CTkEntry(self.tab, show=details.get("show", None))
entry.insert(0, details["default"])
self.consumer_secret_entry = tk.Entry(self.tab, show="*") entry.grid(row=row_index, column=1, padx=5, pady=5, sticky="w")
self.consumer_secret_entry.insert(0, self.credentials.get('consumer_secret', '')) self.inputs[name] = entry
self.consumer_secret_entry.pack(pady=5)
username_label = tk.Label(self.tab, text="Username:")
username_label.pack(pady=5)
self.username_entry = tk.Entry(self.tab)
self.username_entry.insert(0, self.credentials.get('username', ''))
self.username_entry.pack(pady=5)
password_label = tk.Label(self.tab, text="Password:")
password_label.pack(pady=5)
self.password_entry = tk.Entry(self.tab, show="*")
self.password_entry.insert(0, self.credentials.get('password', ''))
self.password_entry.pack(pady=5)
save_button = tk.Button(self.tab, text="Save Credentials", command=self.save_credentials)
save_button.pack(pady=5)
def save_credentials(self): def save_credentials(self):
save_credentials( save_credentials(
self.url_entry.get(), self.inputs["url"].get(),
self.consumer_key_entry.get(), self.inputs["consumer_key"].get(),
self.consumer_secret_entry.get(), self.inputs["consumer_secret"].get(),
self.username_entry.get(), self.inputs["username"].get(),
self.password_entry.get() self.inputs["password"].get()
) )

View File

@@ -11,6 +11,7 @@ class FileProcessor:
""" """
def __init__(self): def __init__(self):
self.selected_file = ""
self.selected_directory = "" self.selected_directory = ""
def browse_directory(self): def browse_directory(self):
@@ -23,6 +24,16 @@ class FileProcessor:
self.selected_directory = filedialog.askdirectory() self.selected_directory = filedialog.askdirectory()
return self.selected_directory return self.selected_directory
def browse_files(self):
"""
Open a dialog to select a directory.
Returns:
str: The selected directory path.
"""
self.selected_file = filedialog.askopenfilename()
return self.selected_file
def get_first_image_path(self): def get_first_image_path(self):
""" """
Get the path of the first image in the selected directory. Get the path of the first image in the selected directory.
@@ -30,6 +41,9 @@ class FileProcessor:
Returns: Returns:
str: The path to the first image, or None if no images found. str: The path to the first image, or None if no images found.
""" """
if self.selected_file:
return self.selected_file
if not self.selected_directory: if not self.selected_directory:
return None return None
@@ -159,6 +173,31 @@ class FileProcessor:
os.remove(file_path) os.remove(file_path)
self.log_message(f"Processed: {file_path}", log) self.log_message(f"Processed: {file_path}", log)
def proces_single_image(self, options):
"""
Process images in the selected directory with logging.
Args:
options (dict): Processing options.
"""
if not self.selected_file:
messagebox.showwarning(
"No File", "Please select a file.")
return
log = options.get("log_message", None)
self.log_message(
f"Processing started for file: {self.selected_file}", log
)
output_directory = self.create_output_directory(log)
image_paths = [self.selected_file]
self.process_images(image_paths, output_directory, options, log)
messagebox.showinfo("Process Complete",
"Image processing is complete.")
self.log_message("Processing complete.", log)
def generate_output_path(self, output_directory, file_path, options, product = None): def generate_output_path(self, output_directory, file_path, options, product = None):
""" """
Generate the output path for resized images based on a template. Generate the output path for resized images based on a template.