From 1fa9f9cd0197b838bb9e5ecd522535892dd82bc4 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Sun, 10 Aug 2025 09:39:18 -0700 Subject: [PATCH] refactor: Improve dose entry handling in UIManager for better synchronization and user experience --- src/ui_manager.py | 130 +++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/src/ui_manager.py b/src/ui_manager.py index 9a1bf22..3649354 100644 --- a/src/ui_manager.py +++ b/src/ui_manager.py @@ -1,3 +1,4 @@ +import contextlib import logging import os import sys @@ -1446,60 +1447,10 @@ class UIManager: quick_frame = ttk.Frame(entry_frame) quick_frame.grid(row=0, column=1, padx=10, pady=5, sticky="w") - # Create the dose StringVar that will be used for saving - dose_string_var = tk.StringVar(value=str(dose_str)) + # Create the dose StringVar; we'll keep it in sync with the text widget + dose_string_var = tk.StringVar(value="") vars_dict[f"{medicine_key}_doses"] = dose_string_var - # Punch button - updated to use the StringVar properly - def create_punch_callback(med_key, entry_var, dose_var): - def punch_dose(): - dose = entry_var.get().strip() - if dose: - from datetime import datetime - - # Format timestamp for display (12-hour format with AM/PM) - timestamp = datetime.now().strftime("%I:%M %p") - new_dose = f"• {timestamp} - {dose}" - - current_doses = dose_var.get() - if current_doses and current_doses.strip(): - # Check if current content is placeholder text - if "No doses recorded" in current_doses: - dose_var.set(new_dose) - else: - dose_var.set(current_doses + f"\n{new_dose}") - else: - dose_var.set(new_dose) - - entry_var.set("") - - return punch_dose - - punch_btn = ttk.Button( - quick_frame, - text=f"Take {medicine.display_name}", - command=create_punch_callback( - medicine_key, dose_entry_var, dose_string_var - ), - width=15, - ) - punch_btn.grid(row=0, column=0, padx=5) - - # Quick dose buttons - quick_doses = self.medicine_manager.get_quick_doses(medicine_key) - for i, dose in enumerate(quick_doses[:3]): # Limit to 3 quick doses - - def create_quick_callback(d, entry_var=dose_entry_var): - return lambda: entry_var.set(d) - - btn = ttk.Button( - quick_frame, - text=f"{dose}mg", - command=create_quick_callback(dose), - width=8, - ) - btn.grid(row=0, column=i + 1, padx=2) - # Dose history section history_frame = ttk.LabelFrame( tab_frame, text="Dose History (HH:MM: dose)", padding="10" @@ -1521,6 +1472,12 @@ class UIManager: # Populate with existing doses using the proper formatting method self._populate_dose_history(dose_text, dose_str) + # Initialize the StringVar from the displayed content for consistency + try: + current_display = dose_text.get("1.0", tk.END).strip() + dose_string_var.set(current_display) + except Exception: + dose_string_var.set("") # Bind text widget to update string var - fixed closure issue def create_update_callback(text_widget, dose_var): @@ -1534,21 +1491,66 @@ class UIManager: dose_text.bind("", update_callback) dose_text.bind("", update_callback) - # Also update text widget when StringVar changes (for punch button) - def create_var_to_text_callback(text_widget, string_var): - def update_text_from_var(*args): - current_text = text_widget.get("1.0", tk.END).strip() - var_content = string_var.get() - if current_text != var_content: - text_widget.delete("1.0", tk.END) - text_widget.insert("1.0", var_content) + # Do not mirror StringVar back to Text automatically to avoid overwriting + # user edits or formatted history; we keep var in sync from Text only. - return update_text_from_var + # Punch button - append to the Text widget then sync the StringVar + def create_punch_callback(med_key, entry_var, text_widget, dose_var): + def punch_dose(): + dose = entry_var.get().strip() + if not dose: + return + from datetime import datetime - var_to_text_callback = create_var_to_text_callback( - dose_text, dose_string_var + # Format timestamp for display (12-hour format with AM/PM) + timestamp = datetime.now().strftime("%I:%M %p") + new_dose_line = f"• {timestamp} - {dose}" + + # Ensure widget is editable and read current content + with contextlib.suppress(Exception): + text_widget.configure(state="normal") + current = text_widget.get("1.0", tk.END).strip() + + # Replace placeholder or append + if not current or current == "No doses recorded today": + updated = new_dose_line + else: + updated = current + "\n" + new_dose_line + + # Write back to the widget and sync the StringVar + text_widget.delete("1.0", tk.END) + text_widget.insert("1.0", updated) + dose_var.set(updated) + + # Clear the quick entry + entry_var.set("") + + return punch_dose + + punch_btn = ttk.Button( + quick_frame, + text=f"Take {medicine.display_name}", + command=create_punch_callback( + medicine_key, dose_entry_var, dose_text, dose_string_var + ), + width=15, ) - dose_string_var.trace("w", var_to_text_callback) + punch_btn.grid(row=0, column=0, padx=5) + + # Quick dose buttons + quick_doses = self.medicine_manager.get_quick_doses(medicine_key) + for i, dose in enumerate(quick_doses[:3]): # Limit to 3 quick doses + + def create_quick_callback(d, entry_var=dose_entry_var): + return lambda: entry_var.set(d) + + btn = ttk.Button( + quick_frame, + text=f"{dose}mg", + command=create_quick_callback(dose), + width=8, + ) + btn.grid(row=0, column=i + 1, padx=2) # Scrollbar for dose text dose_scroll = ttk.Scrollbar(