WebP Images Convert – DarkCorners

Date: 17/12/2025

Category: Python

  • Phần mềm hỗ trợ chuyển đổi hình ảnh sang WEBP hàng loạt.
  • Hỗ trợ nhiều định dạng, kích thước hình ảnh khác nhau.
  1. Khởi động phần mềm.
  2. Chọn thư mục chứa toàn bộ ảnh cần chuyển đổi.
  3. Chọn thư mục xuất ảnh webp đã chuyển đổi.
  4. Chọn kích thước mà bạn muốn.
  5. Chọn chất lượng nén ảnh WEBP.
  6. Nhấn nút [Bắt Đầu].
  7. Theo dõi lịch trình xử lý.
import os
import threading
from tkinter import *
from tkinter import ttk, filedialog, messagebox
from PIL import Image

def center_window(root, width=800, height=600):
    # Lấy kích thước màn hình
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    # Tính vị trí bắt đầu trục ngang và trục đứng
    aaaxxx = (screen_width // 2) - (width // 2)
    bbbyyy = (screen_height // 2) - (height // 2) - (40)
    # Đặt geometry
    root.geometry(f"{width}x{height}+{aaaxxx}+{bbbyyy}")

class WebPConverterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("WebP Images Convert - DarkCorners")
        self.root.resizable(False, False)
        center_window(self.root, 800, 600)
        self.input_dir = StringVar()
        self.output_dir = StringVar()
        self.total_files = 0
        self.converted_files = 0
        self.errors = 0
        self.queue = []
        self.running = False
        self.quality = IntVar(value=80)
        self.setup_ui()

    def setup_ui(self):
        # Hàng 1 : Tên Phần Mềm
        Label(self.root, text="WebP Images Convert - DarkCorners", font=("Arial", 18, "bold"), pady=10).pack()

        # Hàng 2: Chọn Thư Mục Ảnh
        row2 = LabelFrame(self.root, text="Chọn Thư Mục Ảnh", font=("Arial", 10, "bold"), pady=5)
        row2.pack(fill=X, padx=10)
        # Bên trái : thư mục ảnh gốc
        left_frame = Frame(row2)
        left_frame.pack(side=LEFT, fill=X, expand=True, padx=(0, 5))
        Button(left_frame, text="Chọn Thư Mục Gốc", command=self.select_input).pack(side=LEFT)
        Entry(left_frame, textvariable=self.input_dir).pack(side=LEFT, fill=X, expand=True, padx=5)
        # Bên phải : thư mục ảnh lưu
        right_frame = Frame(row2)
        right_frame.pack(side=LEFT, fill=X, expand=True, padx=(5, 0))
        Button(right_frame, text="Chọn Thư Mục Lưu", command=self.select_output).pack(side=LEFT)
        Entry(right_frame, textvariable=self.output_dir).pack(side=LEFT, fill=X, expand=True, padx=5)

        # Hàng 3
        row3 = Frame(self.root)
        row3.pack(fill=X, padx=10, pady=5)
        # Bên trái 70% : tùy chọn kích thước ảnh
        left3 = LabelFrame(row3, text="Tuỳ Chọn Kích Thước Ảnh", width=560, font=("Arial", 10, "bold"))
        left3.pack(side=LEFT, fill=BOTH, expand=True, padx=(0, 5), pady=5)
        self.size_var = StringVar(value="Kích Thước Gốc")
        size_frame1 = Frame(left3)
        size_frame1.pack(anchor=W, pady=2)
        Radiobutton(size_frame1, text="Kích Thước Gốc", variable=self.size_var, value="Kích Thước Gốc").pack(side=LEFT, padx=5)
        Radiobutton(size_frame1, text="Ngang HD (1280x720)", variable=self.size_var, value="Ngang HD").pack(side=LEFT, padx=5)
        Radiobutton(size_frame1, text="Ngang SD (640x360)", variable=self.size_var, value="Ngang SD").pack(side=LEFT, padx=5)
        size_frame2 = Frame(left3)
        size_frame2.pack(anchor=W, pady=2)
        Radiobutton(size_frame2, text="Poster 1 (720x1280)", variable=self.size_var, value="Poster 1").pack(side=LEFT, padx=5)
        Radiobutton(size_frame2, text="Poster 2 (360x640)", variable=self.size_var, value="Poster 2").pack(side=LEFT, padx=5)
        Radiobutton(size_frame2, text="Poster 3 (360x540)", variable=self.size_var, value="Poster 3").pack(side=LEFT, padx=5)
        # Bên phải 30% : điều khiển phần mềm
        right3 = LabelFrame(row3, text="Điều Khiển Phần Mềm", width=240, font=("Arial", 10, "bold"))
        right3.pack(side=LEFT, fill=BOTH, padx=(5, 0), pady=5)
        quality_frame = Frame(right3)
        quality_frame.pack(pady=5)
        Label(quality_frame, text="Chọn Chất Lượng Tỉ Lệ Ảnh WebP :").pack(side=LEFT)
        Spinbox(quality_frame, from_=0, to=100, textvariable=self.quality, width=5).pack(side=LEFT, padx=5)
        button_frame = Frame(right3)
        button_frame.pack(pady=5)
        Button(button_frame, text="Bắt Đầu", command=self.start_conversion, width=10).pack(side=LEFT, padx=5)
        Button(button_frame, text="Tạm Dừng", command=self.pause_conversion, width=10).pack(side=LEFT, padx=5)
        Button(button_frame, text="Thoát", command=self.root.quit, width=10).pack(side=LEFT, padx=5)

        # Hàng 4: Tiến trình xử lý
        status_frame = LabelFrame(self.root, text="Tiến Trình Xử Lý", font=("Arial", 10, "bold"))
        status_frame.pack(fill=X, padx=10, pady=(0, 5))
        self.status_all = Label(status_frame, text="Tất Cả: 0", font=("Arial", 10, "bold"), fg="red")
        self.status_done = Label(status_frame, text="Đã Convert: 0", font=("Arial", 10, "bold"), fg="green")
        self.status_waiting = Label(status_frame, text="Đợi Convert: 0", font=("Arial", 10, "bold"), fg="blue")
        self.status_error = Label(status_frame, text="Bị Lỗi: 0", font=("Arial", 10, "bold"), fg="black")
        for widget in [self.status_all, self.status_done, self.status_waiting, self.status_error]:
            widget.pack(side=LEFT, expand=True, padx=10, pady=5)

        # Hàng 5: Lịch trình xử lý
        log_frame = LabelFrame(self.root, text="Lịch Trình Xử Lý", font=("Arial", 10, "bold"))
        log_frame.pack(fill=BOTH, expand=True, padx=10, pady=(0, 5))
        self.log_text = Text(log_frame, height=20, wrap=WORD)
        self.log_text.pack(fill=BOTH, expand=True)

    def select_input(self):
        folder = filedialog.askdirectory()
        if folder:
            self.input_dir.set(folder)

    def select_output(self):
        folder = filedialog.askdirectory()
        if folder:
            self.output_dir.set(folder)

    def start_conversion(self):
        if not self.input_dir.get() or not self.output_dir.get():
            messagebox.showerror("Lỗi", "Vui lòng chọn đầy đủ thư mục.")
            return
        self.running = True
        self.queue.clear()
        self.converted_files = 0
        self.errors = 0
        valid_exts = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
        for root_dir, _, files in os.walk(self.input_dir.get()):
            for f in files:
                if os.path.splitext(f)[1].lower() in valid_exts:
                    full_path = os.path.join(root_dir, f)
                    self.queue.append(full_path)
        self.total_files = len(self.queue)
        self.update_status()
        threading.Thread(target=self.process_queue, daemon=True).start()

    def pause_conversion(self):
        self.running = False
        self.log("Đã tạm dừng quá trình chuyển đổi.")

    def process_queue(self):
        while self.queue and self.running:
            file_path = self.queue.pop(0)
            try:
                self.convert_image(file_path)
                self.converted_files += 1
                self.log(f"✔ Đã chuyển: {os.path.basename(file_path)}")
            except Exception as e:
                self.errors += 1
                self.log(f"✘ Lỗi với {os.path.basename(file_path)}: {e}")
            self.update_status()

    def convert_image(self, file_path):
        img = Image.open(file_path).convert("RGB")
        size_choice = self.size_var.get()
        if size_choice == "Ngang HD":
            img = img.resize((1280, 720))
        elif size_choice == "Ngang SD":
            img = img.resize((640, 360))
        elif size_choice == "Poster 1":
            img = img.resize((720, 1280))
        elif size_choice == "Poster 2":
            img = img.resize((360, 640))
        elif size_choice == "Poster 3":
            img = img.resize((360, 540))
        out_name = os.path.splitext(os.path.basename(file_path))[0] + ".webp"
        output_path = os.path.join(self.output_dir.get(), out_name)
        img.save(output_path, "webp", quality=self.quality.get())

    def log(self, message):
        self.log_text.insert(END, message + "\n")
        self.log_text.see(END)

    def update_status(self):
        self.status_all.config(text=f"Tất Cả: {self.total_files}")
        self.status_done.config(text=f"Đã Convert: {self.converted_files}")
        self.status_waiting.config(text=f"Đợi Convert: {len(self.queue)}")
        self.status_error.config(text=f"Bị Lỗi: {self.errors}")

if __name__ == '__main__':
    root = Tk()
    app = WebPConverterApp(root)
    root.mainloop()
pyinstaller --noconsole --onefile --windowed --add-data "4-WebpImagesConvert-icon.ico;." --icon=4-WebpImagesConvert-icon.ico 4-WebpImagesConvert-Source.py

Để lại một bình luận