Refactor AutoMatchGUI and ModifyIdentifiedGUI for improved unsaved changes handling
This commit enhances the AutoMatchGUI and ModifyIdentifiedGUI classes by refining the logic for handling unsaved changes when quitting. The AutoMatchGUI now uses a simplified messagebox for unsaved changes, while the ModifyIdentifiedGUI introduces a similar prompt to warn users about pending changes. Additionally, the logic for managing matched IDs has been updated to ensure consistency in face identification. These improvements aim to enhance user experience by providing clearer warnings and preserving user actions more effectively.
This commit is contained in:
parent
ac546a09e0
commit
70cb11adbd
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user