ICO作成ツール『断影結晶符転』 ソースコード

以下は『断影結晶符転』のソースコード全文です:


        from PIL import Image, ImageTk
        import tkinter as tk
        from tkinter import filedialog, messagebox
        import os
        from datetime import datetime
        
        # 使用可能なサイズ
        AVAILABLE_SIZES = [16, 32, 48, 64, 128, 256]
        
        class IconConverterApp:
            def __init__(self, root):
                self.root = root
                self.root.title("ICO作成ツール - 断影結晶符転")
                self.root.geometry("500x500")
                self.root.resizable(False, False)
        
                self.image_path = None
                self.img_preview_label = None
                self.check_vars = {}
        
                self.create_widgets()
        
            def create_widgets(self):
                # 画像選択ボタン
                tk.Button(self.root, text="画像を選択", command=self.select_image, font=("Arial", 12)).pack(pady=10)
        
                # プレビュー用キャンバス
                self.preview_canvas = tk.Canvas(self.root, width=256, height=256, bg="gray90")
                self.preview_canvas.pack(pady=10)
        
                # サイズ選択チェックボックス
                tk.Label(self.root, text="作成するサイズを選択:", font=("Arial", 11)).pack()
                self.check_vars = {size: tk.BooleanVar(value=True) for size in AVAILABLE_SIZES}
                check_frame = tk.Frame(self.root)
                check_frame.pack(pady=5)
                for size in AVAILABLE_SIZES:
                    cb = tk.Checkbutton(check_frame, text=f"{size}x{size}", variable=self.check_vars[size])
                    cb.pack(side=tk.LEFT, padx=5)
        
                # 実行ボタン
                tk.Button(self.root, text="実行", command=self.create_ico, font=("Arial", 12)).pack(pady=15)
        
            def select_image(self):
                path = filedialog.askopenfilename(
                    title="画像ファイルを選択してください",
                    filetypes=[("画像ファイル", "*.png;*.jpg;*.jpeg;*.bmp")]
                )
        
                if not path:
                    return
        
                try:
                    img = Image.open(path)
                except Exception as e:
                    messagebox.showerror("エラー", f"画像の読み込みに失敗しました:\n{e}")
                    return
        
                self.image_path = path
                self.display_preview(img)
        
                if img.width != img.height:
                    messagebox.showwarning("警告", "選択された画像は正方形ではありません。\n歪んだアイコンになる可能性があります。")
        
            def display_preview(self, img):
                img_resized = img.copy().resize((256, 256), Image.LANCZOS)
                self.tk_preview_image = ImageTk.PhotoImage(img_resized)
                self.preview_canvas.delete("all")
                self.preview_canvas.create_image(128, 128, image=self.tk_preview_image)
        
            def create_ico(self):
                if not self.image_path:
                    messagebox.showwarning("警告", "まず画像を選択してください。")
                    return
        
                selected_sizes = [size for size, var in self.check_vars.items() if var.get()]
                if not selected_sizes:
                    messagebox.showwarning("警告", "少なくとも1つのサイズを選択してください。")
                    return
        
                try:
                    img = Image.open(self.image_path)
                except Exception as e:
                    messagebox.showerror("エラー", f"画像の再読み込みに失敗しました:\n{e}")
                    return
        
                # 保存ファイル名と場所
                base = os.path.splitext(os.path.basename(self.image_path))[0]
                now_str = datetime.now().strftime("%Y%m%d-%H%M%S")
                save_path = os.path.join(os.path.dirname(self.image_path), f"{base}-{now_str}.ico")
        
                try:
                    img.save(save_path, format="ICO", sizes=[(s, s) for s in selected_sizes])
                    messagebox.showinfo("保存完了", f"アイコンを保存しました:\n{save_path}")
                except Exception as e:
                    messagebox.showerror("保存失敗", f"ICOファイルの保存に失敗しました:\n{e}")
        
        # アプリ起動
        if __name__ == "__main__":
            root = tk.Tk()
            app = IconConverterApp(root)
            root.mainloop()