This is a fully functional app with a distributable exe file, just one exe file, to make a simple thing, split the frames of a gif and save them as individual png, using a minimalistic Graphic User Interface made with the built in module tkinter.
When you launch the app you will get this GUI with just one button.
After you choose your file, you will see it into the GUI.
Now you push the button at the buttom of the GUI and you will get these images in the folder.
Let’s make the exe file
Now you can use both pyinstaller of auto-py-to-exe to make it. Here is what you need to do, using auto-py-to-exe. It makes the work really easy.
Hands on the code
This is the script
import tkinter as tk from PIL import Image, ImageTk from PIL import Image, ImageSequence from tkinter import filedialog import os import sys def resource_path(relative_path): """ Get absolute path to resource, works for dev and for PyInstaller """ base_path = getattr( sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__))) return os.path.join(base_path, relative_path) def unpack_gif(src): "If disposal == 2 (unclomplete) it copies it on the previous" image = Image.open(src) frames = [] disposal = [] for gifFrame in ImageSequence.Iterator(image): disposal.append(gifFrame.disposal_method) frames.append(gifFrame.convert('P')) output = [] lastFrame = None thisFrame = None for i, loadedFrame in enumerate(frames): thisFrame = loadedFrame if disposal[i] == 2: if i != 0: lastFrame.paste(thisFrame, mask=thisFrame.convert('RGBA')) output.append(lastFrame) else: output.append(thisFrame) elif disposal[i] == 1 or disposal[i] == 0: output.append(thisFrame) else: raise ValueError('Disposal Methods other than 2:Restore to Background,\ 1:Do Not Dispose, and 0:No Disposal are supported at this time') lastFrame = loadedFrame return output def save_all_frames(file): "Saves all the frames of the gif as numbered png files" name = os.path.splitext(file[0])[0] im = unpack_gif(file[0]) for n, i in enumerate(im): if n<10: zero = "0" else: zero = "" i.save(f"{name}{zero}{n}.png") print(im) # root.withdraw() def getfile(): global filename try: filename = filedialog.askopenfilenames( parent=root, initialdir=".", initialfile='tmp', filetypes=[("GIF", "*.gif"), ("All files", "*")] ) print(filename) img = Image.open(resource_path(filename[0])) img = img.resize((150, 150)) img = ImageTk.PhotoImage(img) getfile.image = img label["image"] = img button2.pack() except: pass filename = "" root = tk.Tk() img2 = tk.PhotoImage(file=resource_path("gif_split.png")) label1 = tk.Label(master=root, image=img2) label1.pack() button = tk.Button( master=root, bg="pink", text="Open Gif", command=getfile) button.pack() # Label with the image of the gif (after you choose it) img3 = ImageTk.PhotoImage(file=resource_path("arrowup.png")) label = tk.Label(master=root, image=img3, ) label.pack() button2 = tk.Button( master=root, bg="gold", text="Save png files out of the gif", command=lambda: save_all_frames(filename)) # button2.pack() root.mainloop() # save_all_frames(filename)