吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 787|回复: 14
上一主题 下一主题
收起左侧

[原创工具] 【开源】图片格式转换器,可转ico、icns、png、jpg、bmp

[复制链接]
跳转到指定楼层
楼主
silent 发表于 2025-12-23 17:39 回帖奖励
本帖最后由 silent 于 2025-12-23 17:43 编辑


自己用的小工具,图片格式转换器,可转ico、icns、png、jpg、bmp。

成品和源码都在下面,大家自己可以扩展:
https://wwavu.lanzoub.com/inDfE3ebgs6j 密码:1umt
from tkinterdnd2 import DND_FILES, TkinterDnD
import tkinter as tk
from tkinter import filedialog, ttk
from PIL import Image, ImageTk
import os
from datetime import datetime
import sys

class ImageToIcoConverter:
    def __init__(self, root):
        self.root = root
        self.root.title("图片格式转换器1.01 - by silent 【吾爱破解】")
        
        # 设置窗口图标
        try:
            application_path = os.path.dirname(os.path.abspath(__file__))
            icon_path = os.path.join(application_path, 'app.ico')
            self.root.iconbitmap(icon_path)
        except:
            pass  # 如果设置图标失败,忽略错误
        
        # 增加窗口高度以容纳所有控件
        window_width = 500
        window_height = 550
        
        # 获取屏幕尺寸
        screen_width = root.winfo_screenwidth()
        screen_height = root.winfo_screenheight()
        
        # 计算窗口居中位置
        center_x = int((screen_width - window_width) / 2)
        center_y = int((screen_height - window_height) / 2)
        
        # 设置窗口大小和位置
        self.root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")
        
        # 禁止调整窗口大小
        self.root.resizable(False, False)
        
        # 创建左右分栏
        self.left_frame = ttk.Frame(root, padding="10")
        self.left_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        self.right_frame = ttk.Frame(root, padding="10")
        self.right_frame.grid(row=0, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 为整个窗口注册拖放功能
        self.root.drop_target_register(DND_FILES)
        self.root.dnd_bind('<<Drop>>', self.handle_drop)
        
        # 为左右框架注册拖放功能
        self.left_frame.drop_target_register(DND_FILES)
        self.left_frame.dnd_bind('<<Drop>>', self.handle_drop)
        self.right_frame.drop_target_register(DND_FILES)
        self.right_frame.dnd_bind('<<Drop>>', self.handle_drop)
        
        # 创建拖放提示标签
        self.drop_label = ttk.Label(
            self.left_frame, 
            text="拖放图片到窗口任意位置\n或点击选择图片按钮",
            justify=tk.CENTER
        )
        self.drop_label.grid(row=0, column=0, pady=5)
        
        # 为提示标签注册拖放功能
        self.drop_label.drop_target_register(DND_FILES)
        self.drop_label.dnd_bind('<<Drop>>', self.handle_drop)
        
        # 选择文件按钮
        self.select_btn = ttk.Button(self.left_frame, text="选择图片", command=self.select_file)
        self.select_btn.grid(row=1, column=0, pady=5)
        
        # 显示选中的文件路径
        self.file_label = ttk.Label(self.left_frame, text="未选择文件")
        self.file_label.grid(row=2, column=0, pady=3)
        
        # 尺寸选择框架
        self.size_frame = ttk.LabelFrame(self.left_frame, text="选择输出尺寸", padding="5")
        self.size_frame.grid(row=3, column=0, pady=5)
        
        # 预设尺寸选项
        self.sizes = ["16x16", "32x32", "48x48", "64x64", "128x128", "256x256", "512x512", "1024x1024"]
        self.size_var = tk.StringVar(value="32x32")
        
        for i, size in enumerate(self.sizes):
            ttk.Radiobutton(self.size_frame, text=size, value=size, 
                          variable=self.size_var).grid(row=i//2, column=i%2, padx=10, pady=5)
        
        # 添加格式选择框架
        self.format_frame = ttk.LabelFrame(self.left_frame, text="选择输出格式", padding="5")
        self.format_frame.grid(row=4, column=0, pady=5)
        
        # 预设格式选项
        self.formats = [
            ("ICO", "ico"), 
            ("ICNS", "icns"),
            ("PNG", "png"), 
            ("JPG", "jpg"),
            ("BMP", "bmp")
        ]
        self.format_var = tk.StringVar(value="ico")
        
        # 创建格式单选按钮
        for i, (format_name, format_value) in enumerate(self.formats):
            ttk.Radiobutton(
                self.format_frame, 
                text=format_name, 
                value=format_value, 
                variable=self.format_var
            ).grid(row=i//2, column=i%2, padx=10, pady=5)
        
        # 打开输出目录选项
        self.open_dir_var = tk.BooleanVar(value=True)
        self.open_dir_check = ttk.Checkbutton(
            self.left_frame, 
            text="转换后打开输出目录",
            variable=self.open_dir_var
        )
        self.open_dir_check.grid(row=5, column=0, pady=3)
        
        # 转换按钮
        self.convert_btn = ttk.Button(self.left_frame, text="转换", command=self.convert)
        self.convert_btn.grid(row=6, column=0, pady=5)
        
        # 状态标签
        self.status_label = ttk.Label(self.left_frame, text="")
        self.status_label.grid(row=7, column=0, pady=3)
        
        # 添加预览框架
        self.preview_frame = ttk.LabelFrame(self.right_frame, text="预览", padding="5")
        self.preview_frame.grid(row=0, column=0, padx=10, pady=10)
        
        # 预览图片标签
        self.preview_label = ttk.Label(self.preview_frame)
        self.preview_label.grid(row=0, column=0, padx=10, pady=10)
        # 预览信息标签:文件名、尺寸、大小
        self.info_label = ttk.Label(self.preview_frame, text="")
        self.info_label.grid(row=1, column=0, padx=10, pady=(0, 10))
        
        self.selected_file = None
        self.current_dir = os.path.dirname(os.path.abspath(__file__))
        self.preview_image = None  # 保存预览图片的引用
    
    def update_preview(self, image_path):
        try:
            # 打开图片
            img = Image.open(image_path)
            file_stat = os.stat(image_path)
            
            # 计算预览图大小(保持比例,最大150x150)
            preview_size = 150
            width, height = img.size
            ratio = min(preview_size/width, preview_size/height)
            new_size = (int(width*ratio), int(height*ratio))
            
            # 调整图片大小
            preview_img = img.resize(new_size, Image.Resampling.LANCZOS)
            
            # 转换为PhotoImage
            self.preview_image = ImageTk.PhotoImage(preview_img)
            
            # 更新预览标签
            self.preview_label.configure(image=self.preview_image)
            # 显示文件名、尺寸和大小
            file_name = os.path.basename(image_path)
            file_size_kb = file_stat.st_size / 1024
            self.info_label.configure(text=f"{file_name} | {width}x{height} | {file_size_kb:.1f} KB")
            
        except Exception as e:
            self.status_label.config(text=f"预览失败:{str(e)}")
    
    def select_file(self):
        filetypes = (
            ('图片文件', '*.png *.jpg *.jpeg *.bmp'),
            ('所有文件', '*.*')
        )
        
        filename = filedialog.askopenfilename(
            title='选择图片',
            filetypes=filetypes,
            initialdir=self.current_dir  # 设置默认打开目录为软件所在目录
        )
        
        if filename:
            self.selected_file = filename
            self.file_label.config(text=os.path.basename(filename))
            self.status_label.config(text="")
            self.current_dir = os.path.dirname(filename)
            # 更新预览图
            self.update_preview(filename)
    
    def convert(self):
        if not self.selected_file:
            self.status_label.config(text="请先选择图片文件!")
            return
        
        try:
            # 打开原图
            img = Image.open(self.selected_file)
            
            # 获取选择的格式
            output_format = self.format_var.get().upper()
            
            # 获取用户选择的尺寸
            target_size = tuple(map(int, self.size_var.get().split('x')))
            
            # 根据输出格式处理图像
            if output_format == 'ICO':
                # ICO格式特殊处理
                if img.mode != 'RGBA':
                    img = img.convert('RGBA')
            elif output_format == 'ICNS':
                # ICNS格式特殊处理,需要RGBA模式
                if img.mode != 'RGBA':
                    img = img.convert('RGBA')
            elif output_format == 'JPG':
                # JPG不支持透明度,也不支持调色板等模式,统一转为 RGB
                if img.mode != 'RGB':
                    img = img.convert('RGB')
            
            # 设置默认文件名和文件类型
            default_filename = 'favicon' if output_format == 'ICO' else ('icon' if output_format == 'ICNS' else 'image')
            file_extension = f'.{self.format_var.get().lower()}'
            
            # 保存文件对话框
            save_path = filedialog.asksaveasfilename(
                defaultextension=file_extension,
                filetypes=[(f'{output_format}文件', f'*{file_extension}')],
                initialfile=f'{default_filename}{file_extension}'
            )
            
            if save_path:
                if output_format == 'ICNS':
                    # ICNS 仅生成用户选择尺寸的 144 DPI(Retina,对应 2x 像素)
                    original_width, original_height = img.size
                    original_max_size = max(original_width, original_height)
                    logical_max = max(target_size)
                    pixel_size = logical_max * 2  # 144 DPI 约等于 2x 像素
                    
                    # 若原图不足以支撑 2x,使用原图最大边并提示
                    if original_max_size < pixel_size:
                        pixel_size = original_max_size
                        self.status_label.config(text=f"原图较小,已按 {pixel_size}x{pixel_size} 生成")
                    
                    resized_img = img.resize((pixel_size, pixel_size), Image.Resampling.LANCZOS)
                    resized_img.save(save_path, format='ICNS')
                    print(f"已生成 ICNS:{pixel_size}x{pixel_size} (目标 {logical_max}@144DPI)")
                elif output_format == 'ICO':
                    # 调整图片大小
                    resized_img = img.resize(target_size, Image.Resampling.LANCZOS)
                    resized_img.save(save_path, format='ICO', sizes=[target_size])
                else:
                    # PNG / JPG / BMP 等普通位图,依靠扩展名自动识别格式
                    resized_img = img.resize(target_size, Image.Resampling.LANCZOS)
                    resized_img.save(save_path)
                
                # 获取当前时间并格式化
                current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                self.status_label.config(text=f"转换成功! {current_time}")
                
                # 如果选择了打开输出目录选项,则打开目录
                if self.open_dir_var.get():
                    output_dir = os.path.dirname(save_path)
                    if os.name == 'nt':  # Windows
                        os.startfile(output_dir)
                    else:  # macOS
                        import subprocess, sys
                        if sys.platform == 'darwin':
                            subprocess.run(['open', output_dir])
                        else:
                            subprocess.run(['xdg-open', output_dir])
                    
        except Exception as e:
            self.status_label.config(text=f"转换失败:{str(e)}")
    
    def handle_drop(self, event):
        # 获取拖放的文件路径
        file_path = event.data
        
        # 处理Windows路径中的花括号
        if file_path.startswith('{') and file_path.endswith('}'):
            file_path = file_path[1:-1]
        
        # 检查是否是支持的图片格式
        supported_formats = ('.png', '.jpg', '.jpeg', '.bmp')
        if file_path.lower().endswith(supported_formats):
            self.selected_file = file_path
            self.file_label.config(text=os.path.basename(file_path))
            self.status_label.config(text="")
            self.current_dir = os.path.dirname(file_path)
            # 更新预览图
            self.update_preview(file_path)
        else:
            self.status_label.config(text="不支持的文件格式!")

if __name__ == "__main__":
    root = TkinterDnD.Tk()  # 使用TkinterDnD.Tk替代tk.Tk
    app = ImageToIcoConverter(root)
    root.mainloop() 

免费评分

参与人数 4吾爱币 +9 热心值 +4 收起 理由
bqi153 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
chuanglue + 1 + 1 楼主出品,必属精品!
confiant + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
lxfaipojie 发表于 2025-12-25 18:01
感谢   6666
3#
ss070525 发表于 2025-12-25 18:43
4#
mhfymn 发表于 2025-12-25 19:51
5#
 楼主| silent 发表于 2025-12-26 02:36 |楼主
ss070525 发表于 2025-12-25 18:43
不知道能不能批量转换?感谢分享

这个你可以去扩展一下,我这边用不上批量
6#
laohankaiche 发表于 2025-12-26 08:37
这个好,有的图片查看器看不了的可以批量改格式了
7#
a524601428 发表于 2025-12-26 10:56
感谢博主
8#
独孤的云 发表于 2025-12-26 15:06
小巧方便 感谢分享~~
9#
lls1043 发表于 2025-12-26 17:12
非常感谢,下载使用。
10#
lovefancy88 发表于 2025-12-27 09:01
感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-1-2 16:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表