『幻影織成破断』は、画像を元に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()