有分享游戏、发现好游戏、开启游戏之旅!

[软件] 图片压缩工具

[复制链接]
查看: 9|回复: 5
  • TA的每日心情
    奋斗
    2025-5-9 02:27
  • 签到天数: 19 天

    [LV.4]秘境征服

    1573

    主题

    1657

    帖子

    874万

    积分

    袖揽星河摘月仙✨2025

    Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48

    汽水*** 发表于 前天 18:51 | 显示全部楼层 |阅读模式
    在为网站上传图片时,经常会遇到对图片文件大小和分辨率的限制。为了确保图片符合这些要求而不失质量,图片压缩工具成为不可或缺的好帮手。这类工具不仅能够有效减小文件体积,还能保持图像的清晰度,确保网页加载速度不受影响。
    主要功能与优势:
      1.智能压缩:利用先进的算法,在几乎不影响视觉效果的情况下减少图片文件的大小,确保图片符合网站的上传要求
      2.格式转换:部分工具还提供格式转换功能,可以将图片转换为最适合网络使用的格式(如 JPEG、PNG 等多种图片格式),进一步优化文件大小。
      3.自定义设置:允许用户调整压缩级别,根据具体需求平衡图片质量和文件大小。
      4.即时预览:一些工具提供即时预览功能,让用户可以在压缩前后对比图片质量,确保最终效果满意。
    代码源码在页面最下方,有需要的可以自行修改研究。
    1. import os
    2. import re
    3. import tkinter as tk
    4. from tkinter import filedialog, messagebox, ttk
    5. from PIL import Image, ImageTk, UnidentifiedImageError
    6. import threading
    7. import logging
    8. # 设置日志配置
    9. logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
    10. class ImageCompressorApp:
    11.     def __init__(self, root):
    12.         self.root = root
    13.         self.root.title("图片压缩工具 by ke")
    14.         self.root.geometry("1024x650")
    15.         self.root.configure(bg="#f0f0f0")
    16.         # 初始化变量
    17.         self.image_path = None  # 图片路径
    18.         self.output_location = None  # 输出位置
    19.         self.target_size_kb = tk.IntVar(value=400)  # 默认目标大小为400KB
    20.         self.new_filename = tk.StringVar()  # 新文件名
    21.         self.output_format = tk.StringVar(value="JPG")  # 修改默认输出格式为JPG
    22.         # 创建界面元素
    23.         self.create_widgets()
    24.     def create_widgets(self):
    25.         main_frame = tk.Frame(self.root, bg="#f0f0f0")
    26.         main_frame.pack(expand=True, fill='both', padx=20, pady=20)
    27.         # 文件选择和预览
    28.         img_frame = tk.Frame(main_frame, bg="#f0f0f0")
    29.         img_frame.pack(fill='x')
    30.         self.img_label = tk.Label(img_frame, text="请加载一张图片", bg="#f0f0f0")
    31.         self.img_label.pack(side=tk.LEFT, padx=10, pady=10)
    32.         browse_button = tk.Button(img_frame, text="选择图片...", command=self.browse_files)
    33.         browse_button.pack(side=tk.RIGHT, padx=10, pady=10)
    34.         # 目标大小输入框
    35.         size_frame = tk.Frame(main_frame, bg="#f0f0f0")
    36.         size_frame.pack(fill='x', pady=5)
    37.         tk.Label(size_frame, text="目标大小 (KB):", bg="#f0f0f0").pack(side=tk.LEFT, padx=5)
    38.         self.size_entry = tk.Entry(size_frame, textvariable=self.target_size_kb, width=10)
    39.         self.size_entry.pack(side=tk.LEFT, padx=5)
    40.         # 设置新文件名
    41.         filename_frame = tk.Frame(main_frame, bg="#f0f0f0")
    42.         filename_frame.pack(fill='x', pady=5)
    43.         tk.Label(filename_frame, text="新文件名:", bg="#f0f0f0").pack(side=tk.LEFT, padx=5)
    44.         tk.Entry(filename_frame, textvariable=self.new_filename, width=30).pack(side=tk.LEFT, padx=5)
    45.         # 选择输出格式
    46.         format_frame = tk.Frame(main_frame, bg="#f0f0f0")
    47.         format_frame.pack(fill='x', pady=5)
    48.         tk.Label(format_frame, text="输出格式:", bg="#f0f0f0").pack(side=tk.LEFT, padx=5)
    49.         format_combobox = ttk.Combobox(format_frame, textvariable=self.output_format,
    50.                                        values=["JPG", "JPEG", "PNG", "BMP", "GIF"], width=10)
    51.         format_combobox.current(0)  # 设置默认选中项为第一个选项,即"JPG"
    52.         format_combobox.pack(side=tk.LEFT, padx=5)
    53.         # 输出位置选择
    54.         output_frame = tk.Frame(main_frame, bg="#f0f0f0")
    55.         output_frame.pack(fill='x', pady=5)
    56.         tk.Label(output_frame, text="输出位置:", bg="#f0f0f0").pack(side=tk.LEFT, padx=5)
    57.         self.output_location_var = tk.StringVar()
    58.         tk.Entry(output_frame, textvariable=self.output_location_var, width=50).pack(side=tk.LEFT, padx=5)
    59.         tk.Button(output_frame, text="选择...", command=self.select_output_location).pack(side=tk.LEFT, padx=5)
    60.         # 创建一个容器用于放置压缩并保存按钮和进度条
    61.         bottom_frame = tk.Frame(self.root, bg="#f0f0f0")
    62.         bottom_frame.pack(side=tk.BOTTOM, fill='x', pady=20)
    63.         # 压缩并保存按钮
    64.         compress_button = tk.Button(bottom_frame, text="压缩并保存", command=self.start_compress_and_save)
    65.         compress_button.pack(side=tk.LEFT, padx=10)
    66.         # 进度条放在最下方
    67.         self.progress = ttk.Progressbar(bottom_frame, orient="horizontal", length=700, mode="determinate")
    68.         self.progress.pack(side=tk.RIGHT, padx=10)
    69.     def load_image(self, path):
    70.         """加载并显示图片预览"""
    71.         try:
    72.             self.image_path = path
    73.             img = Image.open(path)
    74.             img.thumbnail((300, 300))
    75.             self.photo = ImageTk.PhotoImage(img)
    76.             self.img_label.config(image=self.photo)
    77.         except Exception as e:
    78.             logging.error(f"加载图片时出错: {str(e)}")
    79.             self.show_message("错误", f"加载图片时出错: {str(e)}")
    80.     def browse_files(self):
    81.         """打开文件对话框选择图片"""
    82.         filename = filedialog.askopenfilename(
    83.             title="选择图片",
    84.             filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp *.gif")]
    85.         )
    86.         if filename:
    87.             self.load_image(filename)
    88.     def select_output_location(self):
    89.         """选择图片压缩后的输出位置"""
    90.         directory = filedialog.askdirectory(title="选择输出位置")
    91.         if directory:
    92.             self.output_location_var.set(directory)
    93.     def start_compress_and_save(self):
    94.         """启动压缩线程并在UI上显示进度条"""
    95.         self.progress["value"] = 0  # 初始化进度条为0%
    96.         self.progress["maximum"] = 100  # 设置进度条的最大值为100%
    97.         compress_thread = threading.Thread(target=self.compress_and_save)
    98.         compress_thread.start()
    99.     def compress_and_save(self):
    100.         """执行图片压缩并保存到指定位置"""
    101.         try:
    102.             if not self.image_path or not self.output_location_var.get():
    103.                 self.show_message("警告", "请选择图片和输出位置!")
    104.                 return
    105.             new_filename = sanitize_filename(self.new_filename.get().strip())
    106.             if not new_filename:
    107.                 self.show_message("警告", "请输入有效的文件名!")
    108.                 return
    109.             output_filename = f"{new_filename}.{self.get_output_extension()}"
    110.             output_path = os.path.join(self.output_location_var.get(), output_filename)
    111.             # 检查输出路径有效性
    112.             if not os.path.isdir(self.output_location_var.get()):
    113.                 logging.error(f"输出位置不存在: {self.output_location_var.get()}")
    114.                 self.show_message("错误", "选择的输出位置无效,请重新选择。")
    115.                 return
    116.             # 检查是否有写权限
    117.             if not os.access(self.output_location_var.get(), os.W_OK):
    118.                 logging.error(f"没有写入权限: {self.output_location_var.get()}")
    119.                 self.show_message("错误", "没有足够的权限在选择的位置写入文件。")
    120.                 return
    121.             target_size_kb = self.target_size_kb.get()
    122.             original_size_kb = os.path.getsize(self.image_path) / 1024
    123.             if original_size_kb  target_size_kb and quality >= 10:
    124.                 try:
    125.                     img.save(output_path, format=format_name, optimize=True, quality=quality)
    126.                     original_size_kb = os.path.getsize(output_path) / 1024
    127.                     quality -= 5
    128.                     self.update_progress(quality)
    129.                 except IOError as e:
    130.                     logging.error(f"IO 错误: {str(e)}")
    131.                     raise ValueError(f"IO 错误: {str(e)}")
    132.                 except Exception as e:
    133.                     logging.error(f"保存图片时出错: {str(e)}")
    134.                     raise ValueError(f"保存图片时出错: {str(e)}")
    135.             if original_size_kb > target_size_kb:
    136.                 raise ValueError("无法将图片压缩到指定大小!请尝试增加目标大小或减少图片复杂度。")
    137.         except UnidentifiedImageError:
    138.             logging.error("无法识别的图片格式")
    139.             self.show_message("错误", "无法识别的图片格式,请选择其他图片。")
    140.         except Exception as e:
    141.             logging.error(f"压缩图片时出错: {str(e)}")
    142.             self.show_message("错误", f"压缩图片时出错: {str(e)}")
    143.     def get_output_extension(self):
    144.         """根据输出格式返回正确的文件扩展名"""
    145.         format_name = self.output_format.get().upper()
    146.         if format_name in ["JPG", "JPEG"]:
    147.             return "jpg"
    148.         elif format_name == "PNG":
    149.             return "png"
    150.         elif format_name == "BMP":
    151.             return "bmp"
    152.         elif format_name == "GIF":
    153.             return "gif"
    154.         else:
    155.             return "jpg"
    156.     def get_output_format(self):
    157.         """根据输出格式返回正确的Pillow格式名称"""
    158.         format_name = self.output_format.get().upper()
    159.         if format_name in ["JPG", "JPEG"]:
    160.             return "JPEG"
    161.         elif format_name == "PNG":
    162.             return "PNG"
    163.         elif format_name == "BMP":
    164.             return "BMP"
    165.         elif format_name == "GIF":
    166.             return "GIF"
    167.         else:
    168.             return "JPEG"
    169.     def update_progress(self, quality):
    170.         """更新进度条的值"""
    171.         max_quality = 95
    172.         min_quality = 10
    173.         progress_value = ((max_quality - quality) / (max_quality - min_quality)) * 100
    174.         self.root.after(0, lambda: self.set_progress_value(min(progress_value, 100)))
    175.     def set_progress_value(self, value):
    176.         """设置进度条的值"""
    177.         self.progress["value"] = value
    178.     def show_message(self, title, message):
    179.         """显示消息框并停止进度条"""
    180.         self.root.after(0, lambda: messagebox.showinfo(title, message))
    181. def sanitize_filename(filename):
    182.     """清理文件名中的非法字符"""
    183.     return re.sub(r'[\/*?:"|]', "", filename)
    184. if __name__ == "__main__":
    185.     root = tk.Tk()
    186.     app = ImageCompressorApp(root)
    187.     root.mainloop()
    复制代码
    游客,如果您要查看本帖隐藏内容请回复

    该用户从未签到

    0

    主题

    0

    帖子

    7

    积分

    呱呱坠地

    Rank: 9Rank: 9Rank: 9

    ha123*** 发表于 前天 18:51 | 显示全部楼层
    哟,找游戏服务端去 “有分享” 就对了。它定期举办资源分享活动,能白嫖不少稀有游戏服务端,超划算。

    该用户从未签到

    0

    主题

    2

    帖子

    89

    积分

    牙牙学语

    Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19

    maye*** 发表于 昨天 03:51 | 显示全部楼层
    哟,找游戏服务端去 “有分享” 就对了。它定期举办资源分享活动,能白嫖不少稀有游戏服务端,超划算。
  • TA的每日心情

    2018-4-9 16:54
  • 签到天数: 5 天

    [LV.2]地图开拓

    0

    主题

    18

    帖子

    751

    积分

    茁壮成长

    Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39Rank: 39

    skyy*** 发表于 昨天 12:52 | 显示全部楼层
    哟,看到你找游戏服务端资源,“有分享” 必须安利。我前阵子在那下了款经典角色扮演游戏的服务端,版本超全,玩得超上头。
  • TA的每日心情
    奋斗
    2022-10-8 14:23
  • 签到天数: 13 天

    [LV.3]遗迹探寻

    0

    主题

    24

    帖子

    55万

    积分

    有分享热血免费版

    Rank: 22Rank: 22Rank: 22Rank: 22Rank: 22Rank: 22

    m012*** 发表于 昨天 21:52 | 显示全部楼层
    你好呀!“有分享” 网站上有很多独家游戏服务端资源,别处可找不到,我已经在这挖到好几个宝藏了。
  • TA的每日心情

    2024-6-24 19:12
  • 签到天数: 585 天

    [LV.9]维度主宰

    1

    主题

    730

    帖子

    350万

    积分

    【就这么任性】

    Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48Rank: 48

    lzy*** 发表于 3 小时前 | 显示全部楼层
    哟,看到你找游戏服务端资源,“有分享” 必须安利。我前阵子在那下了款经典角色扮演游戏的服务端,版本超全,玩得超上头。

    请登陆后参与评论

    游客
    请先登录
    懒得打字嘛,点击右侧快捷回复 【更多功能更新中】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    本站2016-06-06至今已运行3391天。欢迎光临!
    快速回复 返回顶部 返回列表