GIF作成・分割ツール『幻影織成破断』 ソースコード

『幻影織成破断』は、画像を元にGIFアニメーションを生成したり、GIFを画像に分割したりできるツールです。

>>GUI外観 >>使い方

以下は『幻影織成破断』のソースコード全文です:

 
        import tkinter as tk
        from tkinter import filedialog, messagebox, simpledialog
        from PIL import Image, ImageTk
        import imageio
        import os
        from datetime import datetime
        
        class GIFTool:
            def __init__(self, root):
                self.root = root
                self.root.title("GIFアニメーション生成ツール - 幻影織成破断")
                self.root.geometry("500x600")
        
                self.mode = tk.StringVar(value="アニメーション生成モード")
                self.images = []
        
                self.create_widgets()
        
            def create_widgets(self):
                # メニューバーの作成
                menubar = tk.Menu(self.root)
                self.root.config(menu=menubar)
        
                # 「モード」メニュー
                mode_menu = tk.Menu(menubar, tearoff=0)
                menubar.add_cascade(label="モード", menu=mode_menu)
                mode_menu.add_command(label="アニメーション生成モード", command=self.set_animation_mode)
                mode_menu.add_command(label="コマ分割モード", command=self.set_split_mode)
        
                # 「コマ時間一括設定」メニュー
                menubar.add_command(label="コマ時間一括設定", command=self.batch_set_time)
        
                # モード表示用ラベル
                self.mode_label = tk.Label(self.root, text=f"現在のモード: {self.mode.get()}", anchor="e")
                self.mode_label.pack(fill=tk.X, padx=10)
        
                self.mode_frame = tk.Frame(self.root)
                self.mode_frame.pack(pady=20, fill=tk.BOTH, expand=True)
        
                self.update_mode()
        
            def update_mode(self):
                for widget in self.mode_frame.winfo_children():
                    widget.destroy()
        
                if self.mode.get() == "アニメーション生成モード":
                    self.create_animation_mode_widgets()
                else:
                    self.create_split_mode_widgets()
        
                # モードラベルを更新
                self.mode_label.config(text=f"現在のモード: {self.mode.get()}")
        
            def set_animation_mode(self):
                self.mode.set("アニメーション生成モード")
                self.update_mode()
        
            def set_split_mode(self):
                self.mode.set("コマ分割モード")
                self.update_mode()
        
            def create_animation_mode_widgets(self):
                # 画像追加ボタン
                button_frame = tk.Frame(self.mode_frame)
                button_frame.pack(pady=10, fill=tk.X)
        
                self.add_button = tk.Button(button_frame, text="画像追加", command=self.select_images)
                self.add_button.pack(side=tk.LEFT, padx=10)
        
                # 画像選択削除ボタン
                self.delete_button = tk.Button(button_frame, text="選択削除", command=self.delete_selected_image)
                self.delete_button.pack(side=tk.LEFT, padx=10)
        
                # 画像情報リストと「クリア」ボタンを横に並べる
                self.image_list_frame = tk.Frame(self.mode_frame)
                self.image_list_frame.pack(padx=10, pady=10, fill=tk.X)
        
                self.scrollbar = tk.Scrollbar(self.image_list_frame, orient=tk.VERTICAL)
                self.image_listbox = tk.Listbox(self.image_list_frame, width=60, height=10, yscrollcommand=self.scrollbar.set, selectmode=tk.SINGLE)
                self.scrollbar.config(command=self.image_listbox.yview)
                self.image_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
                self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
                # 「クリア」ボタンを画像情報リストの横に配置
                self.clear_button = tk.Button(self.image_list_frame, text="クリア", command=self.clear_animation_mode)
                self.clear_button.pack(side=tk.RIGHT, padx=10)
        
                # コマ時間指定エリアをスクロール可能にする
                self.time_frame_canvas = tk.Canvas(self.mode_frame)
                self.time_frame_scrollbar = tk.Scrollbar(self.mode_frame, orient=tk.VERTICAL, command=self.time_frame_canvas.yview)
                self.time_frame_canvas.config(yscrollcommand=self.time_frame_scrollbar.set)
        
                self.time_frame_scrollable = tk.Frame(self.time_frame_canvas)
        
                self.time_frame_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
                self.time_frame_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
                self.time_frame_canvas.create_window((0, 0), window=self.time_frame_scrollable, anchor="nw")
        
                self.time_frame_scrollable.bind(
                    "<Configure>",
                    lambda e: self.time_frame_canvas.config(scrollregion=self.time_frame_canvas.bbox("all"))
                )
        
                # 生成ボタン
                self.generate_button = tk.Button(self.mode_frame, text="生成", command=self.generate_gif)
                self.generate_button.pack(side=tk.BOTTOM, pady=10)
        
            def create_split_mode_widgets(self):
                # gif選択ボタン
                tk.Button(self.mode_frame, text="GIF選択", command=self.select_gif).pack(pady=10)
        
                # GIFファイルの情報表示
                self.gif_info_label = tk.Label(self.mode_frame, text="ファイルパス: \nコマ数: ")
                self.gif_info_label.pack(pady=10)
        
                # クリアボタン
                tk.Button(self.mode_frame, text="クリア", command=self.clear_split_mode).pack(pady=10)
        
                # コマ分割ボタン
                tk.Button(self.mode_frame, text="分割実行", command=self.split_gif).pack(pady=10)
        
            def select_images(self):
                files = filedialog.askopenfilenames(filetypes=[("Image Files", "*.bmp;*.gif;*.png;*.jpg;*.jpeg")])
                if files:
                    for img_path in files:
                        img = Image.open(img_path)
                        width, height = img.size
                        self.images.append((img_path, None))
                        self.image_listbox.insert(tk.END, f"{os.path.basename(img_path)} - {width}x{height}")
        
                    self.update_time_durations()
        
            def delete_selected_image(self):
                selected_index = self.image_listbox.curselection()
                if selected_index:
                    self.image_listbox.delete(selected_index)
                    self.images.pop(selected_index[0])  # 対応する画像を削除
                    self.update_time_durations()
                else:
                    messagebox.showwarning("選択エラー", "削除する画像を選択してください。")
        
            def update_time_durations(self):
                # コマ時間指定エリアを再描画
                for widget in self.time_frame_scrollable.winfo_children():
                    widget.destroy()
        
                for i, (img_path, _) in enumerate(self.images):
                    time_label = tk.Label(self.time_frame_scrollable, text=f"時間({os.path.basename(img_path)}):")
                    time_label.grid(row=i, column=0, padx=10, pady=5)
        
                    time_entry = tk.Entry(self.time_frame_scrollable)
                    time_entry.grid(row=i, column=1, padx=10, pady=5)
                    # 時間入力をimagesのタプルの2番目の要素に保持
                    self.images[i] = (img_path, time_entry)
        
            def generate_gif(self):
                try:
                    images = []
                    durations = []
        
                    for img_path, time_entry in self.images:
                        img = Image.open(img_path)
                        images.append(img)
                        # コマ時間を取得
                        time_duration = int(time_entry.get()) if time_entry.get() else 100
                        durations.append(time_duration)
        
                    gif_name = f"GIFani_{datetime.now().strftime('%Y%m%d_%H%M%S')}.gif"
                    output_path = os.path.join(os.path.expanduser("~"), "Desktop", gif_name)
                    images[0].save(output_path, save_all=True, append_images=images[1:], duration=durations, loop=0)
        
                    messagebox.showinfo("成功", f"アニメーションGIFを生成しました:\n{output_path}")
                except Exception as e:
                    messagebox.showerror("エラー", f"エラーが発生しました: {str(e)}")
        
            def clear_animation_mode(self):
                """アニメーション生成モードの選択画像をクリア"""
                self.images = []
                self.time_durations = []
                self.image_listbox.delete(0, tk.END)
                for widget in self.time_frame_scrollable.winfo_children():
                    widget.destroy()
        
            def select_gif(self):
                gif_file = filedialog.askopenfilename(filetypes=[("GIF Files", "*.gif")])
                if gif_file:
                    self.gif_path = gif_file
                    gif = imageio.mimread(self.gif_path)
                    self.gif_info_label.config(text=f"ファイルパス: {self.gif_path}\nコマ数: {len(gif)}")
        
            def split_gif(self):
                try:
                    gif = imageio.mimread(self.gif_path)
                    folder_name = os.path.join(os.path.expanduser("~"), "Desktop", os.path.splitext(os.path.basename(self.gif_path))[0])
                    if not os.path.exists(folder_name):
                        os.makedirs(folder_name)
        
                    for i, frame in enumerate(gif):
                        img = Image.fromarray(frame)
                        img.save(os.path.join(folder_name, f"{i+1}.gif"))
        
                    messagebox.showinfo("成功", f"コマを分割して保存しました。\n{folder_name}")
                except Exception as e:
                    messagebox.showerror("エラー", f"エラーが発生しました: {str(e)}")
        
            def clear_split_mode(self):
                """コマ分割モードの選択画像をクリア"""
                self.gif_path = None
                self.gif_info_label.config(text="ファイルパス: \nコマ数: ")
        
            def batch_set_time(self):
                # コマ時間一括設定ダイアログを開く
                time_input = simpledialog.askstring("コマ時間一括設定", "全てのコマ時間を設定してください(ミリ秒):", parent=self.root)
                if time_input:
                    try:
                        time_value = int(time_input)
                        for _, time_entry in self.images:
                            time_entry.delete(0, tk.END)
                            time_entry.insert(0, str(time_value))  # 全てのコマ時間を一括で設定
                        messagebox.showinfo("成功", "全てのコマ時間が設定されました。")
                    except ValueError:
                        messagebox.showerror("エラー", "無効なコマ時間が入力されました。")
        
        if __name__ == "__main__":
            root = tk.Tk()
            
            ##タイトルバーアイコン表示用
            data = '''R0lGODdhEAAQAIUAADc2N1NMR1FGOCwtM3VlTYt1S3NlZ1lTTEI8NqaKVaeUb2VZiH54mJGHcod8Z5B3r5OGmWtXQnNVNI1rOoh7mFxRPGNYdJuUkHlujYFdNGRNOaqdjK6TW4Nul4BvVX9yXXxkpXhgO15VhFpUf5mCoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAEAAQAEAIqQABCBxIsOBAAgkCLFjAgMGCEQcmSCgYQAGBABYMBCCwQYBBAhoQZCgwIQMACSEGGAQgoGKDAAFWBihw4MGDCxAegJiJgCCCChcNCAVZQEDPlQMECFC5suCBA0xXOmhAwIMCBQWmOojKkkAEAxAoQCBhgMCHowAOKOBggMICCgw6YDCQIMEBggUqiGCw4QIGCwIKGAyQoGiECIETLC04YEBPiQIbN50sMCAAOw==''' 
            root.tk.call('wm', 'iconphoto', root._w, tk.PhotoImage(data=data))
            ####
        
            app = GIFTool(root)
            root.mainloop()
        

    

📦 ダウンロード 🏠 ホームへ