From f8fefd29839315ae94dbd1b9fe04f44d96147416 Mon Sep 17 00:00:00 2001 From: tanyar09 Date: Fri, 17 Oct 2025 14:43:34 -0400 Subject: [PATCH] feat: Enhance Identify Panel with responsive face canvas sizing and dynamic updates This commit introduces a new method `_calculate_face_canvas_size` in the `IdentifyPanel` class to calculate a responsive size for the face canvas based on the available window space. Additionally, the `_update_face_canvas_size` method has been implemented to dynamically adjust the canvas size during window resize events. The dashboard GUI has also been updated to improve layout consistency by fixing the width of the folder input text box. These changes enhance the user experience by ensuring that the face canvas adapts to different screen sizes and resolutions. --- src/gui/dashboard_gui.py | 10 ++-- src/gui/identify_panel.py | 121 +++++++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/gui/dashboard_gui.py b/src/gui/dashboard_gui.py index f12a17d..8631691 100644 --- a/src/gui/dashboard_gui.py +++ b/src/gui/dashboard_gui.py @@ -1640,12 +1640,12 @@ class DashboardGUI: tk.Label(folder_frame, text="Folder to scan:", font=("Arial", 12)).grid(row=0, column=0, sticky=tk.W) folder_input_frame = ttk.Frame(folder_frame) - folder_input_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(5, 0)) - folder_input_frame.columnconfigure(0, weight=1) + folder_input_frame.grid(row=1, column=0, sticky=tk.W, pady=(5, 0)) + # Don't configure column weight since we want fixed-width text box self.folder_var = tk.StringVar() - folder_entry = tk.Entry(folder_input_frame, textvariable=self.folder_var, font=("Arial", 11)) - folder_entry.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=(0, 10)) + folder_entry = tk.Entry(folder_input_frame, textvariable=self.folder_var, font=("Arial", 11), width=50) + folder_entry.grid(row=0, column=0, sticky=tk.W, padx=(0, 10)) def browse_folder(): from tkinter import filedialog @@ -1654,7 +1654,7 @@ class DashboardGUI: self.folder_var.set(folder_path) browse_btn = ttk.Button(folder_input_frame, text="Browse", command=browse_folder) - browse_btn.grid(row=0, column=1) + browse_btn.grid(row=0, column=1, sticky=tk.W) # Recursive option self.recursive_var = tk.BooleanVar(value=True) diff --git a/src/gui/identify_panel.py b/src/gui/identify_panel.py index c29c9ca..27e47ac 100644 --- a/src/gui/identify_panel.py +++ b/src/gui/identify_panel.py @@ -73,6 +73,89 @@ class IdentifyPanel: return self.main_frame + def _calculate_face_canvas_size(self) -> int: + """Calculate responsive face canvas size based on available window space""" + try: + # Get the main window to determine available space + root = self.main_frame.winfo_toplevel() + root.update_idletasks() # Ensure geometry is calculated + + # Try to get the actual window height first, fall back to screen height + try: + window_height = root.winfo_height() + if window_height <= 1: # Window not yet rendered + window_height = root.winfo_screenheight() + except: + window_height = root.winfo_screenheight() + + # Estimate space used by other UI elements (in pixels) + # Title bar + menu: ~80px, filters: ~120px, config: ~60px, form: ~120px, buttons: ~40px, padding: ~40px + used_height = 460 + + # Calculate available height for face canvas + available_height = window_height - used_height + + # Define size constraints + min_size = 200 # Minimum usable size + max_size = 400 # Maximum size (original) + preferred_size = 300 # Good balance for most screens + + # Calculate responsive size based on available height + if available_height < 500: # Very small window (laptop with small resolution) + face_size = min_size + elif available_height < 600: # Small laptop screen + face_size = min(max_size, max(min_size, available_height // 2)) + elif available_height < 800: # Medium laptop screen + face_size = min(max_size, max(min_size, available_height // 2.5)) + else: # Large screen + face_size = preferred_size + + # Ensure it's a reasonable size + face_size = max(min_size, min(max_size, int(face_size))) + + if self.verbose > 0: + print(f"📐 Window height: {window_height}px, Available: {available_height}px, Face canvas: {face_size}x{face_size}px") + + return face_size + + except Exception as e: + # Fallback to default size if calculation fails + if self.verbose > 0: + print(f"⚠️ Error calculating face canvas size: {e}, using default 300px") + return 300 + + def _update_face_canvas_size(self): + """Update face canvas size dynamically (for window resize events)""" + try: + if 'face_canvas' not in self.components: + return + + # Calculate new size + new_size = self._calculate_face_canvas_size() + canvas = self.components['face_canvas'] + + # Get current size + current_width = canvas.winfo_width() + current_height = canvas.winfo_height() + + # Only update if size has changed significantly (avoid constant updates) + if abs(current_width - new_size) > 10 or abs(current_height - new_size) > 10: + # Update canvas size + canvas.configure(width=new_size, height=new_size) + + # Refresh the current face image if there's one displayed + if self.current_faces and self.current_face_index < len(self.current_faces): + face_id, photo_id, photo_path, filename, location, face_conf, quality, detector, model = self.current_faces[self.current_face_index] + if self.current_face_crop_path: + self._update_face_image(self.current_face_crop_path, photo_path) + + if self.verbose > 0: + print(f"📐 Updated face canvas size to {new_size}x{new_size}px") + + except Exception as e: + if self.verbose > 0: + print(f"⚠️ Error updating face canvas size: {e}") + def _create_gui_components(self): """Create all GUI components for the identify interface""" # Create variables for form data @@ -426,8 +509,11 @@ class IdentifyPanel: main_content_frame = ttk.Frame(left_panel) main_content_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10)) - # Face image display - flexible height for better layout - self.components['face_canvas'] = tk.Canvas(main_content_frame, width=400, height=400, bg='white', relief='sunken', bd=2) + # Calculate responsive face canvas size based on available space + face_canvas_size = self._calculate_face_canvas_size() + + # Face image display - responsive size for better layout on different screens + self.components['face_canvas'] = tk.Canvas(main_content_frame, width=face_canvas_size, height=face_canvas_size, bg='white', relief='sunken', bd=2) self.components['face_canvas'].pack(pady=(0, 15)) # Person name fields @@ -955,30 +1041,43 @@ class IdentifyPanel: """Update the face image display""" try: if face_crop_path and os.path.exists(face_crop_path): + # Get current canvas size + canvas = self.components['face_canvas'] + canvas_width = canvas.winfo_width() + canvas_height = canvas.winfo_height() + + # Use actual canvas size if available, otherwise use configured size + if canvas_width <= 1 or canvas_height <= 1: + # Canvas not yet rendered, use configured size + canvas_width = canvas['width'] + canvas_height = canvas['height'] + # Load and display face crop image = Image.open(face_crop_path) - # Resize to exactly fill the 400x400 frame - image = image.resize((400, 400), Image.Resampling.LANCZOS) + # Resize to exactly fill the current canvas frame + image = image.resize((canvas_width, canvas_height), Image.Resampling.LANCZOS) photo = ImageTk.PhotoImage(image) # Clear canvas and display image - canvas = self.components['face_canvas'] canvas.delete("all") # Position image at top-left corner like the original canvas.create_image(0, 0, image=photo, anchor=tk.NW) canvas.image = photo # Keep a reference # Add photo icon exactly at the image's top-right corner - # Image starts at (0, 0) and is 400x400, so top-right corner is at (400, 0) + # Image starts at (0, 0) and fills the canvas self.gui_core.create_photo_icon(canvas, photo_path, icon_size=25, face_x=0, face_y=0, - face_width=400, face_height=400, - canvas_width=400, canvas_height=400) + face_width=canvas_width, face_height=canvas_height, + canvas_width=canvas_width, canvas_height=canvas_height) else: # Clear canvas if no image canvas = self.components['face_canvas'] canvas.delete("all") - canvas.create_text(200, 200, text="No face image", fill="gray") + # Center the "No face image" text + canvas_width = canvas.winfo_width() if canvas.winfo_width() > 1 else canvas['width'] + canvas_height = canvas.winfo_height() if canvas.winfo_height() > 1 else canvas['height'] + canvas.create_text(canvas_width//2, canvas_height//2, text="No face image", fill="gray") except Exception as e: print(f"Error updating face image: {e}") @@ -1892,3 +1991,7 @@ class IdentifyPanel: canvas = self.components['similar_canvas'] canvas.update_idletasks() canvas.configure(scrollregion=canvas.bbox("all")) + + # Update face canvas size if needed (for window resize events) + if hasattr(self, 'components') and 'face_canvas' in self.components: + self._update_face_canvas_size()