diff --git a/experiment_pages/experiment/data_collection_ui.py b/experiment_pages/experiment/data_collection_ui.py index ee65890b..b268a18f 100644 --- a/experiment_pages/experiment/data_collection_ui.py +++ b/experiment_pages/experiment/data_collection_ui.py @@ -186,6 +186,10 @@ 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) @@ -207,11 +211,84 @@ 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': + # Set selection and changing_value without triggering inline edit multiple times + self.changing_value = row_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: + 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) + self._inline_editor = entry + + # Function to save when user presses Enter + def save_edit(event=None): + new_val = entry.get() + if new_val.strip() == "": + cleanup_editor() + 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.") + 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]) + + 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("", cleanup_editor) + entry.bind("", cleanup_editor) + + 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): @@ -473,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}") 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