Refactored code to be more maintainable
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
/config.enc
|
config.enc
|
||||||
/__pycache__
|
/__pycache__
|
||||||
*/__pycache__
|
*/__pycache__
|
||||||
/build
|
/build
|
||||||
|
|||||||
30
config/decrypt_config.py
Normal file
30
config/decrypt_config.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from cryptography.fernet import Fernet
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
class ConfigDecryptor:
|
||||||
|
def __init__(self, key):
|
||||||
|
self.key = key
|
||||||
|
|
||||||
|
def decrypt(self):
|
||||||
|
if not os.path.exists("config.enc"):
|
||||||
|
raise FileNotFoundError("The encrypted configuration file 'config.enc' does not exist.")
|
||||||
|
|
||||||
|
fernet = Fernet(self.key)
|
||||||
|
with open("config.enc", "rb") as encrypted_file:
|
||||||
|
encrypted = encrypted_file.read()
|
||||||
|
decrypted = fernet.decrypt(encrypted).decode()
|
||||||
|
return json.loads(decrypted)
|
||||||
|
# Define your key here
|
||||||
|
key = b'u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=' # Replace with your actual key
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
key = b'u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI=' # Replace with your actual key
|
||||||
|
decryptor = ConfigDecryptor(key)
|
||||||
|
try:
|
||||||
|
config = decryptor.decrypt()
|
||||||
|
print(config)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print(e)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"An error occurred: {e}")
|
||||||
28
config/encrypt_config.py
Normal file
28
config/encrypt_config.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from cryptography.fernet import Fernet
|
||||||
|
|
||||||
|
class ConfigEncryptor:
|
||||||
|
def __init__(self):
|
||||||
|
self.key = Fernet.generate_key()
|
||||||
|
|
||||||
|
def encrypt_config(self, data):
|
||||||
|
fernet = Fernet(self.key)
|
||||||
|
encrypted = fernet.encrypt(data.encode())
|
||||||
|
with open("config.enc", "wb") as encrypted_file:
|
||||||
|
encrypted_file.write(encrypted)
|
||||||
|
|
||||||
|
def get_key(self):
|
||||||
|
return self.key.decode()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
config_data = """
|
||||||
|
{
|
||||||
|
"url": "https://yourstore.com",
|
||||||
|
"consumer_key": "ck_yourconsumerkey",
|
||||||
|
"consumer_secret": "cs_yoursecret",
|
||||||
|
"username": "yourusername",
|
||||||
|
"password": "yourpassword"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
encryptor = ConfigEncryptor()
|
||||||
|
print(f"Encryption key: {encryptor.get_key()}")
|
||||||
|
encryptor.encrypt_config(config_data)
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
from cryptography.fernet import Fernet
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Hardcoded key (replace with your generated key)
|
|
||||||
key = b'u4xTBY5Ns4WYdLvqMjEr138mpMmDEhhqTszKCcDy2cI='
|
|
||||||
|
|
||||||
def decrypt_config(key):
|
|
||||||
if not os.path.exists("config.enc"):
|
|
||||||
raise FileNotFoundError("The encrypted configuration file 'config.enc' does not exist.")
|
|
||||||
|
|
||||||
fernet = Fernet(key)
|
|
||||||
with open("config.enc", "rb") as encrypted_file:
|
|
||||||
encrypted = encrypted_file.read()
|
|
||||||
decrypted = fernet.decrypt(encrypted).decode()
|
|
||||||
return json.loads(decrypted)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
config = decrypt_config(key)
|
|
||||||
print(config) # Use the decrypted config
|
|
||||||
except FileNotFoundError as e:
|
|
||||||
print(e)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"An error occurred: {e}")
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
from cryptography.fernet import Fernet
|
|
||||||
|
|
||||||
# Generate a key and print it
|
|
||||||
key = Fernet.generate_key()
|
|
||||||
print(f"Encryption key: {key.decode()}") # Save this key securely
|
|
||||||
|
|
||||||
# Function to encrypt the configuration data
|
|
||||||
def encrypt_config(data, key):
|
|
||||||
fernet = Fernet(key)
|
|
||||||
encrypted = fernet.encrypt(data.encode())
|
|
||||||
with open("config.enc", "wb") as encrypted_file:
|
|
||||||
encrypted_file.write(encrypted)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
config_data = """{
|
|
||||||
"url": "https://yourstore.com",
|
|
||||||
"consumer_key": "ck_yourconsumerkey",
|
|
||||||
"consumer_secret": "cs_yoursecret",
|
|
||||||
"username": "yourusername",
|
|
||||||
"password": "yourpassword"
|
|
||||||
}"""
|
|
||||||
encrypt_config(config_data, key)
|
|
||||||
68
main.py
68
main.py
@@ -1,47 +1,47 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from ui.local_processing_tab import create_tab_local
|
|
||||||
from ui.settings_tab import create_tab_settings
|
|
||||||
from ui.log_window import LogWindow
|
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, key
|
||||||
|
|
||||||
|
class ImageProcessorApp:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
self.root.title("Image Processor")
|
||||||
|
self.root.geometry("700x400")
|
||||||
|
|
||||||
from decrypt_config import decrypt_config, key
|
self.tab_parent = ttk.Notebook(self.root)
|
||||||
|
self.log_window = None
|
||||||
|
|
||||||
try:
|
self.local_processing_tab = LocalProcessingTab(self.tab_parent, "Local Processing", self.open_log_window)
|
||||||
config = decrypt_config(key)
|
self.settings_tab = SettingsTab(self.tab_parent, "Settings")
|
||||||
|
|
||||||
|
self.tab_parent.pack(expand=True, fill='both')
|
||||||
|
|
||||||
|
def open_log_window(self):
|
||||||
|
if self.log_window is None or not self.log_window.winfo_exists():
|
||||||
|
self.log_window = LogWindow(self.root)
|
||||||
|
else:
|
||||||
|
self.log_window.lift()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.root.mainloop()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
decryptor = ConfigDecryptor(key)
|
||||||
|
config = decryptor.decrypt()
|
||||||
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']
|
||||||
wp_username = config['username']
|
wp_username = config['username']
|
||||||
wp_password = config['password']
|
wp_password = config['password']
|
||||||
|
except FileNotFoundError as e:
|
||||||
# Now use these variables to create your WooCommerce API instance
|
print(f"File not found: {e}")
|
||||||
|
except Exception as e:
|
||||||
except FileNotFoundError as e:
|
|
||||||
print(e)
|
|
||||||
# Handle the missing file case, e.g., by prompting the user to create it
|
|
||||||
except Exception as e:
|
|
||||||
print(f"An error occurred: {e}")
|
print(f"An error occurred: {e}")
|
||||||
# Handle other potential errors
|
|
||||||
log_window = None
|
|
||||||
|
|
||||||
def open_log_window():
|
root = tk.Tk()
|
||||||
global log_window
|
app = ImageProcessorApp(root)
|
||||||
if log_window is None or not log_window.winfo_exists():
|
app.run()
|
||||||
log_window = LogWindow(window)
|
|
||||||
else:
|
|
||||||
log_window.lift()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
window = tk.Tk()
|
|
||||||
window.title("Image Processor")
|
|
||||||
window.geometry("700x400")
|
|
||||||
|
|
||||||
tab_parent = ttk.Notebook(window)
|
|
||||||
|
|
||||||
create_tab_local(tab_parent, "Local Processing", open_log_window)
|
|
||||||
create_tab_settings(tab_parent, "Settings")
|
|
||||||
|
|
||||||
tab_parent.pack(expand=True, fill='both')
|
|
||||||
|
|
||||||
window.mainloop()
|
|
||||||
|
|||||||
133
readme.md
133
readme.md
@@ -1,59 +1,48 @@
|
|||||||
Image Processing Application
|
Image Processing Application Documentation
|
||||||
|
Overview
|
||||||
This application is a graphical user interface (GUI) tool designed for processing images either from a local directory or from a WooCommerce product catalog. It supports resizing images, renaming them based on customizable templates, and uploading them to a WooCommerce site.
|
|
||||||
Features
|
|
||||||
|
|
||||||
Resize images to specified dimensions.
|
|
||||||
Rename images using customizable templates with placeholders for various attributes.
|
|
||||||
Upload processed images to WooCommerce.
|
|
||||||
Process images from a local directory or WooCommerce products.
|
|
||||||
|
|
||||||
|
This Image Processing Application provides a GUI for selecting directories containing image files, setting canvas sizes, adding suffixes to filenames, and processing images. It logs all found images, displays how many images were found, and provides previews of the first image before and after processing.
|
||||||
Prerequisites
|
Prerequisites
|
||||||
|
|
||||||
Python 3.10 or later
|
Python 3.10+: Ensure Python is installed on your machine.
|
||||||
|
Tkinter: This should come with Python, but ensure it's installed.
|
||||||
Required Python Packages
|
Pillow: For image handling. Install via pip:
|
||||||
|
|
||||||
You can install the required packages using pip:
|
|
||||||
|
|
||||||
sh
|
sh
|
||||||
|
|
||||||
pip install -r requirements.txt
|
pip install pillow
|
||||||
|
|
||||||
ImageMagick
|
Additional Libraries: Ensure you have any additional libraries your utility functions (file_operations, image_processing) depend on.
|
||||||
|
|
||||||
ImageMagick is required for image processing. You can download and install it from ImageMagick's official website.
|
Application Setup
|
||||||
|
|
||||||
WooCommerce API Credentials
|
Install Required Packages:
|
||||||
|
|
||||||
You need to have WooCommerce API credentials to interact with the WooCommerce site. These include the URL, consumer key, consumer secret, username, and password.
|
|
||||||
|
|
||||||
Setting Up
|
|
||||||
|
|
||||||
Clone the Repository
|
|
||||||
|
|
||||||
Clone this repository to your local machine:
|
|
||||||
|
|
||||||
sh
|
sh
|
||||||
|
|
||||||
git clone https://github.com/your-username/image-processing-app.git
|
pip install pillow
|
||||||
cd image-processing-app
|
|
||||||
|
|
||||||
Install Dependencies
|
Directory Structure:
|
||||||
|
Ensure your project directory looks something like this:
|
||||||
|
|
||||||
Install the required Python packages:
|
arduino
|
||||||
|
|
||||||
sh
|
image-processor/
|
||||||
|
├── ui/
|
||||||
|
│ └── local_processing_tab.py
|
||||||
|
├── utils/
|
||||||
|
│ ├── file_operations.py
|
||||||
|
│ └── image_processing.py
|
||||||
|
└── main.py
|
||||||
|
|
||||||
pip install -r requirements.txt
|
Script Files:
|
||||||
|
main.py: The main entry point of the application containing the script provided above.
|
||||||
Configure WooCommerce API Credentials
|
file_operations.py: Contains browse_directory and other directory-related utility functions.
|
||||||
|
image_processing.py: Contains set_canvas_size and other image processing utility functions.
|
||||||
Run the application and navigate to the Settings tab to enter and save your WooCommerce API credentials.
|
|
||||||
|
|
||||||
Running the Application
|
Running the Application
|
||||||
|
|
||||||
Run the main application script:
|
To run the application, navigate to the project directory and execute:
|
||||||
|
|
||||||
sh
|
sh
|
||||||
|
|
||||||
@@ -61,75 +50,23 @@ python main.py
|
|||||||
|
|
||||||
Creating an Executable
|
Creating an Executable
|
||||||
|
|
||||||
To create a standalone executable from this project, you can use PyInstaller. Follow the steps below:
|
To create an executable for this application, you can use pyinstaller. Follow the steps below:
|
||||||
|
|
||||||
Install PyInstaller
|
Install PyInstaller:
|
||||||
|
|
||||||
You can install PyInstaller using pip:
|
|
||||||
|
|
||||||
sh
|
sh
|
||||||
|
|
||||||
pip install pyinstaller
|
pip install pyinstaller
|
||||||
|
|
||||||
Generate the Executable
|
Create the Executable:
|
||||||
|
Navigate to the directory containing your main.py script and run:
|
||||||
Navigate to the project directory and run PyInstaller:
|
|
||||||
|
|
||||||
sh
|
sh
|
||||||
|
|
||||||
pyinstaller --onefile --noconsole main.py
|
pyinstaller --onefile --windowed main.py
|
||||||
|
|
||||||
Find the Executable
|
--onefile: Packages everything into a single executable file.
|
||||||
|
--windowed: Ensures the console window does not appear when running the GUI application.
|
||||||
|
|
||||||
After PyInstaller finishes, you can find the executable in the dist directory.
|
Locate the Executable:
|
||||||
|
After running the command, you will find the executable in the dist folder within your project directory.
|
||||||
File Structure
|
|
||||||
|
|
||||||
main.py: Entry point for the application.
|
|
||||||
api/: Contains the woocommerce.py file for interacting with WooCommerce API.
|
|
||||||
utils/: Contains utility scripts for file operations and image processing.
|
|
||||||
ui/: Contains the UI components for the application.
|
|
||||||
config.enc: Encrypted file storing WooCommerce API credentials.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
|
|
||||||
Create a requirements.txt file with the following content:
|
|
||||||
|
|
||||||
tk
|
|
||||||
Pillow
|
|
||||||
requests
|
|
||||||
woocommerce
|
|
||||||
cryptography
|
|
||||||
pyinstaller
|
|
||||||
|
|
||||||
Usage
|
|
||||||
|
|
||||||
Select Source Type
|
|
||||||
Choose between local directory and WooCommerce product.
|
|
||||||
|
|
||||||
Set Canvas Size
|
|
||||||
Specify the dimensions to resize the images.
|
|
||||||
|
|
||||||
Filename Template
|
|
||||||
Define a template for renaming the images using placeholders like {name}, {sku}, {width}, {height}.
|
|
||||||
|
|
||||||
Start Processing
|
|
||||||
Click the "Start Processing" button to process the images. The log will appear in a separate window.
|
|
||||||
|
|
||||||
Notes
|
|
||||||
|
|
||||||
Ensure ImageMagick is correctly installed and added to your system's PATH.
|
|
||||||
The application uses the cryptography library to encrypt WooCommerce API credentials for security.
|
|
||||||
|
|
||||||
Support
|
|
||||||
|
|
||||||
If you encounter any issues or have questions, feel free to open an issue in the repository or contact the maintainer.
|
|
||||||
License
|
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
|
||||||
Acknowledgements
|
|
||||||
|
|
||||||
ImageMagick
|
|
||||||
WooCommerce
|
|
||||||
|
|
||||||
With this information, you should be able to set up, run, and create an executable for the Image Processing Application. Enjoy processing your images with ease!
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from tkinter.scrolledtext import ScrolledText
|
from tkinter.scrolledtext import ScrolledText
|
||||||
from tkinter import filedialog
|
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
@@ -9,196 +8,161 @@ from utils.file_operations import browse_directory, process_directory_with_loggi
|
|||||||
from utils.image_processing import set_canvas_size, resize_image
|
from utils.image_processing import set_canvas_size, resize_image
|
||||||
from api.woocommerce import process_product_images, process_all_products
|
from api.woocommerce import process_product_images, process_all_products
|
||||||
|
|
||||||
|
class LocalProcessingTab:
|
||||||
|
def __init__(self, tab_parent, text, log):
|
||||||
|
self.log = log
|
||||||
|
self.tab = ttk.Frame(tab_parent)
|
||||||
|
tab_parent.add(self.tab, text=text)
|
||||||
|
|
||||||
canvas_width = 900
|
self.log_window = None
|
||||||
canvas_height = 900
|
self.create_log_window()
|
||||||
|
|
||||||
|
self.canvas_width = 900
|
||||||
|
self.canvas_height = 900
|
||||||
|
|
||||||
def create_tab_local(tab_parent, text, log):
|
self.source_type = tk.StringVar(value="local")
|
||||||
tab = ttk.Frame(tab_parent)
|
self.checkbox_var = tk.BooleanVar(value=False)
|
||||||
tab_parent.add(tab, text=text)
|
|
||||||
|
|
||||||
log_window = None
|
self.setup_ui()
|
||||||
|
self.update_source_fields()
|
||||||
|
|
||||||
def create_log_window():
|
def create_log_window(self):
|
||||||
nonlocal log_window
|
if self.log_window and tk.Toplevel.winfo_exists(self.log_window):
|
||||||
if log_window and tk.Toplevel.winfo_exists(log_window):
|
|
||||||
return
|
return
|
||||||
log_window = tk.Toplevel()
|
self.log_window = tk.Toplevel()
|
||||||
log_window.title("Processing Log")
|
self.log_window.title("Processing Log")
|
||||||
log_text = ScrolledText(log_window, state='disabled', wrap='word', height=20, width=80)
|
self.log_text = ScrolledText(self.log_window, state='disabled', wrap='word', height=20, width=80)
|
||||||
log_text.pack(expand=True, fill='both')
|
self.log_text.pack(expand=True, fill='both')
|
||||||
|
|
||||||
def log_message(message):
|
def log_message(self, message):
|
||||||
log_text.config(state='normal')
|
self.log_text.config(state='normal')
|
||||||
log_text.insert(tk.END, message + "\n")
|
self.log_text.insert(tk.END, message + "\n")
|
||||||
log_text.see(tk.END)
|
self.log_text.see(tk.END)
|
||||||
log_text.config(state='disabled')
|
self.log_text.config(state='disabled')
|
||||||
log_text.update_idletasks() # Ensure the GUI updates
|
self.log_text.update_idletasks()
|
||||||
return log_message
|
|
||||||
|
def setup_ui(self):
|
||||||
|
|
||||||
# Source selection section
|
# Source selection section
|
||||||
source_label = tk.Label(tab, text="Source Type:")
|
self.source_label = tk.Label(self.tab, text="Source Type:")
|
||||||
source_label.grid(row=0, column=0, padx=5, pady=5, sticky='w')
|
self.source_label.grid(row=0, column=0, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
source_type = tk.StringVar(value="local")
|
self.source_type = tk.StringVar(value="local")
|
||||||
source_dropdown = ttk.Combobox(tab, textvariable=source_type, values=["local", "product", "all_products"], state="readonly")
|
self.source_dropdown = ttk.Combobox(self.tab, textvariable=self.source_type, values=["local", "product", "all_products"], state="readonly")
|
||||||
source_dropdown.grid(row=1, column=0, padx=5, pady=5, sticky='w')
|
self.source_dropdown.grid(row=1, column=0, padx=5, pady=5, sticky='w')
|
||||||
source_dropdown.bind("<<ComboboxSelected>>", lambda e: update_source_fields())
|
self.source_dropdown.bind("<<ComboboxSelected>>", lambda e: self.update_source_fields())
|
||||||
|
|
||||||
# Local Directory selection section
|
self.browse_button = ttk.Button(self.tab, text="Browse", command=self.browse_directory_command)
|
||||||
def update_directory():
|
self.browse_button.grid(row=2, column=0, padx=5, pady=5, sticky='w')
|
||||||
directory = browse_directory()
|
|
||||||
if directory:
|
|
||||||
# Show only the last directory name with ../
|
|
||||||
truncated_directory = f"../{os.path.basename(directory)}"
|
|
||||||
button_browse.config(text=truncated_directory)
|
|
||||||
update_previews()
|
|
||||||
|
|
||||||
button_browse = tk.Button(tab, text="Browse", command=update_directory)
|
|
||||||
button_browse.grid(row=2, column=0, padx=5, pady=5, sticky='nw')
|
|
||||||
|
|
||||||
# WooCommerce Product ID section
|
# WooCommerce Product ID section
|
||||||
product_id_label = tk.Label(tab, text="Product ID:")
|
self.product_id_label = tk.Label(self.tab, text="Product ID:")
|
||||||
product_id_label.grid(row=2, column=0, padx=5, pady=5, sticky='w')
|
self.product_id_label.grid(row=2, column=0, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
|
self.product_id_entry = tk.Entry(self.tab)
|
||||||
|
self.product_id_entry.grid(row=3, column=0, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
product_id_entry = tk.Entry(tab)
|
|
||||||
product_id_entry.grid(row=3, column=0, padx=5, pady=5, sticky='w')
|
|
||||||
# SKU section
|
# SKU section
|
||||||
additional_name_label = tk.Label(tab, text="Add suffix:")
|
self.additional_name_label = tk.Label(self.tab, text="Add suffix:")
|
||||||
additional_name_label.grid(row=3, column=1, padx=5, pady=5, sticky='w')
|
self.additional_name_label.grid(row=3, column=1, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
additional_name_entry = tk.Entry(tab)
|
self.additional_name_entry = tk.Entry(self.tab)
|
||||||
additional_name_entry.grid(row=3, column=2, padx=5, pady=5, sticky='w')
|
self.additional_name_entry.grid(row=3, column=2, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
# Template section
|
# Template section
|
||||||
template_label = tk.Label(tab, text="Filename Template:")
|
self.template_label = tk.Label(self.tab, text="Filename Template:")
|
||||||
template_label.grid(row=3, column=1, padx=5, pady=5, sticky='w')
|
self.template_label.grid(row=3, column=1, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
template_entry = tk.Entry(tab)
|
self.template_entry = tk.Entry(self.tab)
|
||||||
template_entry.insert(0, "{slug}_{sku}_{width}x{height}")
|
self.template_entry.insert(0, "{slug}_{sku}_{width}x{height}")
|
||||||
template_entry.grid(row=3, column=2, padx=5, columnspan=2, pady=5, sticky='w')
|
self.template_entry.grid(row=3, column=2, padx=5, columnspan=2, pady=5, sticky='w')
|
||||||
|
|
||||||
|
|
||||||
def update_source_fields():
|
|
||||||
print(source_type.get())
|
|
||||||
if source_type.get() == "local":
|
|
||||||
button_browse.grid()
|
|
||||||
product_id_label.grid_remove()
|
|
||||||
product_id_entry.grid_remove()
|
|
||||||
additional_name_label.grid()
|
|
||||||
additional_name_entry.grid()
|
|
||||||
template_entry.grid_remove()
|
|
||||||
template_label.grid_remove()
|
|
||||||
elif source_type.get() == "product":
|
|
||||||
button_browse.grid_remove()
|
|
||||||
product_id_label.grid()
|
|
||||||
product_id_entry.grid()
|
|
||||||
additional_name_label.grid_remove()
|
|
||||||
additional_name_entry.grid_remove()
|
|
||||||
else:
|
|
||||||
button_browse.grid_remove()
|
|
||||||
product_id_label.grid_remove()
|
|
||||||
product_id_entry.grid_remove()
|
|
||||||
product_id_label.grid()
|
|
||||||
product_id_entry.grid()
|
|
||||||
additional_name_label.grid_remove()
|
|
||||||
additional_name_entry.grid_remove()
|
|
||||||
|
|
||||||
update_source_fields() # Initial call to set correct fields
|
|
||||||
|
|
||||||
# Canvas size section
|
# Canvas size section
|
||||||
width_label = tk.Label(tab, text="Canvas Width:")
|
width_label = tk.Label(self.tab, text="Canvas Width:")
|
||||||
width_label.grid(row=0, column=1, padx=5, pady=5, sticky='w')
|
width_label.grid(row=0, column=1, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
width_entry = tk.Entry(tab)
|
self.width_entry = tk.Entry(self.tab)
|
||||||
width_entry.insert(0, "900")
|
self.width_entry.insert(0, "900")
|
||||||
width_entry.grid(row=0, column=2, padx=5, pady=5, sticky='w')
|
self.width_entry.grid(row=0, column=2, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
height_label = tk.Label(tab, text="Canvas Height:")
|
|
||||||
height_label.grid(row=1, column=1, padx=5, pady=5, sticky='w')
|
|
||||||
|
|
||||||
height_entry = tk.Entry(tab)
|
|
||||||
height_entry.insert(0, "900")
|
|
||||||
height_entry.grid(row=1, column=2, padx=5, pady=5, sticky='w')
|
|
||||||
|
|
||||||
def apply_canvas_size():
|
|
||||||
global canvas_width, canvas_height
|
|
||||||
canvas_width = int(width_entry.get())
|
|
||||||
canvas_height = int(height_entry.get())
|
|
||||||
set_canvas_size(canvas_width, canvas_height)
|
|
||||||
update_previews()
|
|
||||||
|
|
||||||
button_set_size = tk.Button(tab, text="Save Size", command=apply_canvas_size)
|
|
||||||
button_set_size.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky='w')
|
|
||||||
|
|
||||||
|
|
||||||
|
self.height_label = tk.Label(self.tab, text="Canvas Height:")
|
||||||
|
self.height_label.grid(row=1, column=1, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
|
self.height_entry = tk.Entry(self.tab)
|
||||||
|
self.height_entry.insert(0, "900")
|
||||||
|
self.height_entry.grid(row=1, column=2, padx=5, pady=5, sticky='w')
|
||||||
|
self.button_set_size = tk.Button(self.tab, text="Save Size", command=self.apply_canvas_size)
|
||||||
|
self.button_set_size.grid(row=2, column=1, columnspan=2, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
# Checkbox for deleting images
|
# Checkbox for deleting images
|
||||||
checkbox_var = tk.BooleanVar()
|
self.checkbox_var = tk.BooleanVar()
|
||||||
checkbox = tk.Checkbutton(tab, text="Delete image when done", variable=checkbox_var)
|
self.checkbox = tk.Checkbutton(self.tab, text="Delete image when done", variable=self.checkbox_var)
|
||||||
checkbox.grid(row=0, column=3, columnspan=2, padx=5, pady=5, sticky='w')
|
self.checkbox.grid(row=0, column=3, columnspan=2, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
# Start Processing button
|
self.button_start = tk.Button(self.tab, text="Start Processing", command=self.start_processing)
|
||||||
def start_processing():
|
self.button_start.grid(row=1, column=3, columnspan=2, padx=5, pady=5, sticky='w')
|
||||||
log_message = None # create_log_window()
|
|
||||||
if source_type.get() == "local":
|
|
||||||
process_directory_with_logging(additional_name_entry.get(), checkbox_var.get(), log_message, update_previews)
|
|
||||||
elif source_type.get() == "product":
|
|
||||||
product_id = product_id_entry.get()
|
|
||||||
process_product_images(product_id, template_entry.get(), canvas_width, canvas_height)
|
|
||||||
else:
|
|
||||||
process_all_products(template_entry.get(), canvas_width, canvas_height)
|
|
||||||
|
|
||||||
button_start = tk.Button(tab, text="Start Processing", command=start_processing)
|
|
||||||
button_start.grid(row=1, column=3, columnspan=2, padx=5, pady=5, sticky='w')
|
|
||||||
|
|
||||||
# Image previews
|
# Image previews
|
||||||
before_label = tk.Label(tab, text="Before:")
|
self.before_label = tk.Label(self.tab, text="Before:")
|
||||||
before_label.grid(row=5, column=0, columnspan=2, padx=5, pady=5, sticky='w')
|
self.before_label.grid(row=5, column=0, padx=5, pady=5, sticky='w')
|
||||||
before_image_label = tk.Label(tab)
|
self.before_image_label = tk.Label(self.tab)
|
||||||
before_image_label.grid(row=6, columnspan=2, column=0, padx=5, pady=5, sticky='w')
|
self.before_image_label.grid(row=6, column=0, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
after_label = tk.Label(tab, text="After:")
|
self.after_label = tk.Label(self.tab, text="After:")
|
||||||
after_label.grid(row=5, columnspan=2, column=2, padx=5, pady=5, sticky='w')
|
self.after_label.grid(row=5, column=1, padx=5, pady=5, sticky='w')
|
||||||
after_image_label = tk.Label(tab)
|
self.after_image_label = tk.Label(self.tab)
|
||||||
after_image_label.grid(row=6, columnspan=2, column=2, padx=5, pady=5, sticky='w')
|
self.after_image_label.grid(row=6, column=1, padx=5, pady=5, sticky='w')
|
||||||
|
|
||||||
def update_previews(before_path=None, after_path=None):
|
|
||||||
|
def update_source_fields(self):
|
||||||
|
source = self.source_type.get()
|
||||||
|
if source == "local":
|
||||||
|
self.browse_button.grid()
|
||||||
|
self.product_id_label.grid_remove()
|
||||||
|
self.product_id_entry.grid_remove()
|
||||||
|
self.update_previews()
|
||||||
|
else:
|
||||||
|
self.browse_button.grid_remove()
|
||||||
|
self.product_id_label.grid()
|
||||||
|
self.product_id_entry.grid()
|
||||||
|
|
||||||
|
def update_previews(self, before_path=None, after_path=None):
|
||||||
if before_path and after_path:
|
if before_path and after_path:
|
||||||
before_img = Image.open(before_path)
|
self.set_image_preview(before_path, self.before_image_label)
|
||||||
before_img.thumbnail((150, 150))
|
self.set_image_preview(after_path, self.after_image_label)
|
||||||
before_photo = ImageTk.PhotoImage(before_img)
|
|
||||||
before_image_label.config(image=before_photo)
|
|
||||||
before_image_label.image = before_photo
|
|
||||||
|
|
||||||
after_img = Image.open(after_path)
|
|
||||||
after_img.thumbnail((150, 150))
|
|
||||||
after_photo = ImageTk.PhotoImage(after_img)
|
|
||||||
after_image_label.config(image=after_photo)
|
|
||||||
after_image_label.image = after_photo
|
|
||||||
else:
|
else:
|
||||||
first_image_path = get_first_image_path()
|
first_image_path = get_first_image_path()
|
||||||
if first_image_path:
|
if first_image_path:
|
||||||
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file:
|
self.set_image_preview(first_image_path, self.before_image_label)
|
||||||
output_path = temp_file.name
|
self.after_image_label.config(image='')
|
||||||
resize_image(first_image_path, output_path, additional_name_entry.get())
|
|
||||||
before_img = Image.open(first_image_path)
|
|
||||||
before_img.thumbnail((150, 150))
|
|
||||||
before_photo = ImageTk.PhotoImage(before_img)
|
|
||||||
before_image_label.config(image=before_photo)
|
|
||||||
before_image_label.image = before_photo
|
|
||||||
|
|
||||||
after_img = Image.open(output_path)
|
def set_image_preview(self, image_path, label):
|
||||||
after_img.thumbnail((150, 150))
|
img = Image.open(image_path)
|
||||||
after_photo = ImageTk.PhotoImage(after_img)
|
img.thumbnail((150, 150))
|
||||||
after_image_label.config(image=after_photo)
|
photo = ImageTk.PhotoImage(img)
|
||||||
after_image_label.image = after_photo
|
label.config(image=photo)
|
||||||
|
label.image = photo
|
||||||
|
|
||||||
# Configure column weights to allow the log_text to expand
|
def browse_directory_command(self):
|
||||||
tab.columnconfigure(0, weight=1)
|
directory = browse_directory()
|
||||||
tab.columnconfigure(1, weight=1)
|
if directory:
|
||||||
tab.columnconfigure(2, weight=1)
|
self.browse_button.config(text=directory)
|
||||||
tab.columnconfigure(3, weight=1)
|
self.update_previews()
|
||||||
tab.rowconfigure(6, weight=1)
|
|
||||||
|
|
||||||
|
def apply_canvas_size(self):
|
||||||
|
self.canvas_width = int(self.width_entry.get())
|
||||||
|
self.canvas_height = int(self.height_entry.get())
|
||||||
|
set_canvas_size(self.canvas_width, self.canvas_height)
|
||||||
|
self.update_previews()
|
||||||
|
|
||||||
|
def start_processing(self):
|
||||||
|
source = self.source_type.get()
|
||||||
|
print(self.checkbox_var.get())
|
||||||
|
if source == "local":
|
||||||
|
process_directory_with_logging(self.browse_button.cget("text"), self.additional_name_entry.get(), self.checkbox_var.get(), self.log_message, self.update_previews)
|
||||||
|
elif source == "product":
|
||||||
|
process_product_images(self.product_id_entry.get(), self.canvas_width, self.canvas_height, self.log_message)
|
||||||
|
elif source == "all_products":
|
||||||
|
process_all_products(self.canvas_width, self.canvas_height, self.log_message)
|
||||||
|
self.update_previews()
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
import tkinter as tk
|
from tkinter import Toplevel, Text
|
||||||
from tkinter.scrolledtext import ScrolledText
|
|
||||||
|
|
||||||
class LogWindow(tk.Toplevel):
|
class LogWindow(Toplevel):
|
||||||
def __init__(self, parent):
|
def __init__(self, master=None, **kwargs):
|
||||||
super().__init__(parent)
|
super().__init__(master, **kwargs)
|
||||||
self.title("Processing Log")
|
self.title("Log Window")
|
||||||
self.geometry("600x400")
|
self.geometry("500x300")
|
||||||
self.log_text = ScrolledText(self, state='disabled', wrap='word')
|
self.text = Text(self)
|
||||||
self.log_text.pack(expand=True, fill='both')
|
self.text.pack(expand=True, fill='both')
|
||||||
|
self.protocol("WM_DELETE_WINDOW", self.hide)
|
||||||
|
|
||||||
def log(self, message):
|
def log(self, message):
|
||||||
self.log_text.config(state='normal')
|
self.text.insert('end', message + '\n')
|
||||||
self.log_text.insert(tk.END, message + "\n")
|
self.text.see('end')
|
||||||
self.log_text.see(tk.END)
|
|
||||||
self.log_text.config(state='disabled')
|
def hide(self):
|
||||||
|
self.withdraw()
|
||||||
|
|||||||
@@ -2,57 +2,58 @@ import tkinter as tk
|
|||||||
from tkinter import ttk
|
from tkinter import ttk
|
||||||
from api.woocommerce import save_credentials, load_credentials
|
from api.woocommerce import save_credentials, load_credentials
|
||||||
|
|
||||||
def create_tab_settings(tab_parent, text):
|
class SettingsTab:
|
||||||
tab = ttk.Frame(tab_parent)
|
def __init__(self, tab_parent, text):
|
||||||
tab_parent.add(tab, text=text)
|
self.tab = ttk.Frame(tab_parent)
|
||||||
|
tab_parent.add(self.tab, text=text)
|
||||||
|
|
||||||
credentials = load_credentials()
|
self.credentials = load_credentials()
|
||||||
url_value = credentials['url'] if credentials else ''
|
self.setup_ui()
|
||||||
consumer_key_value = credentials['consumer_key'] if credentials else ''
|
|
||||||
consumer_secret_value = credentials['consumer_secret'] if credentials else ''
|
|
||||||
username_value = credentials['username'] if credentials else ''
|
|
||||||
password_value = credentials['password'] if credentials else ''
|
|
||||||
|
|
||||||
url_label = tk.Label(tab, text="WooCommerce URL:")
|
def setup_ui(self):
|
||||||
|
url_label = tk.Label(self.tab, text="WooCommerce URL:")
|
||||||
url_label.pack(pady=5)
|
url_label.pack(pady=5)
|
||||||
|
|
||||||
url_entry = tk.Entry(tab)
|
self.url_entry = tk.Entry(self.tab)
|
||||||
url_entry.insert(0, url_value)
|
self.url_entry.insert(0, self.credentials.get('url', ''))
|
||||||
url_entry.pack()
|
self.url_entry.pack(pady=5)
|
||||||
|
|
||||||
consumer_key_label = tk.Label(tab, text="Consumer Key:")
|
consumer_key_label = tk.Label(self.tab, text="Consumer Key:")
|
||||||
consumer_key_label.pack(pady=5)
|
consumer_key_label.pack(pady=5)
|
||||||
|
|
||||||
consumer_key_entry = tk.Entry(tab)
|
self.consumer_key_entry = tk.Entry(self.tab)
|
||||||
consumer_key_entry.insert(0, consumer_key_value)
|
self.consumer_key_entry.insert(0, self.credentials.get('consumer_key', ''))
|
||||||
consumer_key_entry.pack()
|
self.consumer_key_entry.pack(pady=5)
|
||||||
|
|
||||||
consumer_secret_label = tk.Label(tab, text="Consumer Secret:")
|
consumer_secret_label = tk.Label(self.tab, text="Consumer Secret:")
|
||||||
consumer_secret_label.pack(pady=5)
|
consumer_secret_label.pack(pady=5)
|
||||||
|
|
||||||
consumer_secret_entry = tk.Entry(tab, show="*")
|
self.consumer_secret_entry = tk.Entry(self.tab, show="*")
|
||||||
consumer_secret_entry.insert(0, consumer_secret_value)
|
self.consumer_secret_entry.insert(0, self.credentials.get('consumer_secret', ''))
|
||||||
consumer_secret_entry.pack()
|
self.consumer_secret_entry.pack(pady=5)
|
||||||
|
|
||||||
username_label = tk.Label(tab, text="Username:")
|
username_label = tk.Label(self.tab, text="Username:")
|
||||||
username_label.pack(pady=5)
|
username_label.pack(pady=5)
|
||||||
|
|
||||||
username_entry = tk.Entry(tab)
|
self.username_entry = tk.Entry(self.tab)
|
||||||
username_entry.insert(0, username_value)
|
self.username_entry.insert(0, self.credentials.get('username', ''))
|
||||||
username_entry.pack()
|
self.username_entry.pack(pady=5)
|
||||||
|
|
||||||
password_label = tk.Label(tab, text="Password:")
|
password_label = tk.Label(self.tab, text="Password:")
|
||||||
password_label.pack(pady=5)
|
password_label.pack(pady=5)
|
||||||
|
|
||||||
password_entry = tk.Entry(tab, show="*")
|
self.password_entry = tk.Entry(self.tab, show="*")
|
||||||
password_entry.insert(0, password_value)
|
self.password_entry.insert(0, self.credentials.get('password', ''))
|
||||||
password_entry.pack()
|
self.password_entry.pack(pady=5)
|
||||||
|
|
||||||
button_save = tk.Button(tab, text="Save Credentials", command=lambda: save_credentials(
|
save_button = tk.Button(self.tab, text="Save Credentials", command=self.save_credentials)
|
||||||
url_entry.get(),
|
save_button.pack(pady=5)
|
||||||
consumer_key_entry.get(),
|
|
||||||
consumer_secret_entry.get(),
|
def save_credentials(self):
|
||||||
username_entry.get(),
|
save_credentials(
|
||||||
password_entry.get()
|
self.url_entry.get(),
|
||||||
))
|
self.consumer_key_entry.get(),
|
||||||
button_save.pack(pady=5)
|
self.consumer_secret_entry.get(),
|
||||||
|
self.username_entry.get(),
|
||||||
|
self.password_entry.get()
|
||||||
|
)
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ def get_first_image_path():
|
|||||||
return os.path.join(root, file)
|
return os.path.join(root, file)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def process_directory_with_logging(additional_name, is_checked, log, update_previews):
|
def process_directory_with_logging(selected_directory: str, additional_name: str = '', is_checked: bool = False, log = None, update_previews = None):
|
||||||
|
print(f"is_checked: {is_checked}")
|
||||||
if not selected_directory:
|
if not selected_directory:
|
||||||
messagebox.showwarning("No Directory", "Please select a directory.")
|
messagebox.showwarning("No Directory", "Please select a directory.")
|
||||||
return
|
return
|
||||||
@@ -56,12 +57,9 @@ def process_directory_with_logging(additional_name, is_checked, log, update_prev
|
|||||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||||
resize_image(file_path, output_path, additional_name)
|
resize_image(file_path, output_path, additional_name)
|
||||||
|
|
||||||
# with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file:
|
|
||||||
# temp_output_path = temp_file.name
|
|
||||||
# resize_image(file_path, temp_output_path, additional_name)
|
|
||||||
# update_previews(file_path, temp_output_path)
|
|
||||||
|
|
||||||
if os.path.exists(file_path) and is_checked:
|
if os.path.exists(file_path) and is_checked:
|
||||||
|
if log:
|
||||||
|
log(f"removing: {file_path}")
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
if log:
|
if log:
|
||||||
log(f"Processed: {file_path}")
|
log(f"Processed: {file_path}")
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
|
import os
|
||||||
from wand.image import Image
|
from wand.image import Image
|
||||||
from wand.color import Color
|
from wand.color import Color
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
canvas_width = 900
|
|
||||||
canvas_height = 900
|
|
||||||
|
|
||||||
def set_canvas_size(width, height):
|
def set_canvas_size(width, height):
|
||||||
global canvas_width, canvas_height
|
global canvas_width, canvas_height
|
||||||
@@ -12,6 +8,11 @@ def set_canvas_size(width, height):
|
|||||||
canvas_height = int(height)
|
canvas_height = int(height)
|
||||||
|
|
||||||
def resize_image(image_path, output_path, additional_name):
|
def resize_image(image_path, output_path, additional_name):
|
||||||
|
|
||||||
|
# Normalize the paths to ensure consistency
|
||||||
|
image_path = os.path.normpath(image_path)
|
||||||
|
output_path = os.path.normpath(output_path)
|
||||||
|
|
||||||
with Image(filename=image_path) as img:
|
with Image(filename=image_path) as img:
|
||||||
img.transform(resize=f'{canvas_width}x{canvas_height}>')
|
img.transform(resize=f'{canvas_width}x{canvas_height}>')
|
||||||
|
|
||||||
@@ -20,9 +21,14 @@ def resize_image(image_path, output_path, additional_name):
|
|||||||
|
|
||||||
with Image(width=canvas_width, height=canvas_height, background=Color('transparent')) as canvas:
|
with Image(width=canvas_width, height=canvas_height, background=Color('transparent')) as canvas:
|
||||||
canvas.composite(img, left=x_offset, top=y_offset)
|
canvas.composite(img, left=x_offset, top=y_offset)
|
||||||
|
# Create a new filename
|
||||||
new_filename = os.path.splitext(os.path.basename(output_path))[0]
|
new_filename = os.path.splitext(os.path.basename(output_path))[0]
|
||||||
if additional_name:
|
if additional_name:
|
||||||
new_filename += " - " + additional_name.strip()
|
new_filename += " - " + additional_name.strip()
|
||||||
new_filename += os.path.splitext(output_path)[1]
|
new_filename += os.path.splitext(output_path)[1]
|
||||||
output_path = os.path.join(os.path.dirname(output_path), new_filename)
|
# Construct the final output path
|
||||||
canvas.save(filename=output_path)
|
final_output_path = os.path.join(os.path.dirname(output_path), new_filename)
|
||||||
|
# Save the image to the final output path
|
||||||
|
canvas.save(filename=final_output_path)
|
||||||
|
print(f"Saved to: {final_output_path}")
|
||||||
|
set_canvas_size(900, 900)
|
||||||
Reference in New Issue
Block a user