| | import tkinter as tk |
| | from tkinter import filedialog, ttk, messagebox |
| | import os |
| | import threading, queue |
| | from PIL import Image, UnidentifiedImageError |
| |
|
| | |
| | stop_processing = False |
| | error_messages = [] |
| | selected_files = [] |
| | error_list = [] |
| |
|
| | def open_image_error_fix(): |
| | global stop_processing, error_messages, selected_files, status_var, num_files_var, errors_var, progress, q, error_list, error_window, thread_count_var |
| |
|
| | |
| | root = tk.Tk() |
| | root.title("Image Error Fix") |
| |
|
| | |
| | status_var = tk.StringVar() |
| | num_files_var = tk.StringVar() |
| | errors_var = tk.StringVar(value="Errors: 0") |
| | progress = tk.IntVar() |
| | thread_count_var = tk.StringVar(value="1") |
| | q = queue.Queue() |
| |
|
| | def center_window(window): |
| | window.update_idletasks() |
| | width = window.winfo_width() + 120 |
| | height = window.winfo_height() |
| | x = (window.winfo_screenwidth() // 2) - (width // 2) |
| | y = (window.winfo_screenheight() // 2) - (height // 2) |
| | window.geometry(f'{width}x{height}+{x}+{y}') |
| |
|
| | def validate_number(P): |
| | return P.isdigit() or P == "" |
| |
|
| | def validate_thread_count(var): |
| | value = var.get() |
| | if value != "": |
| | try: |
| | int_value = int(value) |
| | if int_value <= 0: |
| | raise ValueError |
| | except ValueError: |
| | messagebox.showerror("Invalid Input", "Please enter a valid number of threads.") |
| | var.set("") |
| |
|
| | def select_files(): |
| | global selected_files |
| | filetypes = [ |
| | ("All Image files", "*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tiff;*.tif;*.svg;*.webp"), |
| | ("JPEG files", "*.jpg;*.jpeg"), |
| | ("PNG files", "*.png"), |
| | ("GIF files", "*.gif"), |
| | ("BMP files", "*.bmp"), |
| | ("TIFF files", "*.tiff;*.tif"), |
| | ("SVG files", "*.svg"), |
| | ("WEBP files", "*.webp") |
| | ] |
| | filepaths = filedialog.askopenfilenames(title="Select Image Files", filetypes=filetypes) |
| | if filepaths: |
| | selected_files.clear() |
| | selected_files.extend(filepaths) |
| | num_files_var.set(f"{len(selected_files)} files selected.") |
| |
|
| | def detect_errors(file_path): |
| | """Detect potential errors in an image file.""" |
| | errors = [] |
| | if not os.access(file_path, os.R_OK): |
| | errors.append("Permission Denied") |
| | |
| | try: |
| | with Image.open(file_path) as img: |
| | img.verify() |
| | img = Image.open(file_path) |
| | img.load() |
| | except UnidentifiedImageError: |
| | errors.append("Unidentified image format or corrupted file") |
| | except IOError as e: |
| | errors.append(f"IOError: {str(e)}") |
| | except Exception as e: |
| | errors.append(f"Unknown error: {str(e)}") |
| | return errors |
| |
|
| | def fix_error(file_path, error_type): |
| | """Fix errors in an image file, if possible.""" |
| | if error_type == "Permission Denied": |
| | |
| | try: |
| | os.chmod(file_path, 0o644) |
| | return "Permissions fixed. File permissions were successfully updated." |
| | except Exception as e: |
| | return f"Failed to fix permissions. Ensure you have the necessary permissions to modify this file. Error: {str(e)}" |
| | elif error_type == "Unidentified image format or corrupted file": |
| | return "The file format is unrecognized or the file is corrupted. Please try to re-download or restore from a backup." |
| | elif error_type == "Invalid Format": |
| | return ("Cannot automatically fix invalid formats. Ensure the file extension matches the file content.\n" |
| | "To fix this issue, please follow these steps:\n" |
| | "1. Identify the correct file format by using a file type checker tool (e.g., CheckFileType.com).\n" |
| | "2. Rename the file with the correct extension:\n" |
| | " - Right-click on the file and select 'Rename'.\n" |
| | " - Change the file extension to the correct format (e.g., .jpg, .png).\n" |
| | " - Press Enter to save the changes.\n" |
| | "3. Try opening the file again. If the problem persists, the file may be corrupted or contain unsupported data.") |
| | elif error_type == "File Not Found": |
| | return ("The file could not be found at the specified path. Please ensure that the file has not been moved or deleted.\n" |
| | "1. Verify the file path is correct.\n" |
| | "2. If the file has been moved, update the path in the application or locate the file manually.\n" |
| | "3. If the file was deleted, check your Recycle Bin or use a data recovery tool to attempt to restore it.") |
| | elif error_type == "File Path Too Long": |
| | return ("The file path exceeds the system limit. Windows has a maximum path length of 260 characters.\n" |
| | "To fix this issue:\n" |
| | "1. Move the file to a location with a shorter path, closer to the root directory (e.g., C:\\).\n" |
| | "2. Rename folders in the path to shorter names.\n" |
| | "3. Enable long path support in Windows if using Windows 10 (version 1607 hoặc cao hơn):\n" |
| | " - Open the Group Policy Editor (gpedit.msc).\n" |
| | " - Navigate to 'Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem'.\n" |
| | " - Enable the 'Enable Win32 long paths' option.") |
| | elif error_type == "Transmission Errors": |
| | return ("The file may be incomplete or corrupted due to transmission errors. This can happen if the file was downloaded or transferred incorrectly.\n" |
| | "To fix this issue:\n" |
| | "1. Re-download hoặc re-transfer the file.\n" |
| | "2. Use a reliable network connection to ensure the file is not corrupted during transfer.\n" |
| | "3. Verify the integrity of the file by comparing checksums (if available).") |
| | elif error_type == "Unsupported Format": |
| | return ("The file format is not supported by this application. Supported formats include JPEG, PNG, GIF, BMP, TIFF, SVG, và WEBP.\n" |
| | "To fix this issue:\n" |
| | "1. Convert the file to a supported format using an image converter tool.\n" |
| | "2. Ensure that the file is not corrupted and can be opened with other image viewers or editors.") |
| | else: |
| | return "No action taken. This error type is not recognized or cannot be fixed automatically." |
| |
|
| | def delete_file(file_path): |
| | """Delete the specified file.""" |
| | try: |
| | os.remove(file_path) |
| | |
| | selected_files.remove(file_path) |
| | num_files_var.set(f"{len(selected_files)} files selected.") |
| | return f"File {file_path} deleted successfully." |
| | except Exception as e: |
| | return f"Failed to delete file {file_path}. Error: {str(e)}" |
| |
|
| | def process_image(file_path, q): |
| | if stop_processing: |
| | return |
| |
|
| | errors = detect_errors(file_path) |
| | if errors: |
| | q.put((file_path, errors)) |
| |
|
| | def worker(): |
| | try: |
| | progress.set(0) |
| | total_files = len(selected_files) |
| | thread_count = int(thread_count_var.get()) |
| | for i, input_path in enumerate(selected_files, 1): |
| | if stop_processing: |
| | break |
| |
|
| | thread = threading.Thread(target=process_image, args=(input_path, q)) |
| | thread.start() |
| | thread.join() |
| |
|
| | |
| | progress.set(int(i / total_files * 100)) |
| | |
| | q.put(None) |
| | except Exception as e: |
| | if not stop_processing: |
| | q.put(e) |
| |
|
| | def update_progress(): |
| | try: |
| | global error_list |
| | error_list = [] |
| | while True: |
| | item = q.get() |
| | if item is None: |
| | break |
| | if isinstance(item, tuple): |
| | error_list.append(item) |
| |
|
| | if not error_list: |
| | messagebox.showinfo("No Errors Found", "No errors were detected in the selected images.") |
| | else: |
| | errors_var.set(f"Errors: {len(error_list)}") |
| | display_errors(error_list) |
| | except Exception as e: |
| | if not stop_processing: |
| | root.after(0, status_var.set, f"Error: {e}") |
| |
|
| | def update_error_display(): |
| | """Update the display of errors in the error window.""" |
| | global error_list, frame |
| | |
| | for widget in frame.winfo_children(): |
| | widget.destroy() |
| |
|
| | |
| | for file_path, errors in error_list: |
| | for error in errors: |
| | frame_row = tk.Frame(frame) |
| | frame_row.pack(fill="x", pady=2) |
| |
|
| | |
| | file_label = tk.Label(frame_row, text=f"{file_path}: {error}", anchor="w", wraplength=500, justify='left') |
| | file_label.pack(side=tk.LEFT, fill="x", expand=True) |
| |
|
| | delete_button = tk.Button(frame_row, text="Delete", command=lambda fp=file_path: delete_file_action(fp)) |
| | delete_button.pack(side=tk.RIGHT) |
| |
|
| | fix_button = tk.Button(frame_row, text="Fix", command=lambda fp=file_path, et=error: fix_error_action(fp, et)) |
| | fix_button.pack(side=tk.RIGHT) |
| |
|
| | |
| | separator = ttk.Separator(frame, orient='horizontal') |
| | separator.pack(fill='x', pady=5) |
| |
|
| | frame.update_idletasks() |
| | canvas.configure(scrollregion=canvas.bbox("all")) |
| |
|
| | def display_errors(error_list): |
| | global error_window, canvas, frame |
| | |
| | if 'error_window' in globals() and error_window.winfo_exists(): |
| | error_window.lift() |
| | else: |
| | error_window = tk.Toplevel(root) |
| | error_window.title("Error Details") |
| | error_window.geometry("600x400") |
| |
|
| | canvas = tk.Canvas(error_window) |
| | canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) |
| |
|
| | scrollbar = tk.Scrollbar(error_window, command=canvas.yview) |
| | scrollbar.pack(side=tk.RIGHT, fill=tk.Y) |
| |
|
| | canvas.configure(yscrollcommand=scrollbar.set) |
| |
|
| | frame = tk.Frame(canvas) |
| | canvas.create_window((0, 0), window=frame, anchor="nw") |
| |
|
| | update_error_display() |
| |
|
| | def fix_error_action(file_path, error_type): |
| | fix_message = fix_error(file_path, error_type) |
| | messagebox.showinfo("Fix Error", fix_message) |
| |
|
| | def delete_file_action(file_path): |
| | global error_list |
| | delete_message = delete_file(file_path) |
| | messagebox.showinfo("Delete File", delete_message) |
| | |
| | error_list = [(fp, errs) for fp, errs in error_list if fp != file_path] |
| | errors_var.set(f"Errors: {len(error_list)}") |
| | update_error_display() |
| |
|
| | def delete_all_errors(): |
| | global error_list |
| | for file_path, _ in error_list: |
| | delete_file(file_path) |
| | error_list.clear() |
| | errors_var.set("Errors: 0") |
| | messagebox.showinfo("Delete All Files", "All files with errors have been deleted.") |
| | update_error_display() |
| |
|
| | def scan_and_fix(): |
| | global stop_processing, error_messages |
| | stop_processing = False |
| | error_messages.clear() |
| | errors_var.set("Errors: 0") |
| | if not selected_files: |
| | status_var.set("Please select images to scan.") |
| | return |
| |
|
| | threading.Thread(target=worker).start() |
| | threading.Thread(target=update_progress).start() |
| |
|
| | def stop_processing_func(): |
| | global stop_processing |
| | stop_processing = True |
| | status_var.set("Processing stopped.") |
| |
|
| | def return_to_menu(): |
| | stop_processing_func() |
| | root.destroy() |
| | import main |
| | main.open_main_menu() |
| |
|
| | def on_closing(): |
| | return_to_menu() |
| |
|
| | |
| | validate_command = root.register(validate_number) |
| |
|
| | back_button = tk.Button(root, text="<-", font=('Helvetica', 14), command=return_to_menu) |
| | back_button.pack(anchor='nw', padx=10, pady=10) |
| |
|
| | title_label = tk.Label(root, text="Image Error Fix", font=('Helvetica', 16)) |
| | title_label.pack(pady=10) |
| |
|
| | select_files_button = tk.Button(root, text="Select Files", command=select_files) |
| | select_files_button.pack(pady=5) |
| |
|
| | num_files_label = tk.Label(root, textvariable=num_files_var) |
| | num_files_label.pack(pady=5) |
| |
|
| | thread_count_label = tk.Label(root, text="Number of Threads:") |
| | thread_count_label.pack(pady=5) |
| |
|
| | thread_count_var = tk.StringVar(value="4") |
| | thread_count_entry = tk.Entry(root, textvariable=thread_count_var, width=3, justify='center', validate="key", validatecommand=(validate_command, '%P')) |
| | thread_count_entry.pack(pady=5) |
| |
|
| | |
| | separator = ttk.Separator(root, orient='horizontal') |
| | separator.pack(fill='x', pady=10) |
| |
|
| | scan_fix_button = tk.Button(root, text="Scan and Fix", command=scan_and_fix) |
| | scan_fix_button.pack(pady=10) |
| |
|
| | stop_button = tk.Button(root, text="Stop", command=stop_processing_func) |
| | stop_button.pack(pady=5) |
| |
|
| | progress_bar = ttk.Progressbar(root, variable=progress, maximum=100) |
| | progress_bar.pack(pady=5, fill=tk.X) |
| |
|
| | delete_all_label = tk.Label(root, text="Click 'Delete All' to remove all files with errors.") |
| | delete_all_label.pack(pady=5) |
| |
|
| | delete_all_button = tk.Button(root, text="Delete All", command=delete_all_errors) |
| | delete_all_button.pack(pady=5) |
| |
|
| | errors_label = tk.Label(root, textvariable=errors_var, fg="red") |
| | errors_label.pack(pady=5) |
| |
|
| | status_label = tk.Label(root, textvariable=status_var, fg="green") |
| | status_label.pack(pady=5) |
| |
|
| | center_window(root) |
| | root.protocol("WM_DELETE_WINDOW", on_closing) |
| | root.mainloop() |
| |
|
| | if __name__ == "__main__": |
| | open_image_error_fix() |
| |
|