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()