iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
Software Development

軟體開發養成計畫:以小程式實作深化開發能力系列 第 27

[Day27]Python 圖片壓縮技巧:PictureCompressor

  • 分享至 

  • xImage
  •  

開場

在日常生活中,我們經常需要處理大量圖片,不論是工作報告、專案簡報,還是個人收藏,但高解析度圖片佔用大量硬碟空間,不僅存取不方便,也不利於分享,甚至儲存在檔案裡時佔了過多空間而經常發生傳輸失敗的問題。

為了改善這些問題,我想用 Python 寫一個「智慧圖片壓縮小助手」—— 一個能夠壓縮單張圖片,甚至一次處理整個資料夾的小工具。接下來,就讓我們一步步看看如何動手實作吧!

1.圖片壓縮小工具程式碼

(1)匯入模組

import os
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image
import ttkbootstrap as tb
from datetime import datetime
import subprocess
  • os:檔案與路徑操作,計算檔案大小、檔名處理。
  • tkinter:GUI 介面,檔案對話框、彈出提示。
  • PIL.Image:處理圖片,壓縮、轉存格式。
  • ttkbootstrap:美化 tkinter,讓 GUI 更像 App。
  • datetime:記錄壓縮時間到歷史檔。
  • subprocess:在 Windows 開啟檔案總管。

(2)建立主視窗

app = tb.Window(themename="cosmo")
app.title("圖片壓縮小工具")
app.geometry("600x400")

log_file = "compression_history.txt"
  • 建立一個美化過的主視窗。
  • cosmo 是主題名稱,可以換其他像 "flatly", "cyborg"。
  • log_file:壓縮歷史會存到這個檔案。

(3)圖片壓縮核心功能

根據圖片格式(PNG / JPG)做不同壓縮,並計算檔案大小差異。

def compress_image(input_path, output_path, quality=50):
    img = Image.open(input_path)
    original_size = os.path.getsize(input_path)

    if img.format == "PNG":
        img.save(output_path, "PNG", optimize=True)
    else:
        img.save(output_path, "JPEG", optimize=True, quality=quality)

    new_size = os.path.getsize(output_path)
    return original_size, new_size
  • Image.open(input_path):讀取圖片。
  • os.path.getsize():取得壓縮前後檔案大小。
  • 如果是 PNG:無損壓縮,保留透明度;如果是 JPG:依品質參數壓縮。
  • 壓縮後,把紀錄寫入 compression_history.txt,回傳「原始大小 / 新檔案大小」讓介面顯示。

(4)選擇單張圖片壓縮

def select_file():
    file_path = filedialog.askopenfilename(filetypes=[("圖片檔案", "*.png;*.jpg;*.jpeg")])
    if file_path:
        output_path = os.path.splitext(file_path)[0] + "_compressed.jpg"
        orig, new = compress_image(file_path, output_path, quality=int(quality_var.get()))
        if orig and new:
            messagebox.showinfo("完成", f"單張壓縮完成!\n原始: {orig/1024:.2f} KB\n壓縮後: {new/1024:.2f} KB")
            open_folder(output_path)
  • 檔案選擇器選一張圖片。
  • 呼叫 compress_image() 做壓縮。
  • 壓縮完成:顯示檔案大小比較,並開啟檔案總管。

(5)選擇整個資料夾壓縮

def select_folder():
    folder_path = filedialog.askdirectory()
    if folder_path:
        for filename in os.listdir(folder_path):
            if filename.lower().endswith((".png", ".jpg", ".jpeg")):
                input_path = os.path.join(folder_path, filename)
                output_path = os.path.join(folder_path, "compressed_" + filename)
                compress_image(input_path, output_path, quality=int(quality_var.get()))
        messagebox.showinfo("完成", f"資料夾壓縮完成!\n歷史紀錄已更新。")
        open_folder(folder_path)
  • 選擇一個資料夾,程式會遍歷裡面所有圖片。
  • 每張圖片都壓縮,並加上 compressed_ 前綴。
  • 完成後提示 + 開啟資料夾。

(6)開啟檔案總管

def open_folder(path):
    if os.name == "nt":  # Windows
        subprocess.Popen(f'explorer /select,"{path}"')
    else:  # Mac / Linux
        subprocess.Popen(["open", path])
  • Windows:用 explorer 打開並選取檔案。
  • 其他系統(Mac / Linux):用 open 開啟。

(7)查看壓縮紀錄

def view_history():
    if os.path.exists(log_file):
        with open(log_file, "r", encoding="utf-8") as f:
            history_text = f.read()
        history_window = tb.Toplevel(app)
        history_window.title("壓縮紀錄")
        text_box = tk.Text(history_window, wrap="word", width=70, height=20)
        text_box.insert("1.0", history_text)
        text_box.config(state="disabled")
        text_box.pack(padx=10, pady=10)
    else:
        messagebox.showinfo("提示", "目前沒有歷史紀錄。")
  • 開啟** compression_history.txt **檔案,讀取內容。
  • 用** Toplevel **開新視窗顯示。
  • 如果沒有紀錄檔,就提示「沒有紀錄」。

(8)GUI 介面設計

title_label = tb.Label(app, text="📷 圖片壓縮小工具", font=("Microsoft JhengHei", 18, "bold"))
title_label.pack(pady=20)

frame = tb.Frame(app)
frame.pack(pady=10)

btn_file = tb.Button(frame, text="選擇單張圖片", bootstyle="primary", command=select_file)
btn_file.grid(row=0, column=0, padx=10)

btn_folder = tb.Button(frame, text="選擇資料夾", bootstyle="success", command=select_folder)
btn_folder.grid(row=0, column=1, padx=10)

btn_history = tb.Button(frame, text="查看歷史紀錄", bootstyle="info", command=view_history)
btn_history.grid(row=0, column=2, padx=10)

quality_label = tb.Label(app, text="壓縮品質 (1-100,數字越小壓縮越狠)", font=("Microsoft JhengHei", 12))
quality_label.pack(pady=10)

quality_var = tk.StringVar(value="50")
quality_entry = tb.Entry(app, textvariable=quality_var, width=10)
quality_entry.pack()

app.mainloop()
  • 視窗標題 + 按鈕排版:[選擇單張圖片]、[選擇資料夾]、[查看歷史紀錄]
  • 壓縮品質輸入框(預設 50,範圍 1–100)。
  • app.mainloop() :進入 GUI 主迴圈。

2.圖片壓縮小工具成果展示

  1. 介面展示
    https://ithelp.ithome.com.tw/upload/images/20251003/20178527TmsT7Taw2u.jpg
  2. 選擇單張圖片
    https://ithelp.ithome.com.tw/upload/images/20251003/20178527KH9d8AVnJx.jpg
  3. 選擇資料夾
    https://ithelp.ithome.com.tw/upload/images/20251003/20178527QVhoLxZ6DA.jpg
  4. 查看歷史紀錄
    https://ithelp.ithome.com.tw/upload/images/20251003/20178527aTrbwWvPVZ.jpg

3.心得

這次完成圖片壓縮工具的實作,對我來說是一個把理論轉化為實用工具 的好機會。原本只是在學習 PIL 圖片處理與檔案操作,後來我結合了 ttkbootstrap 美化介面,讓程式不僅能用,還能有點像「小APP」的感覺。

在過程中,我遇到了幾個挑戰,例如如何同時支援單張圖片與整個資料夾 的選擇,還有如何在壓縮的同時保留 PNG 與 JPG 的格式差異。經過不斷嘗試,我更熟悉了 tkinter.filedialog 與檔案副檔名的處理方法。

這次實作最大的收穫是,讓我明白到即使是一個小小的工具,只要加上 友善的介面與多元的應用情境,就能讓程式真正融入日常生活。同時,這將會是我 2025 IT 鐵人賽 所製作的最後一個小程式,但絕對不會是我學習生涯的終點。未來我會繼續往更進階的程式應用邁進,期待能再次展示新的作品,並與大家一起學習、一起進步。


上一篇
[Day26]Hash ≠ 加密,Base62 ≠ 魔法:資料處理入門基礎
下一篇
[Day28]一週回顧 & 讓程式更有韌性:錯誤處理與測試
系列文
軟體開發養成計畫:以小程式實作深化開發能力30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言