key0527 发表于 2025-7-23 12:47

【DeepSeek创作】IMG2ICO,图片转ICO工具 源码

本帖最后由 key0527 于 2025-7-23 18:56 编辑







大概花了2小时沟通
DeepSeek 问小白 沟通过程:
https://www.wenxiaobai.com/share/chat/2a426d54-d18d-4821-92b4-d32ef1dc2ecd

所需依赖库:
pip install pillow pyinstaller ttkbootstrap

源码+图标+成品+打包bat 全在(后续什么工具都在这):
网盘链接: https://pan.baidu.com/s/5MCmy-a7m0B2N9b_z-28aHA?

import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image
import os
import sys
import subprocess

class ICOConverterApp:
    def __init__(self, root):
      # 初始化主窗口
      self.root = root
      self.root.title("图片转ICO图标工具")
      self.root.geometry("540x450")# 设置窗口大小为540x450
      self.root.resizable(False, False)
      
      # 尝试设置程序图标
      try:
            # 尝试从打包资源加载图标
            self.root.iconbitmap(self.resource_path("img2ico.ico"))
      except Exception:
            try:
                # 尝试从当前目录加载
                if os.path.exists("img2ico.ico"):
                  self.root.iconbitmap("img2ico.ico")
            except:
                pass
      
      # 创建主框架
      main_frame = ttk.Frame(root, padding=10)
      main_frame.pack(fill=tk.BOTH, expand=True)
      
      # 输入图片部分
      input_frame = ttk.LabelFrame(main_frame, text="输入设置", padding=(10, 5))
      input_frame.pack(fill=tk.X, pady=5)
      
      ttk.Label(input_frame, text="输入图片路径:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
      
      self.input_path = tk.StringVar()
      input_entry = ttk.Entry(input_frame, textvariable=self.input_path, width=40)
      input_entry.grid(row=0, column=1, padx=5, pady=5)
      
      input_btn = ttk.Button(input_frame, text="浏览...", command=self.browse_input)
      input_btn.grid(row=0, column=2, padx=5, pady=5)
      
      # 输出设置部分
      output_frame = ttk.LabelFrame(main_frame, text="输出设置", padding=(10, 5))
      output_frame.pack(fill=tk.X, pady=5)
      
      ttk.Label(output_frame, text="输出ICO路径:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
      
      self.output_path = tk.StringVar()
      output_entry = ttk.Entry(output_frame, textvariable=self.output_path, width=40)
      output_entry.grid(row=0, column=1, padx=5, pady=5)
      
      output_btn = ttk.Button(output_frame, text="浏览...", command=self.browse_output)
      output_btn.grid(row=0, column=2, padx=5, pady=5)
      
      # 尺寸选择部分(高度减少)
      size_frame = ttk.LabelFrame(main_frame, text="选择尺寸 (可多选)", padding=10)
      size_frame.pack(fill=tk.BOTH, expand=True, pady=5)
      
      # 创建滚动区域容器(固定高度为150像素)
      scroll_container = ttk.Frame(size_frame)
      scroll_container.pack(fill=tk.BOTH, expand=True)
      
      # 创建滚动区域
      canvas = tk.Canvas(scroll_container, height=150)# 限制高度为150px
      scrollbar = ttk.Scrollbar(scroll_container, orient="vertical", command=canvas.yview)
      scrollable_frame = ttk.Frame(canvas)
      
      # 配置滚动区域
      scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
      )
      canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
      canvas.configure(yscrollcommand=scrollbar.set)
      
      # 放置滚动区域
      canvas.pack(side="left", fill="both", expand=True)
      scrollbar.pack(side="right", fill="y")
      
      # 尺寸选择变量
      self.size_vars = {}
      # 所有可用尺寸
      all_sizes =
      
      # 默认选中的尺寸
      default_sizes =
      
      # 创建尺寸选择复选框 (每行5个以节省空间)
      for i, size in enumerate(all_sizes):
            self.size_vars = tk.BooleanVar(value=(size in default_sizes))
            chk = ttk.Checkbutton(scrollable_frame, text=f"{size}x{size}",
                                 variable=self.size_vars)
            # 每行5个
            chk.grid(row=i//5, column=i%5, sticky=tk.W, padx=8, pady=3)# 减少边距
      
      # 按钮区域 - 放在尺寸框架外面
      btn_frame = ttk.Frame(main_frame)
      btn_frame.pack(fill=tk.X, pady=10)
      
      # 左侧按钮组
      left_btn_frame = ttk.Frame(btn_frame)
      left_btn_frame.pack(side=tk.LEFT, fill=tk.X)
      
      select_all_btn = ttk.Button(left_btn_frame, text="全选", command=self.select_all_sizes, width=8)
      select_all_btn.pack(side=tk.LEFT, padx=5)
      
      select_none_btn = ttk.Button(left_btn_frame, text="清空", command=self.select_no_sizes, width=8)
      select_none_btn.pack(side=tk.LEFT, padx=5)
      
      # 中间按钮组
      center_btn_frame = ttk.Frame(btn_frame)
      center_btn_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
      
      # 右侧按钮组
      right_btn_frame = ttk.Frame(btn_frame)
      right_btn_frame.pack(side=tk.RIGHT, fill=tk.X)
      
      # 主要操作按钮放在中间
      convert_btn = ttk.Button(center_btn_frame, text="开始转换", command=self.convert_image, width=15)
      convert_btn.pack(side=tk.LEFT, padx=10)
      
      exit_btn = ttk.Button(right_btn_frame, text="退出程序", command=self.exit_app, width=10)
      exit_btn.pack(side=tk.RIGHT, padx=10)
      
      # 状态栏
      self.status_var = tk.StringVar(value="就绪")
      status_bar = ttk.Label(root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
      status_bar.pack(side=tk.BOTTOM, fill=tk.X)
      
      # 设置初始值
      self.output_path.set(os.path.join(os.getcwd(), "output.ico"))
   
    def resource_path(self, relative_path):
      """ 获取资源的绝对路径,用于打包后的程序 """
      try:
            # PyInstaller创建的临时文件夹路径
            base_path = sys._MEIPASS
      except Exception:
            base_path = os.path.abspath(".")
      return os.path.join(base_path, relative_path)
   
    def select_all_sizes(self):
      """全选所有尺寸"""
      for var in self.size_vars.values():
            var.set(True)
   
    def select_no_sizes(self):
      """清除所有尺寸选择"""
      for var in self.size_vars.values():
            var.set(False)
   
    def browse_input(self):
      """浏览并选择输入图片文件"""
      filetypes = (
            ("图片文件", "*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.webp"),
            ("所有文件", "*.*")
      )
      filepath = filedialog.askopenfilename(
            title="选择图片文件",
            initialdir=os.getcwd(),
            filetypes=filetypes
      )
      if filepath:
            self.input_path.set(filepath)
            # 自动设置输出文件名 - 在源文件同目录下
            output_dir = os.path.dirname(filepath)
            filename = os.path.splitext(os.path.basename(filepath)) + ".ico"
            self.output_path.set(os.path.join(output_dir, filename))
   
    def browse_output(self):
      """浏览并选择输出ICO文件路径"""
      filetypes = (
            ("ICO文件", "*.ico"),
            ("所有文件", "*.*")
      )
      filepath = filedialog.asksaveasfilename(
            title="保存ICO文件",
            initialdir=os.path.dirname(self.output_path.get()),
            initialfile=os.path.basename(self.output_path.get()),
            defaultextension=".ico",
            filetypes=filetypes
      )
      if filepath:
            self.output_path.set(filepath)
   
    def convert_image(self):
      """执行图片转换操作"""
      input_path = self.input_path.get()
      output_path = self.output_path.get()
      
      # 验证输入
      if not input_path:
            messagebox.showerror("错误", "请选择输入图片路径")
            return
      
      if not output_path:
            messagebox.showerror("错误", "请设置输出ICO路径")
            return
      
      # 获取选中的尺寸
      selected_sizes =
      if not selected_sizes:
            messagebox.showerror("错误", "请至少选择一个尺寸")
            return
      
      # 检查输入文件是否存在
      if not os.path.isfile(input_path):
            messagebox.showerror("错误", f"输入文件不存在: {input_path}")
            return
      
      # 检查输出目录是否存在
      output_dir = os.path.dirname(output_path)
      if not os.path.isdir(output_dir):
            try:
                os.makedirs(output_dir)
            except Exception as e:
                messagebox.showerror("错误", f"无法创建输出目录: {str(e)}")
                return
      
      # 更新状态
      self.status_var.set("转换中...")
      self.root.update()
      
      try:
            # 打开原始图片
            img = Image.open(input_path)
            
            # 确保图片是RGBA模式(支持透明通道)
            if img.mode != 'RGBA':
                img = img.convert('RGBA')
            
            # 创建不同尺寸的图片列表
            icon_images = []
            for size in selected_sizes:
                # 创建正方形画布(透明背景)
                canvas = Image.new('RGBA', (size, size), (0, 0, 0, 0))
               
                # 计算缩放比例并调整图片大小
                ratio = min(size / img.width, size / img.height)
                new_width = int(img.width * ratio)
                new_height = int(img.height * ratio)
                resized_img = img.resize((new_width, new_height), Image.LANCZOS)
               
                # 将缩放后的图片居中放在画布上
                position = (
                  (size - new_width) // 2,
                  (size - new_height) // 2
                )
                canvas.paste(resized_img, position, resized_img)
               
                icon_images.append(canvas)
            
            # 按尺寸从小到大排序 (ICO文件需要)
            icon_images.sort(key=lambda img: img.width)
            selected_sizes_sorted = sorted(selected_sizes)
            
            # 保存为ICO格式(包含所有尺寸)
            icon_images.save(
                output_path,
                format='ICO',
                append_images=icon_images,
                sizes=[(s, s) for s in selected_sizes_sorted]
            )
            
            # 转换成功
            self.status_var.set(f"转换成功! 已保存到: {output_path}")
            
            # 询问用户是否打开输出目录
            response = messagebox.askyesno("成功",
                f"图片已成功转换为ICO图标!\n\n"
                f"保存位置: {output_path}\n"
                f"包含尺寸: {', '.join(map(str, selected_sizes_sorted))}\n\n"
                "是否打开输出目录?")
            
            if response:
                # 打开输出目录
                output_dir = os.path.dirname(output_path)
                if os.path.exists(output_dir):
                  # 跨平台打开目录
                  if sys.platform == "win32":
                        os.startfile(output_dir)
                  elif sys.platform == "darwin":
                        subprocess.Popen(["open", output_dir])
                  else:
                        subprocess.Popen(["xdg-open", output_dir])
      
      except Exception as e:
            # 转换失败
            self.status_var.set(f"转换失败: {str(e)}")
            messagebox.showerror("错误", f"转换失败: {str(e)}")
   
    def exit_app(self):
      """退出程序"""
      self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = ICOConverterApp(root)
    root.mainloop()

hrh123 发表于 2025-7-23 15:04

【公告】禁止纯AI生成代码使用原创标签
https://www.52pojie.cn/thread-2046647-1-1.html
(出处: 吾爱破解论坛)

随风起舞 发表于 2025-7-23 16:58

可以的,至少是能完成目标的,就怕有py+AI还搞不定简单的小工具就真要好好学习了。{:1_918:} ,源码+图标+成品+打包bat 这样分享东西真的很不错,都给完了。并且还给了AI提问过程,新来的萌新就很容易跟着做了。论坛就需要你们

璐璐诺 发表于 2025-7-23 13:20

300行代码需要2小时沟通的话 说明你的需求不明确

qizhih 发表于 2025-7-23 13:39

新手,来学习的,

winer2chao 发表于 2025-7-23 14:22

感谢大佬,学习了

redapple2015 发表于 2025-7-23 14:31

璐璐诺 发表于 2025-7-23 13:20
300行代码需要2小时沟通的话 说明你的需求不明确

我可能需要半天时间

realmaster 发表于 2025-7-23 14:51

小白学代码有入门吗

qianjieqaq 发表于 2025-7-23 15:20

感觉有用,帮楼主顶一下

guohuanxian 发表于 2025-7-23 15:46

还是非常感谢地

vipgg0 发表于 2025-7-23 15:48

感谢大佬,这个还是可以学习一下,虽然也没看懂。。。
页: [1] 2 3
查看完整版本: 【DeepSeek创作】IMG2ICO,图片转ICO工具 源码