import tkinter as tk from tkinter import filedialog, ttk, messagebox import os import threading, queue from PIL import Image, UnidentifiedImageError # Global variables for managing state and errors 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 # Initialize the main Tkinter window root = tk.Tk() root.title("Image Error Fix") # Initialize Tkinter variables 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") # Default thread count 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") # Additional error checks try: with Image.open(file_path) as img: img.verify() # Verify the image integrity img = Image.open(file_path) img.load() # Load the image data to ensure it's not truncated 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": # Attempt to change permissions 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) # Remove from selected files 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()) # Get the number of threads 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() # Update progress bar 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 # Clear all existing widgets in the frame for widget in frame.winfo_children(): widget.destroy() # Repopulate the frame with updated error list for file_path, errors in error_list: for error in errors: frame_row = tk.Frame(frame) frame_row.pack(fill="x", pady=2) # Hiển thị đường dẫn đầy đủ và tự động xuống dòng 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) # Thêm dấu phân cách giữa các lỗi 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 # Create or raise the error window 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) # Remove the error from the error_list and refresh the display 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() # Update the error display after deleting all errors 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() # Create GUI elements 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 for aesthetics 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()