diff --git a/auto_match_gui.py b/auto_match_gui.py index fa937a3..4ba6c38 100644 --- a/auto_match_gui.py +++ b/auto_match_gui.py @@ -305,8 +305,9 @@ class AutoMatchGUI: def on_confirm_matches(): nonlocal identified_count, current_matched_index, identified_faces_per_person - 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] matches_for_this_person = matches_by_matched[matched_id] # Initialize identified faces for this person if not exists @@ -438,87 +439,22 @@ class AutoMatchGUI: def on_quit_auto_match(): nonlocal window_destroyed - # Check for unsaved changes before quitting if has_unsaved_changes(): - # Show warning dialog with custom width - from tkinter import messagebox - - # Create a custom dialog for better width control - dialog = tk.Toplevel(root) - dialog.title("Unsaved Changes") - dialog.geometry("500x250") - dialog.resizable(True, True) - dialog.transient(root) - dialog.grab_set() - - # Center the dialog - dialog.geometry("+%d+%d" % (root.winfo_rootx() + 50, root.winfo_rooty() + 50)) - - # Main message - message_frame = ttk.Frame(dialog, padding="20") - message_frame.pack(fill=tk.BOTH, expand=True) - - # Warning icon and text - icon_label = ttk.Label(message_frame, text="⚠️", font=("Arial", 16)) - icon_label.pack(anchor=tk.W) - - main_text = ttk.Label(message_frame, - text="You have unsaved changes that will be lost if you quit.", - font=("Arial", 10)) - main_text.pack(anchor=tk.W, pady=(5, 10)) - - # Options - options_text = ttk.Label(message_frame, - text="• Yes: Save current changes and quit\n" - "• No: Quit without saving\n" - "• Cancel: Return to auto-match", - font=("Arial", 9)) - options_text.pack(anchor=tk.W, pady=(0, 10)) - - - # Buttons - button_frame = ttk.Frame(dialog) - button_frame.pack(fill=tk.X, padx=20, pady=(0, 20)) - - result = None - - def on_yes(): - nonlocal result - result = True - dialog.destroy() - - def on_no(): - nonlocal result - result = False - dialog.destroy() - - def on_cancel(): - nonlocal result - result = None - dialog.destroy() - - yes_btn = ttk.Button(button_frame, text="Yes", command=on_yes) - no_btn = ttk.Button(button_frame, text="No", command=on_no) - cancel_btn = ttk.Button(button_frame, text="Cancel", command=on_cancel) - - yes_btn.pack(side=tk.LEFT, padx=(0, 5)) - no_btn.pack(side=tk.LEFT, padx=5) - cancel_btn.pack(side=tk.RIGHT, padx=(5, 0)) - - # Wait for dialog to close - dialog.wait_window() - - if result is None: # Cancel - don't quit + result = messagebox.askyesnocancel( + "Unsaved Changes", + "You have unsaved changes that will be lost if you quit.\n\n" + "Yes: Save current changes and quit\n" + "No: Quit without saving\n" + "Cancel: Return to auto-match" + ) + if result is None: + # Cancel return - elif result: # Yes - save changes first - # Save current checkbox states before quitting + if result: + # Save current person's changes, then quit save_current_checkbox_states() - # Note: We don't actually save to database here, just preserve the states - # The user would need to click Save button for each person to persist changes - print("⚠️ Warning: Changes are preserved but not saved to database.") - print(" Click 'Save Changes' button for each person to persist changes.") - + on_confirm_matches() if not window_destroyed: window_destroyed = True try: @@ -587,8 +523,9 @@ class AutoMatchGUI: Note: Do NOT modify original states here to avoid false positives when a user toggles and reverts a checkbox. """ - if current_matched_index < len(matched_ids) and match_vars: - current_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) and match_vars: + current_matched_id = active_ids[current_matched_index] matches_for_current_person = matches_by_matched[current_matched_id] if len(match_vars) == len(matches_for_current_person): @@ -752,8 +689,10 @@ class AutoMatchGUI: if self.verbose >= 2: print(f"DEBUG: Checkbox changed for person {person_id}, face {face_id}: {current_value}") - # Bind the callback to the variable - match_var.trace('w', lambda *args: on_checkbox_change(match_var, matched_id, match['unidentified_id'])) + # Bind the callback to the variable (avoid late-binding by capturing ids) + current_person_id = matched_id + current_face_id = match['unidentified_id'] + match_var.trace('w', lambda *args, var=match_var, person_id=current_person_id, face_id=current_face_id: on_checkbox_change(var, person_id, face_id)) # Configure match frame for grid layout match_frame.columnconfigure(0, weight=0) # Checkbox column - fixed width diff --git a/modify_identified_gui.py b/modify_identified_gui.py index 0a51cbf..5b94b73 100644 --- a/modify_identified_gui.py +++ b/modify_identified_gui.py @@ -1015,6 +1015,26 @@ class ModifyIdentifiedGUI: def on_quit(): nonlocal window_destroyed + # Warn if there are pending unmatched faces (unsaved changes) + try: + if unmatched_faces: + result = messagebox.askyesnocancel( + "Unsaved Changes", + "You have pending changes that are not saved.\n\n" + "Yes: Save and quit\n" + "No: Quit without saving\n" + "Cancel: Return to window" + ) + if result is None: + # Cancel + return + if result is True: + # Save then quit + on_save_all_changes() + # If result is False, fall through and quit without saving + except Exception: + # If any issue occurs, proceed to normal quit + pass on_closing() if not window_destroyed: window_destroyed = True @@ -1049,10 +1069,10 @@ class ModifyIdentifiedGUI: show_person_faces(current_person_id, current_person_name) messagebox.showinfo("Changes Saved", f"Successfully unlinked {count} face(s).") - save_btn_bottom = ttk.Button(control_frame, text="💾 Save changes", command=on_save_all_changes) - save_btn_bottom.pack(side=tk.RIGHT, padx=(0, 10)) quit_btn = ttk.Button(control_frame, text="❌ Quit", command=on_quit) quit_btn.pack(side=tk.RIGHT) + save_btn_bottom = ttk.Button(control_frame, text="💾 Save changes", command=on_save_all_changes) + save_btn_bottom.pack(side=tk.RIGHT, padx=(0, 10)) # Show the window try: