This guide explains the form state preservation improvements made to prevent loss of entered data on validation errors.
- ✅ Added inline error labels for all fields (First Name, Last Name, Username, Age, Terms & Conditions)
- ✅ Replaced all
messagebox.showerror()with inline error messages - ✅ Added visual field highlighting (red #EF4444 for errors, green #10B981 for valid)
- ✅ Form data is preserved when validation fails
- ✅ Form remains visible and editable when errors occur
Before:
if not first_name:
tk.messagebox.showerror("Error", "First name is required")
return # Form closes, user loses dataAfter:
if not first_name:
fn_error_label.config(text="First name is required")
first_name_entry.config(highlightbackground="#EF4444", highlightcolor="#EF4444")
if not first_field_with_error:
first_field_with_error = first_name_entry
has_error = True
# Later, if has_error: return (form stays open with data intact)- ✅ Verified existing implementation
- ✅ Already uses inline error labels
- ✅ Preserves credentials through failed login attempts
- ✅ Shows rate limiting and CAPTCHA errors inline
- ✅ Updated to collect all validation errors at once
- ✅ Shows all validation issues together
- ✅ Form data preserved on validation failure
- ✅ Users can fix multiple issues before resubmitting
Before:
if not valid_email:
messagebox.showwarning("Validation Error", msg_email)
return # Returns at first errorAfter:
validation_errors = []
# ... collect ALL errors ...
if validation_errors:
error_message = "Please fix the following issues:\n\n" + "\n".join(validation_errors)
messagebox.showwarning("Validation Error", error_message)
return # Form stays open, shows all errorsNew utility module for managing form state across the application:
from app.ui.form_state_manager import get_form_state_manager
# Create and manage form state
manager = get_form_state_manager()
form_state = manager.create_form("my_form")
# Register fields
manager.register_field("my_form", "email",
widget=email_entry, error_label=email_error)
# Set validation errors
manager.set_field_error("my_form", "email", "Invalid email format")
# Check for errors
if manager.has_errors("my_form"):
return # Don't submit, form stays open
# Get clean data
clean_data = manager.get_form_values("my_form")User Fills Form and Submits
↓
Clear All Previous Errors
↓
Validate ALL Fields (collect all errors)
↓
Display Inline Error Messages
↓
Highlight Problem Fields (red)
↓
Focus First Field with Error
↓
Has Errors?
Yes → Return (Form Stays Open, Data Preserved)
No → Proceed with Submission
- Never Clear the Form: Entry widgets retain their values
- Show Errors Inline: Error messages appear below fields
- Visual Feedback: Red highlighting for errors, green for valid
- Focus Management: Auto-focus first problematic field
- Show All Errors: Collect and display all issues at once
User enters data → Validation fails → Messagebox appears
→ Dialog steals focus → User clicks OK → Form disappears
→ User has to re-enter everything → Frustration! 😞
User enters data → Validation fails → Error shown inline
→ Form stays visible with user's data → User can see what's wrong
→ User fixes errors → Form ready to resubmit → Smooth workflow! 😊
- Open the registration form
- Fill in some fields but leave others empty
- Click "Create Account"
- Expected: All your entered data remains visible, error messages show below each required field
- Result: ✓ Can see exactly what's missing and fix it
- Open the login screen
- Click "Login" without entering credentials
- Expected: Error messages show inline, form stays visible
- Result: ✓ Form doesn't disappear, can see where errors are
- Open Profile → Overview
- Enter invalid email (e.g., "notanemail") or invalid phone (e.g., "123")
- Click "Save Details"
- Expected: Warning shows all validation issues, form visible with data intact
- Result: ✓ Can fix issues and resubmit without re-entering everything
# For each input field, add an error label below it
error_label = tk.Label(parent_frame, text="", font=("Segoe UI", 8),
bg=form_bg, fg="#EF4444")
error_label.pack(anchor="w")def validate_form():
# Clear previous errors
error_label_1.config(text="")
error_label_2.config(text="")
# Get values
value1 = field_1.get().strip()
value2 = field_2.get().strip()
# Collect errors
has_error = False
first_error_field = None
if not value1:
error_label_1.config(text="Field 1 is required")
field_1.config(highlightbackground="#EF4444", highlightcolor="#EF4444")
first_error_field = field_1
has_error = True
if not value2:
error_label_2.config(text="Field 2 is required")
field_2.config(highlightbackground="#EF4444", highlightcolor="#EF4444")
if not first_error_field:
first_error_field = field_2
has_error = True
# If errors, focus first and return
if has_error:
if first_error_field:
first_error_field.focus_set()
return # Form stays open, data preserved!
# All valid - proceed with submission
process_form_data(value1, value2)submit_btn = tk.Button(form, text="Submit", command=validate_form)
submit_btn.pack()| State | Color | Hex Code | Meaning |
|---|---|---|---|
| Error | Red | #EF4444 | Field has validation error |
| Valid | Green | #10B981 | Field passes validation |
| Neutral | Gray | #E2E8F0 | Not yet validated |
- "Email is required"
- "Email must be valid (e.g., user@example.com)"
- "Age must be between 13 and 120"
- "This password is too common. Try adding numbers or symbols."
- "Error" (too vague)
- "INVALID_EMAIL" (too technical)
- "Fix the form" (not actionable)
Some fields support real-time validation (show errors as you type):
# Bind to KeyRelease event for real-time validation
entry_field.bind("<KeyRelease>", validate_email_realtime)
def validate_email_realtime(event=None):
email = entry_field.get()
if validate(email):
error_label.config(text="")
entry_field.config(highlightbackground="#10B981", highlightcolor="#10B981")
else:
error_label.config(text="Invalid email format")
entry_field.config(highlightbackground="#EF4444", highlightcolor="#EF4444")Q: Why not use messagebox anymore? A: Messagebox dialogs steal focus, hide the form, and interrupt user workflow. Inline errors keep the form visible and let users see context.
Q: Will this slow down form submission? A: No! The validation happens on the client-side instantly. There's no performance impact.
Q: Can users still lose data by closing the window? A: Yes, but that's expected behavior. This implementation prevents data loss due to validation errors only.
Q: How do I show success feedback? A: Use a success dialog after successful submission, or show an inline success message that auto-dismisses.
app/ui/form_state_manager.py- NEW: Form state management utilityapp/auth/app_auth.py- Updated registration form validationapp/ui/profile.py- Updated profile form validationdocs/FORM_STATE_PRESERVATION.md- Documentationdocs/FORM_STATE_PRESERVATION_IMPLEMENTATION.md- Implementation details
- Form State Manager: See
app/ui/form_state_manager.pyfor API documentation - Registration Example: See
app/auth/app_auth.py-show_signup_screen()method - Login Example: See
app/auth/app_auth.py-show_login_screen()method - Profile Example: See
app/ui/profile.py-save_personal_data()method
- Review the examples in the code
- Test the registration and login forms
- Apply the pattern to other forms in your app
- Use the FormStateManager utility for complex forms
Summary: Form data is now preserved when validation fails. Users will see clear, inline error messages and can fix issues without losing their input. This significantly improves the user experience! 🎉