diff --git a/fiftyone/operators/types.py b/fiftyone/operators/types.py index a7c06620e3..1539a45d27 100644 --- a/fiftyone/operators/types.py +++ b/fiftyone/operators/types.py @@ -1828,16 +1828,18 @@ class Action(View): on_click: the operator to execute when the action is clicked """ - def __init__(self, **kwargs): + def __init__(self, name, **kwargs): super().__init__(**kwargs) + self.name = name def clone(self): - clone = Action(**self._kwargs) + clone = Action(self.name, **self._kwargs) return clone def to_json(self): - return {**super().to_json()} - + return {**super().to_json(), "name": self.name} + + class Tooltip(View): """A tooltip (currently supported only in a :class:`TableView`). @@ -1847,15 +1849,17 @@ class Tooltip(View): column: the column of the tooltip """ - def __init__(self, **kwargs): + def __init__(self, row, column, **kwargs): super().__init__(**kwargs) + self.row = row + self.column = column def clone(self): - clone = Tooltip(**self._kwargs) + clone = Tooltip(self.row, self.column, **self._kwargs) return clone def to_json(self): - return {**super().to_json()} + return {**super().to_json(), "row": self.row, "column": self.column} class TableView(View): @@ -1871,11 +1875,16 @@ def __init__(self, **kwargs): self.columns = kwargs.get("columns", []) self.row_actions = kwargs.get("row_actions", []) self.tooltips = kwargs.get("tooltips", []) + self._tooltip_map = {} def keys(self): return [column.key for column in self.columns] def add_column(self, key, **kwargs): + for column in self.columns: + if column.key == key: + raise ValueError(f"Column with key '{key}' already exists") + column = Column(key, **kwargs) self.columns.append(column) return column @@ -1883,6 +1892,10 @@ def add_column(self, key, **kwargs): def add_row_action( self, name, on_click, label=None, icon=None, tooltip=None, **kwargs ): + for action in self.row_actions: + if action.name == name: + raise ValueError(f"Action with name '{name}' already exists") + row_action = Action( name=name, on_click=on_click, @@ -1893,10 +1906,16 @@ def add_row_action( ) self.row_actions.append(row_action) return row_action - + def add_tooltip(self, row, column, value, **kwargs): + if (row, column) in self._tooltip_map: + raise ValueError( + f"Tooltip for row '{row}' and column '{column}' already exists" + ) + tooltip = Tooltip(row=row, column=column, value=value, **kwargs) self.tooltips.append(tooltip) + self._tooltip_map[(row, column)] = tooltip return tooltip def clone(self): @@ -1904,6 +1923,10 @@ def clone(self): clone.columns = [column.clone() for column in self.columns] clone.row_actions = [action.clone() for action in self.row_actions] clone.tooltips = [tooltip.clone() for tooltip in self.tooltips] + clone._tooltip_map = { + (tooltip.row, tooltip.column): tooltip + for tooltip in clone.tooltips + } return clone def to_json(self): diff --git a/tests/unittests/operators/tableview_tests.py b/tests/unittests/operators/tableview_tests.py new file mode 100644 index 0000000000..88945ec92c --- /dev/null +++ b/tests/unittests/operators/tableview_tests.py @@ -0,0 +1,46 @@ +import unittest + +from fiftyone.operators.types import TableView + + +class TableViewTests(unittest.TestCase): + def test_table_view_basic(self): + table = TableView() + table.add_column("column1", label="Column 1") + table.add_column("column2", label="Column 2") + assert table.keys() == ["column1", "column2"] + + with self.assertRaises(ValueError): + table.add_column("column1", label="Column 3") + + mock_on_click = lambda: None + + table.add_row_action( + "action1", + on_click=mock_on_click, + icon="icon1", + color="primary", + tooltip="Action 1", + ) + table.add_row_action( + "action2", + on_click=mock_on_click, + icon="icon2", + color="secondary", + tooltip="Action 2", + ) + + with self.assertRaises(ValueError): + table.add_row_action( + "action1", + on_click=mock_on_click, + icon="icon3", + color="primary", + tooltip="Action 3", + ) + + table.add_tooltip(1, 1, "Tooltip 1") + table.add_tooltip(1, 2, "Tooltip 2") + + with self.assertRaises(ValueError): + table.add_tooltip(1, 1, "Tooltip 3")