|
6 | 6 | import threading |
7 | 7 | import time |
8 | 8 |
|
| 9 | +VERSION = "1.0.1" |
9 | 10 |
|
10 | | -def convert_to_webp(): |
11 | | - file_paths = filedialog.askopenfilenames(title="Select image files", |
12 | | - filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.bmp;*.tiff")]) |
| 11 | +class ImageConverterApp: |
| 12 | + def __init__(self, root): |
| 13 | + self.root = root |
| 14 | + self.root.title(f"Image to WebP Converter | V{VERSION}") |
| 15 | + self.root.geometry("300x150") |
13 | 16 |
|
14 | | - if not file_paths: |
15 | | - return |
| 17 | + self.btn_select = tk.Button(root, text="Select Images & Convert", command=self.convert_to_webp, padx=10, pady=5) |
| 18 | + self.btn_select.pack(pady=10) |
16 | 19 |
|
17 | | - save_dir = filedialog.askdirectory(title="Select destination folder") |
18 | | - if not save_dir: |
19 | | - return |
| 20 | + self.progress_label = tk.Label(root, text="0/0 images processed", font=("Arial", 12)) |
| 21 | + self.progress_label.pack(pady=5) |
20 | 22 |
|
21 | | - progress_label.config(text=f"0/{len(file_paths)} images processed") |
22 | | - time_label.config(text="Elapsed time: 0 seconds") |
| 23 | + self.status_label = tk.Label(root, text="", font=("Arial", 10), fg="green") |
| 24 | + self.status_label.pack(pady=5) |
23 | 25 |
|
24 | | - remaining_files = list(file_paths) |
| 26 | + self.start_time = 0 |
| 27 | + self.completed_count = 0 |
| 28 | + self.num_files = 0 |
| 29 | + self.lock = None |
25 | 30 |
|
26 | | - start_time = time.time() |
| 31 | + def convert_to_webp(self): |
| 32 | + file_paths = filedialog.askopenfilenames(title="Select image files", |
| 33 | + filetypes=[("Image files", "*.png;*.jpg;*.jpeg")]) |
| 34 | + if not file_paths: |
| 35 | + return |
27 | 36 |
|
28 | | - def convert_image(file_path): |
29 | | - try: |
30 | | - img = Image.open(file_path) |
31 | | - file_name = os.path.splitext(os.path.basename(file_path))[0] + ".webp" |
32 | | - save_path = os.path.join(save_dir, file_name) |
33 | | - if img.format == "JPEG": |
34 | | - img.save(save_path, "webp", lossy=True) |
35 | | - else: |
36 | | - img.save(save_path, "webp", lossless=True) |
| 37 | + save_dir = filedialog.askdirectory(title="Select destination folder") |
| 38 | + if not save_dir: |
| 39 | + return |
37 | 40 |
|
38 | | - remaining_files.remove(file_path) |
39 | | - progress_label.config(text=f"{len(file_paths) - len(remaining_files)}/{len(file_paths)} images processed") |
| 41 | + self.num_files = len(file_paths) |
| 42 | + self.progress_label.config(text=f"0/{self.num_files} images processed") |
| 43 | + self.status_label.config(text="", fg="green") |
40 | 44 |
|
41 | | - elapsed_time = time.time() - start_time |
42 | | - time_label.config(text=f"Elapsed time: {int(elapsed_time)} seconds") |
43 | | - root.update_idletasks() |
44 | | - except Exception as e: |
45 | | - messagebox.showerror("Error", f"Failed to convert {file_path}: {e}") |
| 45 | + self.start_time = time.time() |
| 46 | + self.completed_count = 0 |
| 47 | + self.lock = None |
46 | 48 |
|
47 | | - def convert_images(): |
48 | | - max_workers = os.cpu_count() // 2 |
| 49 | + def convert_image(file_path): |
| 50 | + try: |
| 51 | + img = Image.open(file_path) |
| 52 | + file_name = os.path.splitext(os.path.basename(file_path))[0] + ".webp" |
| 53 | + save_path = os.path.join(save_dir, file_name) |
49 | 54 |
|
50 | | - with ThreadPoolExecutor(max_workers=max_workers) as executor: |
51 | | - futures = [executor.submit(convert_image, file_path) for file_path in file_paths] |
52 | | - for future in futures: |
53 | | - future.result() |
| 55 | + icc_profile = img.info.get("icc_profile") |
54 | 56 |
|
55 | | - total_time = time.time() - start_time |
56 | | - messagebox.showinfo("Success", f"All images have been converted to WebP.\nTotal time: {int(total_time)} seconds.") |
| 57 | + if img.mode in ("P", "CMYK"): |
| 58 | + img = img.convert("RGBA" if img.mode == "P" else "RGB") |
57 | 59 |
|
58 | | - threading.Thread(target=convert_images, daemon=True).start() |
| 60 | + if img.format == "JPEG": |
| 61 | + img.save(save_path, "webp", lossy=True, icc_profile=icc_profile) |
| 62 | + else: |
| 63 | + img.save(save_path, "webp", lossless=True, icc_profile=icc_profile) |
59 | 64 |
|
| 65 | + with self.lock: |
| 66 | + self.completed_count += 1 |
| 67 | + self.update_progress() |
60 | 68 |
|
61 | | -root = tk.Tk() |
62 | | -root.title("Image to WebP Converter") |
63 | | -root.geometry("300x150") |
| 69 | + except Exception as e: |
| 70 | + print(f"Error converting {file_path}: {e}") |
| 71 | + |
| 72 | + def run_conversion(): |
| 73 | + self.lock = threading.Lock() |
| 74 | + max_workers = max(1, os.cpu_count() // 2) |
| 75 | + with ThreadPoolExecutor(max_workers=max_workers) as executor: |
| 76 | + executor.map(convert_image, file_paths) |
64 | 77 |
|
65 | | -btn_select = tk.Button(root, text="Select Images & Convert", command=convert_to_webp, padx=10, pady=5) |
66 | | -btn_select.pack(pady=10) |
| 78 | + self.show_completion_message() |
67 | 79 |
|
68 | | -progress_label = tk.Label(root, text="0/0 images processed", font=("Arial", 12)) |
69 | | -progress_label.pack(pady=5) |
| 80 | + threading.Thread(target=run_conversion, daemon=True).start() |
70 | 81 |
|
71 | | -time_label = tk.Label(root, text="Elapsed time: 0 seconds", font=("Arial", 10)) |
72 | | -time_label.pack(pady=5) |
| 82 | + def update_progress(self): |
| 83 | + self.progress_label.config(text=f"{self.completed_count}/{self.num_files} images processed") |
73 | 84 |
|
| 85 | + def show_completion_message(self): |
| 86 | + total_time = int(time.time() - self.start_time) |
| 87 | + self.root.after(0, lambda: self.status_label.config(text=f"Conversion completed in {total_time} seconds!", fg="blue")) |
| 88 | + |
| 89 | +root = tk.Tk() |
| 90 | +app = ImageConverterApp(root) |
74 | 91 | root.mainloop() |
0 commit comments