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:
encrypted = file.read()
decrypted = fernet.decrypt(encrypted).decode()
return json.loads(decrypted)
return json.loads(decrypted).get("credentials")
def get_wcapi():

View File

@@ -13,6 +13,7 @@ class ConfigEncryptor:
encrypted_data = self.fernet.encrypt(json_data.encode())
with open(self.filename, "wb") as encrypted_file:
encrypted_file.write(encrypted_data)
print("Credentials saved")
def get_key(self):
return self.key.decode()
@@ -38,7 +39,11 @@ class ConfigEncryptor:
with open(self.filename, "rb") as encrypted_file:
encrypted_data = encrypted_file.read()
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:
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.
"""
import tkinter as tk
from tkinter import ttk
import customtkinter
import customtkinter as ctk
from ui.log_window import LogWindow
from ui.local_processing_tab import LocalProcessingTab
from ui.settings_tab import SettingsTab
from config.decrypt_config import ConfigDecryptor, DECRYPTION_KEY
from config.encrypt_config import ConfigEncryptor
class ImageProcessorApp:
"""
@@ -21,42 +19,69 @@ class ImageProcessorApp:
Initialize the ImageProcessorApp.
Args:
root (tk.Tk): The root Tkinter window.
root (ctk.CTk): The root CustomTkinter window.
"""
self.root = root
self.root.title("Image Processor")
self.root.geometry("500x450")
self.root.geometry("520x600")
self.tab_parent = ttk.Notebook(self.root)
self.log_window = None
# Create menu frame at the top
menu_frame = ctk.CTkFrame(self.root)
menu_frame.pack(side="top", fill="x")
self.local_processing_tab = LocalProcessingTab(
self.tab_parent, "Local Processing", self.open_log_window
)
self.settings_tab = SettingsTab(self.tab_parent, "Settings")
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)
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.log_window = LogWindow(self.root)
else:
self.log_window.lift()
self.local_processing_tab.tab.tkraise()
def show_settings_tab(self):
"""
Show the Settings tab.
"""
self.settings_tab.tab.tkraise()
def run(self):
"""
Run the Tkinter main loop.
Run the CustomTkinter main loop.
"""
self.root.mainloop()
if __name__ == "__main__":
try:
decryptor = ConfigDecryptor(DECRYPTION_KEY)
config = decryptor.decrypt()
decryptor = ConfigEncryptor(DECRYPTION_KEY)
config = decryptor.load_config()
wc_url = config["url"]
wc_consumer_key = config["consumer_key"]
wc_consumer_secret = config["consumer_secret"]
@@ -65,6 +90,8 @@ if __name__ == "__main__":
except FileNotFoundError as 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.run()

View File

@@ -4,40 +4,39 @@ Module for the Local Processing Tab in the Image Processor application.
import tempfile
import threading
import tkinter as tk
from tkinter import ttk
import customtkinter as ctk
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 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, pprint
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, text, log):
def __init__(self, tab_parent, log_window):
"""
Initialize the LocalProcessingTab.
Args:
tab_parent (ttk.Notebook): The parent notebook widget.
text (str): The text to display on the tab.
log (function): The function to log messages.
tab_parent (ctk.CTkFrame): The parent frame widget.
log_window (LogWindow): The log window frame.
"""
key = b"u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI="
self.log = log
self.tab = ttk.Frame(tab_parent)
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
tab_parent.add(self.tab, text=text)
self.config = ConfigEncryptor(key)
self.log_window = None
self.canvas_width = 900
self.canvas_height = 900
@@ -48,7 +47,7 @@ class LocalProcessingTab:
self.image_format = "AUTO"
self.image_size = "contain"
self.load_config()
self.source_type = StringVar(value="local")
self.source_type = StringVar(value="directory")
self.checkbox_var = BooleanVar(value=False)
self.file = FileProcessor()
self.image = ImageProcessor()
@@ -58,8 +57,8 @@ class LocalProcessingTab:
self.update_options()
def load_config(self):
config = self.config.load_config()
print(config)
if config:
if options := config.get("options"):
self.canvas_width = options.get("canvas_width", 900)
@@ -71,125 +70,126 @@ class LocalProcessingTab:
self.image_format = options.get("image_format", "AUTO")
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):
"""
Set up the user interface for the tab.
"""
# Source selection section
self.source_label = Label(self.tab, text="Source Type:")
self.source_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
current_row = 0
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,
textvariable=self.source_type,
values=["local", "product", "all_products"],
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, 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(
"<<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
self.options_button = ttk.Button(
# Options button
self.options_button = ctk.CTkButton(
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.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
self.before_label = Label(self.tab, text="Before:")
self.before_label.grid(row=5, 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.before_label = ctk.CTkLabel(self.tab, text="Before:")
self.before_label.grid(row=current_row, column=0, padx=5, pady=5, sticky="w")
self.after_label = Label(self.tab, text="After:")
self.after_label.grid(row=5, column=1, padx=5, pady=5, sticky="w")
self.after_image_label = Label(self.tab)
self.after_label = ctk.CTkLabel(self.tab, text="After:")
self.after_label.grid(row=current_row, column=3, padx=5, pady=5, sticky="w")
current_row += 1
self.after_image_label = ctk.CTkLabel(self.tab, text="")
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.
"""
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.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":
self.browse_button.grid_remove()
self.product_id_label.grid()
self.product_id_button.grid()
self.product_id_entry.grid()
self.additional_name_label.grid_remove()
self.additional_name_entry.grid_remove()
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()
elif self.source_type.get() == "file":
self.browse_file_button.grid()
self.update_previews()
def update_previews(self, before_path=None, after_path=None):
@@ -200,39 +200,40 @@ class LocalProcessingTab:
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 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:
before_img = Image.open(before_path)
before_img.thumbnail((150, 150))
before_img.thumbnail((200, 200))
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
after_img = Image.open(after_path)
after_img.thumbnail((150, 150))
after_img.thumbnail((200, 200))
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
else:
first_image_path = self.file.get_first_image_path()
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((150, 150))
before_photo = ImageTk.PhotoImage(before_img)
self.before_image_label.config(image=before_photo)
self.before_image_label.image = before_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((150, 150))
after_photo = ImageTk.PhotoImage(after_img)
self.after_image_label.config(image=after_photo)
self.after_image_label.image = after_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):
"""
@@ -240,24 +241,41 @@ class LocalProcessingTab:
Args:
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.thumbnail((150, 150))
photo = ImageTk.PhotoImage(img)
label.config(image=photo)
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:
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.update_previews()
def apply_canvas_size(self):
"""
Apply the canvas size settings and update previews.
@@ -276,7 +294,6 @@ class LocalProcessingTab:
"""
self.image.set_background_color(self.background_color)
def get_options(self) -> dict:
"""
Get the current processing options.
@@ -288,7 +305,7 @@ class LocalProcessingTab:
"selected_directory": self.browse_button.cget("text"),
"canvas_width": self.canvas_width,
"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,
"update_previews": self.update_previews,
"product_id": self.product_id_entry.get(),
@@ -333,7 +350,7 @@ class LocalProcessingTab:
"type": "color",
"label": "Background Color:",
"default": self.background_color
},
},
"image_format": {
"type": "dropdown",
"label": "Image Format:",
@@ -359,8 +376,7 @@ class LocalProcessingTab:
options (dict): The options to apply.
"""
if self.log_window:
self.log_window.destroy()
self.log_window = None
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"]
@@ -383,17 +399,16 @@ class LocalProcessingTab:
obj (object): The object to format and log.
"""
formatted_message = pformat(obj)
self.log_message(formatted_message)
self.log(formatted_message)
def start_processing(self):
"""
Start the image processing based on the selected options.
"""
self.create_log_window()
source = self.source_type.get()
options = self.get_options()
if source == "local":
if source == "directory":
threading.Thread(
target=self.file.process_directory_with_logging, args=(options,)
).start()
@@ -402,6 +417,11 @@ class LocalProcessingTab:
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,

View File

@@ -1,18 +1,37 @@
from tkinter import Toplevel, Text
from pprint import pprint
import customtkinter as ctk
class LogWindow(Toplevel):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.title("Log Window")
self.geometry("500x300")
self.text = Text(self)
self.text.pack(expand=True, fill="both")
self.protocol("WM_DELETE_WINDOW", self.hide)
class LogWindow:
def __init__(self, parent):
self.frame = ctk.CTkFrame(parent)
self.frame.pack(expand=True, fill="both")
def log(self, message):
self.text.insert("end", pprint(message) + "\n")
self.text.see("end")
self.log_text = ctk.CTkTextbox(self.frame, state="disabled", wrap="word", height=10)
self.log_text.pack(side="left", expand=True, fill="both")
def hide(self):
self.withdraw()
self.scrollbar = ctk.CTkScrollbar(self.frame, command=self.log_text.yview)
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
from tkinter import ttk, colorchooser, messagebox
from pprint import pprint
class OptionsWindow(tk.Toplevel):
import customtkinter as ctk
from tkinter import colorchooser, messagebox
class OptionsWindow(ctk.CTkToplevel):
def __init__(self, parent, apply_callback, current_options):
super().__init__(parent)
self.title("Options")
@@ -12,6 +13,8 @@ class OptionsWindow(tk.Toplevel):
self.inputs = {}
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):
"""
@@ -19,27 +22,33 @@ class OptionsWindow(tk.Toplevel):
"""
self.row_index = 0
for name, details in self.options.items():
if details["type"] == "number":
self.add_number_input(
name,
details["label"],
details["default"],
details["min"],
details["max"],
)
elif details["type"] == "text":
self.add_text_input(name, details["label"], details["default"])
elif details["type"] == "checkbox":
self.add_checkbox(name, details["label"], details["default"])
elif details["type"] == "dropdown":
self.add_dropdown(
name, details["label"], details["options"], details["default"]
)
elif details["type"] == "color":
self.add_color_picker(name, details["label"], details["default"])
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":
self.add_number_input(
name,
details["label"],
details["default"],
details["min"],
details["max"],
)
elif details["type"] == "text":
self.add_text_input(name, details["label"], details["default"])
elif details["type"] == "checkbox":
self.add_checkbox(name, details["label"], details["default"])
elif details["type"] == "dropdown":
self.add_dropdown(
name, details["label"], details["options"], details["default"]
)
elif details["type"] == "color":
self.add_color_picker(name, details["label"], details["default"])
def add_number_input(self, name, label, default, min_val, max_val):
"""
Add a number input field.
@@ -51,10 +60,10 @@ class OptionsWindow(tk.Toplevel):
min_val (int): The minimum value.
max_val (int): The maximum value.
"""
lbl = tk.Label(self, text=label)
lbl.grid(row=self.row_index, columnspan=1,column=0, padx=5, pady=5, sticky="w")
lbl = ctk.CTkLabel(self, text=label)
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.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.
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")
entry = tk.Entry(self)
entry = ctk.CTkEntry(self)
entry.insert(0, default)
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.
default (bool): The default value.
"""
var = tk.BooleanVar(value=default)
chk = tk.Checkbutton(self, text=label, variable=var)
var = ctk.BooleanVar(value=default)
chk = ctk.CTkCheckBox(self, text=label, variable=var)
chk.grid(row=self.row_index, column=0,
columnspan=2, padx=5, pady=5, sticky="w")
@@ -112,32 +121,24 @@ class OptionsWindow(tk.Toplevel):
options (list): The list of options.
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")
combo = ttk.Combobox(self, values=options, state="readonly")
combo = ctk.CTkComboBox(self, values=options, state="readonly")
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.row_index += 1
def check_transparent(self, var, color_entry, pick_button, color_preview):
if var.get():
color_entry.config(state="disabled")
pick_button.config(state="disabled")
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, button):
self.attributes('-topmost', False) # Temporarily disable topmost to allow colorchooser to be on top
color_code = colorchooser.askcolor(parent=self, title="Choose color")[1]
self.attributes('-topmost', True) # Re-enable topmost for this window
def pick_color(self, color_entry, color_preview):
color_code = colorchooser.askcolor(title="Choose color")[1]
if color_code:
color_entry.delete(0, tk.END)
color_entry.insert(0, color_code)
color_preview.config(bg=color_code)
button.configure(fg_color=color_code)
self.inputs[button.name]["color"] = color_code
def add_color_picker(self, name, label, default):
"""
@@ -148,36 +149,32 @@ class OptionsWindow(tk.Toplevel):
label (str): The label for the color picker.
default (str): The default color.
"""
if default == "transparent":
default = "#ffffff"
var = tk.BooleanVar(value=True)
else:
var = tk.BooleanVar(value=False)
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")
color_preview = tk.Label(self, bg=default, width=2, height=1)
color_preview.grid(row=self.row_index, column=1, padx=5, pady=5, sticky="w")
color_button = ctk.CTkButton(self, text="", width=30, command=lambda: self.pick_color(color_button))
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)
color_entry.insert(0, default)
color_entry.grid(row=self.row_index, column=2, padx=5, pady=5, sticky="w")
chk_var = ctk.BooleanVar(value=(default == "transparent"))
chk = ctk.CTkCheckBox(self, text="Transparent", variable=chk_var, command=lambda: self.check_transparent(chk_var, color_button))
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))
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.inputs[name] = {"type": "color", "button": color_button, "transparent_var": chk_var, "color": default}
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):
"""
Create the apply button.
"""
apply_button = tk.Button(
apply_button = ctk.CTkButton(
self, text="Apply", command=self.apply_options)
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":
options[name] = details["widget"].get()
elif details["type"] == "color":
if "value" in details:
options[name] = details["value"]
else:
if details["transparent_var"].get():
options[name] = "transparent"
else:
options[name] = details["color"]
self.apply_callback(options)
self.destroy()
@@ -222,33 +219,7 @@ class OptionsWindow(tk.Toplevel):
condition (function): The condition function that returns a boolean.
"""
if condition():
if self.inputs[name]["type"] == "number":
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"]
)
self.create_option(name, self.inputs[name])
# Example usage
@@ -256,7 +227,7 @@ if __name__ == "__main__":
def apply_options(options):
print(options)
root = tk.Tk()
root = ctk.CTk()
current_options = {
"canvas_width": {"type": "number", "label": "Width:", "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
from tkinter import ttk
import customtkinter as ctk
from api.woocommerce_api import save_credentials, load_credentials
class SettingsTab:
def __init__(self, tab_parent, text):
self.tab = ttk.Frame(tab_parent)
tab_parent.add(self.tab, text=text)
def __init__(self, tab_parent):
self.tab = ctk.CTkFrame(tab_parent)
self.tab.grid(row=0, column=0, sticky="nsew")
self.credentials = load_credentials()
self.inputs = {}
self.setup_ui()
def setup_ui(self):
url_label = tk.Label(self.tab, text="WooCommerce URL:")
url_label.pack(pady=5)
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": "*"}
}
self.url_entry = tk.Entry(self.tab)
self.url_entry.insert(0, self.credentials.get('url', ''))
self.url_entry.pack(pady=5)
row_index = 0
for name, details in settings_options.items():
self.create_setting(name, details, row_index)
row_index += 1
consumer_key_label = tk.Label(self.tab, text="Consumer Key:")
consumer_key_label.pack(pady=5)
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)
self.consumer_key_entry = tk.Entry(self.tab)
self.consumer_key_entry.insert(0, self.credentials.get('consumer_key', ''))
self.consumer_key_entry.pack(pady=5)
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")
consumer_secret_label = tk.Label(self.tab, text="Consumer Secret:")
consumer_secret_label.pack(pady=5)
self.consumer_secret_entry = tk.Entry(self.tab, show="*")
self.consumer_secret_entry.insert(0, self.credentials.get('consumer_secret', ''))
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)
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
def save_credentials(self):
save_credentials(
self.url_entry.get(),
self.consumer_key_entry.get(),
self.consumer_secret_entry.get(),
self.username_entry.get(),
self.password_entry.get()
self.inputs["url"].get(),
self.inputs["consumer_key"].get(),
self.inputs["consumer_secret"].get(),
self.inputs["username"].get(),
self.inputs["password"].get()
)

View File

@@ -11,6 +11,7 @@ class FileProcessor:
"""
def __init__(self):
self.selected_file = ""
self.selected_directory = ""
def browse_directory(self):
@@ -23,6 +24,16 @@ class FileProcessor:
self.selected_directory = filedialog.askdirectory()
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):
"""
Get the path of the first image in the selected directory.
@@ -30,6 +41,9 @@ class FileProcessor:
Returns:
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:
return None
@@ -159,6 +173,31 @@ class FileProcessor:
os.remove(file_path)
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):
"""
Generate the output path for resized images based on a template.