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:
parent
34c7998ce9
commit
de23fccf6a
@ -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
|
||||
|
||||
|
||||
20
database.py
20
database.py
@ -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
1426
identify_panel.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user