From a93d56c7a3914d719c75ffc085ed470853662071 Mon Sep 17 00:00:00 2001 From: Ansel P Date: Thu, 19 Mar 2026 14:35:29 -0500 Subject: [PATCH 1/3] Fix inline editing for None values in data collection table --- .../experiment/data_collection_ui.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/experiment_pages/experiment/data_collection_ui.py b/experiment_pages/experiment/data_collection_ui.py index ee65890b..7d0a4b63 100644 --- a/experiment_pages/experiment/data_collection_ui.py +++ b/experiment_pages/experiment/data_collection_ui.py @@ -186,6 +186,7 @@ def __init__(self, parent: CTk, prev_page: CTkFrame = None, database_name = "", self.table.bind('<>', self.item_selected) self.table.bind("", self._open_selected_for_edit) + self.table.bind("<1>", self._on_table_click) self.changer = ChangeMeasurementsDialog(parent, self, self.measurement_strings) @@ -207,11 +208,69 @@ def item_selected(self, _): if selected: self.changing_value = selected[0] + def _on_table_click(self, event): + """Handle click for inline edit on None values.""" + region = self.table.identify("region", event.x, event.y) + if region == "cell": + column_id = self.table.identify_column(event.x) + row_id = self.table.identify_row(event.y) + if column_id == '#2': + val = self.table.item(row_id, "values") + if len(val) > 1 and str(val[1]) == 'None': + self.table.selection_set(row_id) + self.changing_value = row_id + self._enter_inline_edit(row_id, column_id) + return "break" + + def _enter_inline_edit(self, row_id, column_id): + # Get bounding box of the cell + bbox = self.table.bbox(row_id, column_id) + if not bbox: + return + x, y, width, height = bbox + + # Define entry widget + entry = CTkEntry(self.table, width=width, height=height, font=("Arial", self._ui.get("table_font_size", 14))) + entry.place(x=x, y=y) + + # Function to save when user presses Enter + def save_edit(event=None): + new_val = entry.get() + if new_val.strip() == "": + entry.destroy() + return + + try: + # Ensure it is a valid float as expected + _ = float(new_val) + except ValueError: + self.raise_warning("Invalid input. Please enter a valid number.") + entry.destroy() + return + + # Save the value + animal_id = self.table.item(row_id, "values")[0] + self.change_selected_value(animal_id, [new_val]) + entry.destroy() + + def on_focus_out(event): + entry.destroy() + + entry.bind("", save_edit) + entry.bind("", on_focus_out) + entry.bind("", on_focus_out) + + entry.focus_set() + def _open_selected_for_edit(self, _): """Open the edit dialog for the currently selected row.""" selected = self.table.selection() if selected: self.changing_value = selected[0] + val = self.table.item(self.changing_value, "values") + if len(val) > 1 and str(val[1]) == 'None': + # Rely on single-click to open inline editor + return self.open_changer() def set_status(self, text): From 24c64374824b1cf09b28c7815c073e909c3d6085 Mon Sep 17 00:00:00 2001 From: Ansel P Date: Mon, 30 Mar 2026 12:26:39 -0500 Subject: [PATCH 2/3] Apply formatting and auto-edits to data_collection_ui.py --- .../experiment/data_collection_ui.py | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/experiment_pages/experiment/data_collection_ui.py b/experiment_pages/experiment/data_collection_ui.py index 7d0a4b63..b268a18f 100644 --- a/experiment_pages/experiment/data_collection_ui.py +++ b/experiment_pages/experiment/data_collection_ui.py @@ -187,6 +187,9 @@ def __init__(self, parent: CTk, prev_page: CTkFrame = None, database_name = "", self.table.bind('<>', self.item_selected) self.table.bind("", self._open_selected_for_edit) self.table.bind("<1>", self._on_table_click) + + # Initialize inline editor tracking + self._inline_editor = None self.changer = ChangeMeasurementsDialog(parent, self, self.measurement_strings) @@ -217,12 +220,17 @@ def _on_table_click(self, event): if column_id == '#2': val = self.table.item(row_id, "values") if len(val) > 1 and str(val[1]) == 'None': - self.table.selection_set(row_id) + # Set selection and changing_value without triggering inline edit multiple times self.changing_value = row_id - self._enter_inline_edit(row_id, column_id) + # Use after_idle to ensure table updates complete before opening inline edit + self.table.after_idle(lambda: self._enter_inline_edit(row_id, column_id)) return "break" def _enter_inline_edit(self, row_id, column_id): + # Prevent multiple inline editors from opening + if hasattr(self, '_inline_editor') and self._inline_editor and self._inline_editor.winfo_exists(): + return + # Get bounding box of the cell bbox = self.table.bbox(row_id, column_id) if not bbox: @@ -232,12 +240,13 @@ def _enter_inline_edit(self, row_id, column_id): # Define entry widget entry = CTkEntry(self.table, width=width, height=height, font=("Arial", self._ui.get("table_font_size", 14))) entry.place(x=x, y=y) + self._inline_editor = entry # Function to save when user presses Enter def save_edit(event=None): new_val = entry.get() if new_val.strip() == "": - entry.destroy() + cleanup_editor() return try: @@ -245,20 +254,29 @@ def save_edit(event=None): _ = float(new_val) except ValueError: self.raise_warning("Invalid input. Please enter a valid number.") - entry.destroy() + cleanup_editor() return # Save the value animal_id = self.table.item(row_id, "values")[0] + + # Clean up editor first to prevent visual issues + cleanup_editor() + + # Then update the value (which will update both DB and table display) self.change_selected_value(animal_id, [new_val]) - entry.destroy() - def on_focus_out(event): - entry.destroy() + def cleanup_editor(event=None): + if hasattr(self, '_inline_editor') and self._inline_editor: + try: + self._inline_editor.destroy() + except: + pass + self._inline_editor = None entry.bind("", save_edit) - entry.bind("", on_focus_out) - entry.bind("", on_focus_out) + entry.bind("", cleanup_editor) + entry.bind("", cleanup_editor) entry.focus_set() @@ -532,10 +550,20 @@ def change_selected_value(self, animal_id_to_change, list_of_values): # Update display table try: + updated = False for child in self.table.get_children(): - if animal_id_to_change == self.table.item(child)["values"][0]: + table_animal_id = self.table.item(child)["values"][0] + # Handle type conversions for comparison + if str(animal_id_to_change) == str(table_animal_id): self.table.item(child, values=(animal_id_to_change, new_value)) - print("Table display updated") + updated = True + # Force table to refresh/redraw + self.table.update_idletasks() + break + if updated: + print(f"Table display updated for animal {animal_id_to_change}") + else: + print(f"Warning: Could not find animal {animal_id_to_change} in table") except Exception as table_error: print(f"Error updating table display: {table_error}") From 8f065226189dd524e0085c6496b3db5924ff66ef Mon Sep 17 00:00:00 2001 From: Ansel P Date: Mon, 30 Mar 2026 12:30:28 -0500 Subject: [PATCH 3/3] requirements.txt addition --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0c1a4c68..73b1ca3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,5 +9,5 @@ cryptography customtkinter CTkMessagebox CTkMenuBar -git+https://github.com/taconi/playsound.git +playsound==1.2.2 requests