From 52bca275ce112262d13cb09f47e302677f0c5e09 Mon Sep 17 00:00:00 2001 From: coraxcode Date: Sat, 1 Jun 2024 00:36:59 -0300 Subject: [PATCH] Update GIFCraft.py --- GIFCraft.py | 92 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/GIFCraft.py b/GIFCraft.py index ead4dde..3546da2 100644 --- a/GIFCraft.py +++ b/GIFCraft.py @@ -3,6 +3,7 @@ from tkinter import filedialog, messagebox, Frame, Canvas, Menu, Checkbutton, In from PIL import Image, ImageTk, ImageSequence import os + class GIFEditor: def __init__(self, master): """Initialize the GIF editor with the main window and UI setup.""" @@ -45,13 +46,13 @@ class GIFEditor: self.create_file_menu() self.create_edit_menu() self.create_animation_menu() - self.create_help_menu() # Add this line to include the Help menu + self.create_help_menu() self.master.config(menu=self.menu_bar) def create_file_menu(self): """Create the File menu.""" file_menu = Menu(self.menu_bar, tearoff=0) - file_menu.add_command(label="New", command=self.new_file, accelerator="Ctrl+N") # Add this line + file_menu.add_command(label="New", command=self.new_file, accelerator="Ctrl+N") file_menu.add_command(label="Load GIF/PNG/WebP", command=self.load_file, accelerator="Ctrl+O") file_menu.add_separator() file_menu.add_command(label="Save", command=self.save, accelerator="Ctrl+S") @@ -73,6 +74,8 @@ class GIFEditor: edit_menu.add_separator() edit_menu.add_command(label="Check/Uncheck All", command=self.toggle_check_all) edit_menu.add_separator() + edit_menu.add_command(label="Resize All Frames", command=self.resize_all_frames_dialog) + edit_menu.add_separator() edit_menu.add_command(label="Undo", command=self.undo, accelerator="Ctrl+Z") edit_menu.add_command(label="Redo", command=self.redo, accelerator="Ctrl+Y") self.menu_bar.add_cascade(label="Edit", menu=edit_menu) @@ -86,7 +89,7 @@ class GIFEditor: def create_help_menu(self): """Create the Help menu.""" help_menu = Menu(self.menu_bar, tearoff=0) - help_menu.add_command(label="About", command=self.show_about) # Add this line to create the About menu item + help_menu.add_command(label="About", command=self.show_about) self.menu_bar.add_cascade(label="Help", menu=help_menu) def setup_frame_list(self): @@ -198,6 +201,7 @@ class GIFEditor: self.checkbox_vars = [] self.current_file = None self.frame_index = 0 + self.base_size = None # Clear the base size self.update_frame_list() self.show_frame() self.update_title() @@ -217,14 +221,15 @@ class GIFEditor: try: with Image.open(file_path) as img: - for frame in ImageSequence.Iterator(img): - self.frames.append(self.center_image(self.resize_image(frame.copy()))) + for i, frame in enumerate(ImageSequence.Iterator(img)): + if i == 0: + self.base_size = frame.size # Store the size of the first frame + self.frames.append(self.resize_to_base_size(frame.copy())) delay = int(frame.info.get('duration', 100)) # Ensure delay is always an integer self.delays.append(delay) var = IntVar() var.trace_add('write', lambda *args, i=len(self.checkbox_vars): self.set_current_frame(i)) self.checkbox_vars.append(var) - self.resize_all_frames() # Resize all frames to the largest size self.frame_index = 0 self.update_frame_list() self.show_frame() @@ -233,12 +238,54 @@ class GIFEditor: except Exception as e: messagebox.showerror("Error", f"Failed to load file: {e}") - def resize_all_frames(self): - """Resize all frames to the largest frame size.""" - max_width = max(frame.width for frame in self.frames) - max_height = max(frame.height for frame in self.frames) + def resize_to_base_size(self, image): + """Resize the image to the base size of the first frame and center it.""" + if hasattr(self, 'base_size'): + base_width, base_height = self.base_size + new_image = Image.new("RGBA", self.base_size, (0, 0, 0, 0)) + image = image.resize(self.base_size, Image.Resampling.LANCZOS) + new_image.paste(image, ((base_width - image.width) // 2, (base_height - image.height) // 2)) + return new_image + return image + + def resize_all_frames_dialog(self): + """Open a dialog to get the new size and resize all frames.""" + dialog = tk.Toplevel(self.master) + dialog.title("Resize All Frames") + + tk.Label(dialog, text="New Width:").pack(pady=5) + width_entry = tk.Entry(dialog) + width_entry.pack(pady=5) + + tk.Label(dialog, text="New Height:").pack(pady=5) + height_entry = tk.Entry(dialog) + height_entry.pack(pady=5) + + def resize(): + try: + new_width = int(width_entry.get()) + new_height = int(height_entry.get()) + self.resize_all_frames(new_width, new_height) + dialog.destroy() + except ValueError: + messagebox.showerror("Invalid Input", "Please enter valid integers for width and height.") + + tk.Button(dialog, text="Resize", command=resize).pack(pady=10) + dialog.grab_set() + + def resize_all_frames(self, new_width=None, new_height=None): + """Resize all frames to the specified width and height.""" + if new_width is None or new_height is None: + if hasattr(self, 'base_size'): + new_width, new_height = self.base_size + else: + return # If no dimensions provided and no base size, do nothing + + self.save_state() # Save the state before making changes for i, frame in enumerate(self.frames): - self.frames[i] = self.center_image(frame.resize((max_width, max_height), Image.Resampling.LANCZOS)) + self.frames[i] = frame.resize((new_width, new_height), Image.Resampling.LANCZOS) + self.show_frame() + self.update_frame_list() def resize_image(self, image, max_width=800, max_height=600): """Resize the image to fit within the specified max width and height.""" @@ -249,16 +296,6 @@ class GIFEditor: image = image.resize(new_size, Image.Resampling.LANCZOS) return image - def center_image(self, image): - """Center the image within the maximum frame size.""" - max_width = max((frame.width for frame in self.frames + [image]), default=image.width) - max_height = max((frame.height for frame in self.frames + [image]), default=image.height) - - new_image = Image.new("RGBA", (max_width, max_height), (255, 255, 255, 0)) - new_image.paste(image, ((max_width - image.width) // 2, (max_height - image.height) // 2)) - - return new_image - def add_image(self): """Add images to the frames.""" file_paths = filedialog.askopenfilenames(filetypes=[("Image files", "*.jpg *.jpeg *.png *.webp *.gif *.bmp")]) @@ -269,14 +306,16 @@ class GIFEditor: try: for file_path in file_paths: with Image.open(file_path) as image: - # Resize and center the new image to match the maximum dimensions - image = self.center_image(self.resize_to_max_dimensions(image.copy())) + # Resize the new image to match the base dimensions + if not self.frames: # If no frames, set the base size to the first image's size + self.base_size = image.size + image = self.resize_to_base_size(image.copy()) self.frames.append(image) self.delays.append(100) # Default delay for added images var = IntVar() var.trace_add('write', lambda *args, i=len(self.checkbox_vars): self.set_current_frame(i)) self.checkbox_vars.append(var) - + self.update_frame_list() self.show_frame() except Exception as e: @@ -517,11 +556,13 @@ class GIFEditor: self.checkbox_vars = [IntVar(value=state) for state in checkbox_states] for i, var in enumerate(self.checkbox_vars): var.trace_add('write', lambda *args, i=i: self.set_current_frame(i)) + self.base_size = self.frames[0].size if self.frames else None # Reset base size based on the remaining frames self.update_frame_list() self.show_frame() self.update_title() self.check_all.set(False) # Reset the check_all variable to ensure consistency + def redo(self, event=None): """Redo the last undone action.""" if self.redo_stack: @@ -554,7 +595,9 @@ class GIFEditor: """Display the About dialog.""" messagebox.showinfo("About GIFCraft", "GIFCraft - GIF Editor\nVersion 1.0\n© 2024 by Seehrum") + def main(): + """Main function to initialize the GIF editor.""" root = tk.Tk() app = GIFEditor(master=root) try: @@ -563,5 +606,6 @@ def main(): print("Program interrupted with Ctrl+C") root.destroy() + if __name__ == "__main__": main()