Integrate Identify Panel into Dashboard GUI for enhanced face identification functionality

This commit introduces the IdentifyPanel class into the Dashboard GUI, allowing for a fully integrated face identification interface. The Dashboard now requires a database manager and face processor to create the Identify panel, which includes features for face browsing, identification, and management. Additionally, the DatabaseManager has been updated to support case-insensitive person additions, improving data consistency. The PhotoTagger class has also been modified to accommodate these changes, ensuring seamless interaction between components.
This commit is contained in:
tanyar09 2025-10-09 15:36:44 -04:00
parent 34c7998ce9
commit de23fccf6a
4 changed files with 1477 additions and 37 deletions

View File

@ -11,6 +11,7 @@ from tkinter import ttk, messagebox
from typing import Dict, Optional, Callable
from gui_core import GUICore
from identify_panel import IdentifyPanel
class DashboardGUI:
@ -20,8 +21,10 @@ class DashboardGUI:
navigation (menu bar) and content (panels).
"""
def __init__(self, gui_core: GUICore, on_scan=None, on_process=None, on_identify=None):
def __init__(self, gui_core: GUICore, db_manager=None, face_processor=None, on_scan=None, on_process=None, on_identify=None):
self.gui_core = gui_core
self.db_manager = db_manager
self.face_processor = face_processor
self.on_scan = on_scan
self.on_process = on_process
self.on_identify = on_identify
@ -274,30 +277,36 @@ class DashboardGUI:
return panel
def _create_identify_panel(self) -> ttk.Frame:
"""Create the identify panel (placeholder for now)"""
"""Create the identify panel with full functionality"""
panel = ttk.Frame(self.content_frame)
# Title
title_label = ttk.Label(panel, text="👤 Identify Faces", font=("Arial", 18, "bold"))
title_label.pack(anchor="w", pady=(0, 20))
# Placeholder content
placeholder_frame = ttk.LabelFrame(panel, text="Coming Soon", padding="20")
placeholder_frame.pack(expand=True, fill=tk.BOTH)
placeholder_text = (
"The Identify panel will be integrated here.\n\n"
"This will contain the full face identification interface\n"
"currently available in the separate Identify window.\n\n"
"Features will include:\n"
"• Face browsing and identification\n"
"• Similar face matching\n"
"• Person management\n"
"• Batch processing options"
)
placeholder_label = ttk.Label(placeholder_frame, text=placeholder_text, font=("Arial", 12), justify=tk.LEFT)
placeholder_label.pack(expand=True)
# Create the identify panel if we have the required dependencies
if self.db_manager and self.face_processor:
self.identify_panel = IdentifyPanel(panel, self.db_manager, self.face_processor, self.gui_core)
identify_frame = self.identify_panel.create_panel()
identify_frame.pack(fill=tk.BOTH, expand=True)
else:
# Fallback placeholder if dependencies are not available
placeholder_frame = ttk.LabelFrame(panel, text="Configuration Required", padding="20")
placeholder_frame.pack(expand=True, fill=tk.BOTH)
placeholder_text = (
"Identify panel requires database and face processor to be configured.\n\n"
"This will contain the full face identification interface\n"
"currently available in the separate Identify window.\n\n"
"Features will include:\n"
"• Face browsing and identification\n"
"• Similar face matching\n"
"• Person management\n"
"• Batch processing options"
)
placeholder_label = ttk.Label(placeholder_frame, text=placeholder_text, font=("Arial", 12), justify=tk.LEFT)
placeholder_label.pack(expand=True)
return panel

View File

@ -241,20 +241,28 @@ class DatabaseManager:
def add_person(self, first_name: str, last_name: str, middle_name: str = None,
maiden_name: str = None, date_of_birth: str = None) -> int:
"""Add a person to the database and return their ID"""
"""Add a person to the database and return their ID (case-insensitive)"""
# Normalize names to title case for case-insensitive matching
normalized_first = first_name.strip().title()
normalized_last = last_name.strip().title()
normalized_middle = middle_name.strip().title() if middle_name else ''
normalized_maiden = maiden_name.strip().title() if maiden_name else ''
normalized_dob = date_of_birth.strip() if date_of_birth else ''
with self.get_db_connection() as conn:
cursor = conn.cursor()
cursor.execute('''
INSERT OR IGNORE INTO people (first_name, last_name, middle_name, maiden_name, date_of_birth)
VALUES (?, ?, ?, ?, ?)
''', (first_name, last_name, middle_name, maiden_name, date_of_birth))
''', (normalized_first, normalized_last, normalized_middle, normalized_maiden, normalized_dob))
# Get the person ID
# Get the person ID (case-insensitive lookup)
cursor.execute('''
SELECT id FROM people
WHERE first_name = ? AND last_name = ? AND middle_name = ?
AND maiden_name = ? AND date_of_birth = ?
''', (first_name, last_name, middle_name, maiden_name, date_of_birth))
WHERE LOWER(first_name) = LOWER(?) AND LOWER(last_name) = LOWER(?)
AND LOWER(COALESCE(middle_name, '')) = LOWER(?) AND LOWER(COALESCE(maiden_name, '')) = LOWER(?)
AND date_of_birth = ?
''', (normalized_first, normalized_last, normalized_middle, normalized_maiden, normalized_dob))
result = cursor.fetchone()
return result[0] if result else None

1426
identify_panel.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,7 @@ class PhotoTagger:
self.modify_identified_gui = ModifyIdentifiedGUI(self.db, self.face_processor, verbose)
self.tag_manager_gui = TagManagerGUI(self.db, self.gui_core, self.tag_manager, self.face_processor, verbose)
self.search_gui = SearchGUI(self.db, self.search_stats, self.gui_core, self.tag_manager, verbose)
self.dashboard_gui = DashboardGUI(self.gui_core, on_scan=self._dashboard_scan, on_process=self._dashboard_process, on_identify=self._dashboard_identify)
self.dashboard_gui = DashboardGUI(self.gui_core, self.db, self.face_processor, on_scan=self._dashboard_scan, on_process=self._dashboard_process, on_identify=self._dashboard_identify)
# Legacy compatibility - expose some methods directly
self._db_connection = None
@ -179,10 +179,10 @@ class PhotoTagger:
return self.search_stats.print_statistics()
# GUI methods (legacy compatibility - these would need to be implemented)
def identify_faces(self, batch_size: int = DEFAULT_BATCH_SIZE, show_faces: bool = False, tolerance: float = DEFAULT_FACE_TOLERANCE,
def identify_faces(self, batch_size: int = DEFAULT_BATCH_SIZE, tolerance: float = DEFAULT_FACE_TOLERANCE,
date_from: str = None, date_to: str = None, date_processed_from: str = None, date_processed_to: str = None) -> int:
"""Interactive face identification with GUI"""
return self.identify_gui.identify_faces(batch_size, show_faces, tolerance,
"""Interactive face identification with GUI (show_faces is always True)"""
return self.identify_gui.identify_faces(batch_size, True, tolerance,
date_from, date_to, date_processed_from, date_processed_to)
def tag_management(self) -> int:
@ -211,11 +211,11 @@ class PhotoTagger:
return self.process_faces()
return self.process_faces(limit=limit_value)
def _dashboard_identify(self, batch_value: Optional[int], show_faces: bool) -> int:
"""Callback to identify faces from the dashboard with optional batch and show_faces."""
def _dashboard_identify(self, batch_value: Optional[int]) -> int:
"""Callback to identify faces from the dashboard with optional batch (show_faces is always True)."""
if batch_value is None:
return self.identify_faces(show_faces=show_faces)
return self.identify_faces(batch_size=batch_value, show_faces=show_faces)
return self.identify_faces()
return self.identify_faces(batch_size=batch_value)
def _setup_window_size_saving(self, root, config_file="gui_config.json"):
"""Set up window size saving functionality (legacy compatibility)"""
@ -342,8 +342,6 @@ Examples:
parser.add_argument('--recursive', action='store_true',
help='Scan folders recursively')
parser.add_argument('--show-faces', action='store_true',
help='Show individual face crops during identification')
parser.add_argument('--tolerance', type=float, default=DEFAULT_FACE_TOLERANCE,
help=f'Face matching tolerance (0.0-1.0, lower = stricter, default: {DEFAULT_FACE_TOLERANCE})')
@ -397,8 +395,7 @@ Examples:
tagger.process_faces(args.limit, args.model)
elif args.command == 'identify':
show_faces = getattr(args, 'show_faces', False)
tagger.identify_faces(args.batch, show_faces, args.tolerance,
tagger.identify_faces(args.batch, args.tolerance,
args.date_from, args.date_to,
args.date_processed_from, args.date_processed_to)