Add last name search functionality to PhotoTagger GUI. Implement search and clear buttons for filtering people by last name, enhancing user experience. Update navigation and display logic to reflect filtered results, ensuring proper handling of UI states and feedback when no matches are found.

This commit is contained in:
tanyar09 2025-09-29 13:07:46 -04:00
parent 347a597927
commit 2394afb5ee

View File

@ -2710,13 +2710,24 @@ class PhotoTagger:
# Configure row weights
main_frame.rowconfigure(0, weight=1)
# Search controls for filtering people by last name
last_name_search_var = tk.StringVar()
search_row = ttk.Frame(left_frame)
search_row.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 10))
search_entry = ttk.Entry(search_row, textvariable=last_name_search_var, width=20)
search_entry.pack(side=tk.LEFT)
search_btn = ttk.Button(search_row, text="Search", width=8)
search_btn.pack(side=tk.LEFT, padx=(6, 0))
clear_btn = ttk.Button(search_row, text="Clear", width=6)
clear_btn.pack(side=tk.LEFT, padx=(6, 0))
# Matched person info
matched_info_label = ttk.Label(left_frame, text="", font=("Arial", 10, "bold"))
matched_info_label.grid(row=0, column=0, pady=(0, 10), sticky=tk.W)
matched_info_label.grid(row=1, column=0, pady=(0, 10), sticky=tk.W)
# Matched person image
matched_canvas = tk.Canvas(left_frame, width=300, height=300, bg='white')
matched_canvas.grid(row=1, column=0, pady=(0, 10))
matched_canvas.grid(row=2, column=0, pady=(0, 10))
# Save button for this person (will be created after function definitions)
save_btn = None
@ -2777,6 +2788,7 @@ class PhotoTagger:
# Button commands
current_matched_index = 0
matched_ids = [person_id for person_id, _, _ in person_faces_list if person_id in matches_by_matched and matches_by_matched[person_id]]
filtered_matched_ids = None # filtered subset based on last name search
match_checkboxes = []
match_vars = []
@ -2846,7 +2858,8 @@ class PhotoTagger:
# Save current checkbox states before navigating away
save_current_checkbox_states()
current_matched_index += 1
if current_matched_index < len(matched_ids):
active_ids = filtered_matched_ids if filtered_matched_ids is not None else matched_ids
if current_matched_index < len(active_ids):
update_display()
else:
finish_auto_match()
@ -2874,6 +2887,48 @@ class PhotoTagger:
return True
return False
def apply_last_name_filter():
"""Filter people by last name and update navigation"""
nonlocal filtered_matched_ids, current_matched_index
query = last_name_search_var.get().strip().lower()
if query:
# Filter person_faces_list by last name
filtered_people = []
for person_id, face, person_name in person_faces_list:
# Extract last name from person_name (format: "Last, First")
if ',' in person_name:
last_name = person_name.split(',')[0].strip().lower()
else:
last_name = person_name.strip().lower()
if query in last_name:
filtered_people.append((person_id, face, person_name))
# Get filtered matched_ids
filtered_matched_ids = [person_id for person_id, _, _ in filtered_people if person_id in matches_by_matched and matches_by_matched[person_id]]
else:
filtered_matched_ids = None
# Reset to first person in filtered list
current_matched_index = 0
if filtered_matched_ids:
update_display()
else:
# No matches - clear display
matched_info_label.config(text="No people match filter")
matched_canvas.delete("all")
matched_canvas.create_text(150, 150, text="No matches found", fill="gray")
matches_canvas.delete("all")
update_button_states()
def clear_last_name_filter():
"""Clear filter and show all people"""
nonlocal filtered_matched_ids, current_matched_index
last_name_search_var.set("")
filtered_matched_ids = None
current_matched_index = 0
update_display()
def on_quit_auto_match():
nonlocal window_destroyed
@ -2985,10 +3040,11 @@ class PhotoTagger:
# Create save button now that functions are defined
save_btn = ttk.Button(left_frame, text="💾 Save Changes", command=on_confirm_matches)
save_btn.grid(row=2, column=0, pady=(0, 10), sticky=(tk.W, tk.E))
save_btn.grid(row=3, column=0, pady=(0, 10), sticky=(tk.W, tk.E))
def update_button_states():
"""Update button states based on current position"""
active_ids = filtered_matched_ids if filtered_matched_ids is not None else matched_ids
# Enable/disable Back button based on position
if current_matched_index > 0:
back_btn.config(state='normal')
@ -2996,15 +3052,16 @@ class PhotoTagger:
back_btn.config(state='disabled')
# Enable/disable Next button based on position
if current_matched_index < len(matched_ids) - 1:
if current_matched_index < len(active_ids) - 1:
next_btn.config(state='normal')
else:
next_btn.config(state='disabled')
def update_save_button_text():
"""Update save button text with current person name"""
if current_matched_index < len(matched_ids):
matched_id = matched_ids[current_matched_index]
active_ids = filtered_matched_ids if filtered_matched_ids is not None else matched_ids
if current_matched_index < len(active_ids):
matched_id = active_ids[current_matched_index]
# Get person name from the first match for this person
matches_for_current_person = matches_by_matched[matched_id]
if matches_for_current_person:
@ -3042,11 +3099,12 @@ class PhotoTagger:
def update_display():
nonlocal current_matched_index
if current_matched_index >= len(matched_ids):
active_ids = filtered_matched_ids if filtered_matched_ids is not None else matched_ids
if current_matched_index >= len(active_ids):
finish_auto_match()
return
matched_id = matched_ids[current_matched_index]
matched_id = active_ids[current_matched_index]
matches_for_this_person = matches_by_matched[matched_id]
# Update button states
@ -3056,7 +3114,8 @@ class PhotoTagger:
update_save_button_text()
# Update title
root.title(f"Auto-Match Face Identification - {current_matched_index + 1}/{len(matched_ids)}")
active_ids = filtered_matched_ids if filtered_matched_ids is not None else matched_ids
root.title(f"Auto-Match Face Identification - {current_matched_index + 1}/{len(active_ids)}")
# Get the first match to get matched person info
if not matches_for_this_person:
@ -3234,6 +3293,14 @@ class PhotoTagger:
# Window was destroyed before we could show it
return 0
# Wire up search controls now that helper functions exist
try:
search_btn.config(command=lambda: apply_last_name_filter())
clear_btn.config(command=lambda: clear_last_name_filter())
search_entry.bind('<Return>', lambda e: apply_last_name_filter())
except Exception:
pass
# Start with first matched person
update_display()