diff --git a/photo_tagger.py b/photo_tagger.py index 7e116cc..ab752b4 100644 --- a/photo_tagger.py +++ b/photo_tagger.py @@ -3338,6 +3338,18 @@ class PhotoTagger: people_frame = ttk.LabelFrame(main_frame, text="People", padding="10") people_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 8)) people_frame.columnconfigure(0, weight=1) + + # Search controls (Last Name) + last_name_search_var = tk.StringVar() + search_row = ttk.Frame(people_frame) + search_row.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 6)) + 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)) + people_canvas = tk.Canvas(people_frame, bg='white') people_scrollbar = ttk.Scrollbar(people_frame, orient="vertical", command=people_canvas.yview) people_list_inner = ttk.Frame(people_canvas) @@ -3349,9 +3361,9 @@ class PhotoTagger: lambda e: people_canvas.configure(scrollregion=people_canvas.bbox("all")) ) - people_canvas.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) - people_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S)) - people_frame.rowconfigure(0, weight=1) + people_canvas.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + people_scrollbar.grid(row=1, column=1, sticky=(tk.N, tk.S)) + people_frame.rowconfigure(1, weight=1) # Right panel: Faces for selected person faces_frame = ttk.LabelFrame(main_frame, text="Faces", padding="10") @@ -3399,6 +3411,7 @@ class PhotoTagger: # Load people from DB with counts people_data = [] # list of dicts: {id, name, count, first_name, last_name} + people_filtered = None # filtered subset based on last name search def load_people(): nonlocal people_data @@ -3445,6 +3458,34 @@ class PhotoTagger: 'date_of_birth': date_of_birth or "", 'count': count }) + # Re-apply filter (if any) after loading + try: + apply_last_name_filter() + except Exception: + pass + + # 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('', lambda e: apply_last_name_filter()) + except Exception: + pass + + def apply_last_name_filter(): + nonlocal people_filtered + query = last_name_search_var.get().strip().lower() + if query: + people_filtered = [p for p in people_data if p.get('last_name', '').lower().find(query) != -1] + else: + people_filtered = None + populate_people_list() + + def clear_last_name_filter(): + nonlocal people_filtered + last_name_search_var.set("") + people_filtered = None + populate_people_list() def clear_faces_panel(): for w in faces_inner.winfo_children(): @@ -3618,7 +3659,12 @@ class PhotoTagger: def populate_people_list(): for w in people_list_inner.winfo_children(): w.destroy() - for idx, person in enumerate(people_data): + source = people_filtered if people_filtered is not None else people_data + if not source: + empty_label = ttk.Label(people_list_inner, text="No people match filter", foreground="gray") + empty_label.grid(row=0, column=0, sticky=tk.W, pady=4) + return + for idx, person in enumerate(source): row = ttk.Frame(people_list_inner) row.grid(row=idx, column=0, sticky=(tk.W, tk.E), pady=4) # Freeze per-row values to avoid late-binding issues