so I made this Python GUI program that started with a very simple function: using askfiledialog to input one image, enter the target size in MB, resize the image, output in askfiledialog again. I decided to expand the program so that it will accept multiple input and using if-else statement to determine whether the input files is single or multiple; if the input is multiple, instead of using askfiledialog, it will default to save all the resized images in the original file path and the file names will be original file name + "_(target size)"
after running it i found that only the last image is resized and saved successfully. I have used AI to help diagnose the issue but it keeps chasing its own tail and told me it has changed the code but in fact was just repeating the code i input to it.
this is the complete code of the program:
import os
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image
import io
from itertools import zip_longest
class ImageResizer:
def __init__(self, master):
self.master = master
master.title("Image Resizer")
self.mode = None
self.label = tk.Label(master, text="Select an image to resize")
self.label.pack()
self.select_button = tk.Button(master, text="Select Image", command=self.select_image)
self.select_button.pack()
self.size_entry = tk.Entry(master)
self.size_entry.pack()
self.size_entry.insert(0, "Enter target size in MB")
self.size_entry.bind("<FocusIn>", self.clear_text)
self.size_entry.bind("<FocusOut>", self.reset_text)
self.maintain_pixels_var = tk.IntVar()
self.maintain_pixels_cb = tk.Checkbutton(master, text="Maintain original pixels",
variable=self.maintain_pixels_var)
self.maintain_pixels_cb.pack()
self.maintain_orientation_var = tk.IntVar()
self.maintain_orientation_cb = tk.Checkbutton(master, text="Maintain original orientation",
variable=self.maintain_orientation_var)
self.maintain_orientation_cb.pack()
self.save_button = tk.Button(master, text="Save resized image", command=self.save_image, state="disabled")
self.save_button.pack()
self.file_path = None
self.image = None
self.format = None
self.width = None
self.height = None
self.file_paths = None # Store the list of file paths
self.images = None # Store the list of images
self.formats = None # Store the list of formats
self.widths = None
self.heights = None
def select_image(self):
self.file_path = filedialog.askopenfilename(filetypes=(("jpeg files", "*.jpg"), ("png files", "*.png")),multiple=True)
self.file_paths = self.master.tk.splitlist(self.file_path)
num_images = len(self.file_paths)
if num_images > 1:
images = []
formats = []
widths = []
heights = []
for file_path in self.file_paths:
try:
image = Image.open(file_path)
format = image.format
width = image.width
height = image.height
images.append(image)
formats.append(format)
widths.append(width)
heights.append(height)
self.images = images
self.formats = formats
self.heights = heights
self.widths = widths
except:
print(f"Invalid file path: {file_path}")
self.images = images
self.formats = formats
self.heights = heights
self.widths = widths
self.label['text'] = f"Selected {num_images} images"
self.save_button['state'] = "normal"
print("Heights:", heights)
print("Widths:", widths)
messagebox.showinfo("Heights and Widths", f"Heights: {heights}\nWidths: {widths}")
else:
self.file_path = self.file_paths[0]
self.image = Image.open(self.file_path)
self.format = self.image.format
self.label['text'] = f"Selected {self.file_path} \nSize: {os.path.getsize(self.file_path)/1e6:.2f}MB \nDimensions: {self.image.size}"
# Determine if image is landscape or portrait
if self.image.width > self.image.height:
self.label['text'] += "\nOrientation: Landscape"
else:
self.label['text'] += "\nOrientation: Portrait"
self.save_button['state'] = "normal"
def save_image(self):
size_str = self.size_entry.get()
save_paths = []
if self.images:
for i, (image, format, height, width, file_path) in enumerate(
zip_longest(self.images, self.formats, self.heights, self.widths, self.file_paths)):
ratio = (float(size_str) * 1e6 / os.path.getsize(file_path)) ** 0.5
new_size = (int(height * ratio), int(width * ratio))
if self.maintain_orientation_var.get() and new_size[0] > image.width:
new_size = (image.width, image.height)
else:
resized_image = image.resize(new_size, Image.LANCZOS) if not self.maintain_pixels_var.get() else image
save_path = image.filename
save_path += f"{i}"
save_path = os.path.splitext(file_path)[0] + f"_{size_str}" + os.path.splitext(file_path)[1]
save_paths.append(save_path)
for i, save_path in enumerate(save_paths):
# get the resized image at the corresponding index
resized_img = self.images[i]
# save the resized image to the new file with the save path
resized_img.save(save_path)
# print the save path to the console
print(f"Saved image {i} to {save_path}")
messagebox.showinfo("Success", f"Saved {len(self.images)} resized images")
else:
target_size = float(size_str) * 1e6 # convert to bytes
# get the ratio of the change in size
ratio = (target_size / os.path.getsize(self.file_path)) ** 0.5
new_size = (int(self.image.width * ratio), int(self.image.height * ratio))
if self.maintain_orientation_var.get() and new_size[0] < new_size[1]: # If image is meant to be landscape
new_size = (new_size[1], new_size[0])
resized_image = self.image.resize(new_size, Image.LANCZOS) if not self.maintain_pixels_var.get() else self.image
save_path = filedialog.asksaveasfilename(defaultextension=".jpg", filetypes=(("jpeg files", "*.jpg"), ("png files", "*.png")))
quality = 95
while True:
with io.BytesIO() as temp_file:
resized_image.save(temp_file, format=self.format, optimize=True, quality=quality)
if temp_file.tell() <= target_size or quality <= 10:
break
quality -= 5 # reduce quality by 5
resized_image.save(save_path, format=self.format, optimize=True, quality=quality)
messagebox.showinfo("Success", f"Image saved to {save_path}")
def clear_text(self, event):
if self.size_entry.get() == 'Enter target size in MB':
self.size_entry.delete(0, 'end')
def reset_text(self, event):
if not self.size_entry.get():
self.size_entry.insert(0, 'Enter target size in MB')
root = tk.Tk()
my_gui = ImageResizer(root)
root.mainloop()