Project

General

Profile

Codes » History » Revision 4

Revision 3 (Zhi Jie YEW, 11/06/2025 02:47 PM) → Revision 4/7 (Zhi Jie YEW, 11/06/2025 02:48 PM)

[[Wiki|← Back to Start Page]] 

 h1. Codes 

 gui.py 
 <pre><code class="python"> <pre><code> 
 # gui.py 

 import tkinter as tk 
 from tkinter import ttk, filedialog, messagebox 
 import threading 
 import json 
 import os 

 # Import the logic classes 
 from main_alpha_blender import MainAlphaBlender 
 from video_processor import VideoProcessor 

 class BlenderGUI: 
     """A Tkinter GUI with tabs for image and video edge blending.""" 
     def __init__(self, master): 
         self.master = master 
         master.title("Image and Video Edge Blender") 
         master.geometry("600x450") # Increased height for new buttons 

         # --- Create a Tabbed Interface --- 
         self.notebook = ttk.Notebook(master) 
         self.notebook.pack(pady=10, padx=10, fill="both", expand=True) 

         self.image_tab = ttk.Frame(self.notebook, padding="10") 
         self.video_tab = ttk.Frame(self.notebook, padding="10") 

         self.notebook.add(self.image_tab, text="Image Blender") 
         self.notebook.add(self.video_tab, text="Video Processor") 

         # --- Populate each tab --- 
         self.create_image_widgets() 
         self.create_video_widgets() 

         # --- NEW: Add a frame at the bottom for config management --- 
         self.config_frame = ttk.Frame(master, padding=(10, 0, 10, 10)) 
         self.config_frame.pack(fill=tk.X, side=tk.BOTTOM) 
         self.create_config_widgets() 

         # --- NEW: Load default config on startup --- 
         # It will silently fail if config.json doesn't exist, using hardcoded defaults. 
         self.load_config(filepath="config.json", silent=True) 

     def create_image_widgets(self): 
         """Creates all widgets for the Image Blender tab.""" 
         self.image_blender = MainAlphaBlender() 
        
         ttk.Label(self.image_tab, text="Input Image Directory:").grid(row=0, column=0, sticky=tk.W, pady=2) 
         self.img_input_path_var = tk.StringVar(value=self.image_blender.image_path) 
         ttk.Entry(self.image_tab, textvariable=self.img_input_path_var, width=50).grid(row=0, column=1, sticky=tk.EW, padx=5) 
         ttk.Button(self.image_tab, text="Browse...", command=self.select_img_input_dir).grid(row=0, column=2) 

         ttk.Label(self.image_tab, text="Output Directory:").grid(row=1, column=0, sticky=tk.W, pady=2) 
         self.img_output_path_var = tk.StringVar(value=self.image_blender.output_dir) 
         ttk.Entry(self.image_tab, textvariable=self.img_output_path_var, width=50).grid(row=1, column=1, sticky=tk.EW, padx=5) 
         ttk.Button(self.image_tab, text="Browse...", command=self.select_img_output_dir).grid(row=1, column=2) 

         ttk.Label(self.image_tab, text="Blend Width (pixels):").grid(row=2, column=0, sticky=tk.W, pady=5) 
         self.img_blend_width_var = tk.IntVar(value=self.image_blender.blend_width) 
         ttk.Entry(self.image_tab, textvariable=self.img_blend_width_var, width=10).grid(row=2, column=1, sticky=tk.W, padx=5) 

         ttk.Label(self.image_tab, text="Gamma Value:").grid(row=3, column=0, sticky=tk.W, pady=2) 
         self.img_gamma_var = tk.DoubleVar(value=self.image_blender.gamma_value) 
         ttk.Entry(self.image_tab, textvariable=self.img_gamma_var, width=10).grid(row=3, column=1, sticky=tk.W, padx=5) 

         ttk.Label(self.image_tab, text="Blend Method:").grid(row=4, column=0, sticky=tk.W, pady=2) 
         self.img_method_var = tk.StringVar(value=self.image_blender.method) 
         methods = ['linear', 'cosine', 'quadratic', 'sqrt', 'log', 'sigmoid'] 
         ttk.Combobox(self.image_tab, textvariable=self.img_method_var, values=methods, state="readonly").grid(row=4, column=1, sticky=tk.W, padx=5) 

         self.img_preview_var = tk.BooleanVar(value=self.image_blender.preview) 
         ttk.Checkbutton(self.image_tab, text="Show Preview After Processing", variable=self.img_preview_var).grid(row=5, column=1, sticky=tk.W, pady=10, padx=5) 

         ttk.Button(self.image_tab, text="Run Blending Process", command=self.run_image_blending).grid(row=6, column=1, pady=20, sticky=tk.W) 

         self.img_status_var = tk.StringVar(value="Ready.") 
         ttk.Label(self.image_tab, textvariable=self.img_status_var, font=("Helvetica", 10, "italic")).grid(row=7, column=0, columnspan=3, sticky=tk.W, pady=5) 

         self.image_tab.columnconfigure(1, weight=1) 

     def create_video_widgets(self): 
         """Creates all widgets for the Video Processor tab.""" 
         self.video_processor = VideoProcessor() 

         ttk.Label(self.video_tab, text="Input Video File:").grid(row=0, column=0, sticky=tk.W, pady=2) 
         self.vid_input_path_var = tk.StringVar() 
         ttk.Entry(self.video_tab, textvariable=self.vid_input_path_var, width=50).grid(row=0, column=1, sticky=tk.EW, padx=5) 
         ttk.Button(self.video_tab, text="Browse...", command=self.select_vid_input_file).grid(row=0, column=2) 

         ttk.Label(self.video_tab, text="Output Directory:").grid(row=1, column=0, sticky=tk.W, pady=2) 
         self.vid_output_path_var = tk.StringVar(value=self.video_processor.output_dir) 
         ttk.Entry(self.video_tab, textvariable=self.vid_output_path_var, width=50).grid(row=1, column=1, sticky=tk.EW, padx=5) 
         ttk.Button(self.video_tab, text="Browse...", command=self.select_vid_output_dir).grid(row=1, column=2) 

         ttk.Label(self.video_tab, text="Blend Width (pixels):").grid(row=2, column=0, sticky=tk.W, pady=5) 
         self.vid_blend_width_var = tk.IntVar(value=self.video_processor.blend_width) 
         ttk.Entry(self.video_tab, textvariable=self.vid_blend_width_var, width=10).grid(row=2, column=1, sticky=tk.W, padx=5) 

         ttk.Label(self.video_tab, text="Blend Method:").grid(row=3, column=0, sticky=tk.W, pady=2) 
         self.vid_method_var = tk.StringVar(value=self.video_processor.blend_method) 
         methods = ['linear', 'cosine'] 
         ttk.Combobox(self.video_tab, textvariable=self.vid_method_var, values=methods, state="readonly").grid(row=3, column=1, sticky=tk.W, padx=5) 

         self.run_video_button = ttk.Button(self.video_tab, text="Process Video", command=self.run_video_processing_thread) 
         self.run_video_button.grid(row=4, column=1, pady=20, sticky=tk.W) 

         self.vid_status_var = tk.StringVar(value="Ready.") 
         ttk.Label(self.video_tab, textvariable=self.vid_status_var).grid(row=5, column=0, columnspan=3, sticky=tk.W, pady=5) 
        
         self.video_tab.columnconfigure(1, weight=1) 

     def create_config_widgets(self): 
         """Creates the Load and Save configuration buttons.""" 
         ttk.Button(self.config_frame, text="Load Config", command=self.load_config).pack(side=tk.LEFT, padx=5) 
         ttk.Button(self.config_frame, text="Save Config", command=self.save_config).pack(side=tk.LEFT, padx=5) 

     def load_config(self, filepath=None, silent=False): 
         """Loads settings from a JSON file and updates the GUI.""" 
         if filepath is None: 
             filepath = filedialog.askopenfilename( 
                 title="Open Configuration File", 
                 filetypes=[("JSON files", "*.json"), ("All files", "*.*")] 
             ) 
        
         if not filepath or not os.path.exists(filepath): 
             if not silent: 
                 messagebox.showwarning("Load Config", "No configuration file selected or file not found.") 
             return 

         try: 
             with open(filepath, 'r') as f: 
                 data = json.load(f) 

             # Update Image Tab variables 
             self.img_input_path_var.set(data.get("image_path", "OriginalImages")) 
             self.img_output_path_var.set(data.get("output_dir", "Results")) 
             self.img_blend_width_var.set(data.get("blend_width", 200)) 
             self.img_gamma_var.set(data.get("gamma_value", 1.4)) 
             self.img_method_var.set(data.get("blend_method", "cosine")) 
             self.img_preview_var.set(data.get("preview", True)) 

             # Update Video Tab variables 
             self.vid_input_path_var.set(data.get("video_input_path", "")) 
             self.vid_output_path_var.set(data.get("video_output_dir", "VideoResults")) 
             self.vid_blend_width_var.set(data.get("video_blend_width", 100)) 
             self.vid_method_var.set(data.get("video_blend_method", "linear")) 
            
             if not silent: 
                 messagebox.showinfo("Load Config", f"Configuration loaded successfully from {os.path.basename(filepath)}.") 

         except Exception as e: 
             if not silent: 
                 messagebox.showerror("Load Config Error", f"Failed to load or parse the configuration file.\n\nError: {e}") 

     def save_config(self): 
         """Saves the current GUI settings to a JSON file.""" 
         filepath = filedialog.asksaveasfilename( 
             title="Save Configuration File", 
             defaultextension=".json", 
             initialfile="config.json", 
             filetypes=[("JSON files", "*.json"), ("All files", "*.*")] 
         ) 

         if not filepath: 
             return 

         try: 
             config_data = { 
                 # Image Tab settings 
                 "image_path": self.img_input_path_var.get(), 
                 "output_dir": self.img_output_path_var.get(), 
                 "blend_width": self.img_blend_width_var.get(), 
                 "gamma_value": self.img_gamma_var.get(), 
                 "blend_method": self.img_method_var.get(), 
                 "preview": self.img_preview_var.get(), 
                
                 # Video Tab settings 
                 "video_input_path": self.vid_input_path_var.get(), 
                 "video_output_dir": self.vid_output_path_var.get(), 
                 "video_blend_width": self.vid_blend_width_var.get(), 
                 "video_blend_method": self.vid_method_var.get() 
             } 

             with open(filepath, 'w') as f: 
                 json.dump(config_data, f, indent=4) 
            
             messagebox.showinfo("Save Config", f"Configuration saved successfully to {os.path.basename(filepath)}.") 

         except Exception as e: 
             messagebox.showerror("Save Config Error", f"Failed to save the configuration file.\n\nError: {e}") 

     # --- Callbacks for Image Tab --- 
     def select_img_input_dir(self): 
         path = filedialog.askdirectory(title="Select Input Image Directory") 
         if path: self.img_input_path_var.set(path) 

     def select_img_output_dir(self): 
         path = filedialog.askdirectory(title="Select Output Directory") 
         if path: self.img_output_path_var.set(path) 

     def run_image_blending(self): 
         self.image_blender.image_path = self.img_input_path_var.get() 
         self.image_blender.output_dir = self.img_output_path_var.get() 
         self.image_blender.blend_width = self.img_blend_width_var.get() 
         self.image_blender.gamma_value = self.img_gamma_var.get() 
         self.image_blender.method = self.img_method_var.get() 
         self.image_blender.preview = self.img_preview_var.get() 
         self.image_blender.update_paths() 
        
         success, message = self.image_blender.run() 
         if success: 
             self.img_status_var.set(f"Success! {message}") 
             messagebox.showinfo("Success", message) 
         else: 
             self.img_status_var.set(f"Error: {message}") 
             messagebox.showerror("Error", message) 

     # --- Callbacks for Video Tab --- 
     def select_vid_input_file(self): 
         path = filedialog.askopenfilename(title="Select Input Video File", filetypes=[("MP4 files", "*.mp4"), ("All files", "*.*")]) 
         if path: self.vid_input_path_var.set(path) 

     def select_vid_output_dir(self): 
         path = filedialog.askdirectory(title="Select Output Directory") 
         if path: self.vid_output_path_var.set(path) 

     def update_video_status(self, message): 
         """Thread-safe method to update the GUI status label.""" 
         self.vid_status_var.set(message) 

     def run_video_processing_thread(self): 
         """Starts the video processing in a new thread to avoid freezing the GUI.""" 
         self.run_video_button.config(state="disabled") 
         thread = threading.Thread(target=self.run_video_processing) 
         thread.daemon = True 
         thread.start() 

     def run_video_processing(self): 
         """The actual processing logic, run in the background thread.""" 
         try: 
             self.video_processor.input_video_path = self.vid_input_path_var.get() 
             self.video_processor.output_dir = self.vid_output_path_var.get() 
             self.video_processor.blend_width = self.vid_blend_width_var.get() 
             self.video_processor.blend_method = self.vid_method_var.get() 

             success, message = self.video_processor.run(status_callback=self.update_video_status) 

             if success: 
                 messagebox.showinfo("Success", message) 
             else: 
                 messagebox.showerror("Error", message) 

         except Exception as e: 
             messagebox.showerror("Critical Error", f"An unexpected error occurred: {e}") 
         finally: 
             self.run_video_button.config(state="normal") 
 </code></pre>