Skip to content

Commit

Permalink
update partial interface
Browse files Browse the repository at this point in the history
  • Loading branch information
hanjinliu committed Jan 9, 2023
1 parent ccfa5d3 commit d88e3bd
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 11 deletions.
24 changes: 24 additions & 0 deletions rst/main/columnwise_settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,27 @@ set validator functions appropriate for the data types.
You can also set dtypes from GUI. Right-click the column header and select
:menu:`Column dtype`.

Use Syntax of Table Subset
==========================

A :class:`pd.Series`-like table subset, :class:`TableSeries` can be obtained by
indexing a table.

.. code-block:: python
sheet = viewer.open_sample("iris")
sheet["sepal_length"]
.. code-block::
<TableSeries<'sepal_length'> of SpreadSheet<'sheet'>>
:class:`TableSeries` also as fields such as :attr:`text_color` and :attr:`formatter`
and they can be considered as partial table fields.

.. code-block:: python
print(sheet["sepal_length"].formatter) # get formatter function
sheet["sepal_length"].formatter = f"{:2f} cm" # set formatter
del sheet["sepal_length"].formatter # reset formatter
2 changes: 2 additions & 0 deletions rst/main/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ then you'll get following :class:`DataFrame`.
A int64
B object
Rows and columns can be inserted or removed in the right-click contextmenu.

A spreadsheet can be added to the viewer by :meth:`add_spreadsheet` method.

.. code-block:: python
Expand Down
4 changes: 2 additions & 2 deletions tabulous/widgets/_component/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __get__(self, obj: Literal[None], owner=None) -> Self[_NoRef]:
...

@overload
def __get__(self, obj: T, owner=None) -> Self[T]:
def __get__(self, obj: Any, owner=None) -> Self[T]:
...

def __get__(self, obj, owner=None):
Expand All @@ -60,7 +60,7 @@ def __get__(self, obj, owner=None):
out = self._instances[_id] = self.__class__(obj)
return out

def __set__(self, obj: T, value: Any) -> None:
def __set__(self, obj: Any, value: Any) -> None:
if obj is None:
raise AttributeError("Cannot set attribute.")
_id = id(obj)
Expand Down
9 changes: 8 additions & 1 deletion tabulous/widgets/_component/_column_setting.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

_DtypeLike = Union[ExtensionDtype, np.dtype]

from typing_extensions import TypeGuard
from typing_extensions import TypeGuard, Self
import pandas as pd

_Formatter = Union[Callable[[Any], str], str, None]
Expand Down Expand Up @@ -81,12 +81,19 @@ def __iter__(self) -> Iterator[str]:
return iter(self._get_dict())

def set(self, column_name: str, func: _F = _Void):
"""Set function to given column name."""

def _wrapper(f: _F) -> _F:
self._set_value(column_name, f)
return f

return _wrapper(func) if func is not _Void else _wrapper

def reset(self, column_name) -> Self:
"""Reset function for the given column name."""
self._set_value(column_name, None)
return self

def __call__(self, *args, **kwargs):
# backwards compatibility
return self.set(*args, **kwargs)
Expand Down
70 changes: 65 additions & 5 deletions tabulous/widgets/_component/_table_subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,32 +143,89 @@ def __init__(self, parent: TableBase, row_slice: slice | list[int], column: str)
self._row_slice = row_slice
self._column = column

def __repr__(self) -> str:
return f"<TableSeries<{self._column!r}> of {self.parent!r}>"

@property
def data(self) -> pd.Series:
table = self.parent
return table.native._get_sub_frame(self._column).iloc[self._row_slice]

@property
def text_color(self):
self._assert_row_not_slices()
"""Get the text colormap of the column."""
self._assert_row_not_sliced()
return PartialTextColormapInterface(self.parent, self._column)

@text_color.setter
def text_color(self, val) -> None:
"""Set the text colormap of the column."""
self._assert_row_not_sliced()
self.parent.text_color[self._column] = val
return None

@text_color.deleter
def text_color(self) -> None:
"""Delete the text colormap of the column."""
self._assert_row_not_sliced()
del self.parent.text_color[self._column]
return None

@property
def background_color(self):
self._assert_row_not_slices()
"""Get the background colormap of the column."""
self._assert_row_not_sliced()
return PartialBackgroundColormapInterface(self.parent, self._column)

@background_color.setter
def background_color(self, val):
"""Set the background colormap of the column."""
self._assert_row_not_sliced()
self.parent.background_color[self._column] = val
return None

@background_color.deleter
def background_color(self):
"""Delete the background colormap of the column."""
self._assert_row_not_sliced()
del self.parent.background_color[self._column]
return None

@property
def formatter(self):
self._assert_row_not_slices()
self._assert_row_not_sliced()
return PartialTextFormatterInterface(self.parent, self._column)

@formatter.setter
def formatter(self, val):
self._assert_row_not_sliced()
self.parent.formatter[self._column] = val
return None

@formatter.deleter
def formatter(self):
self._assert_row_not_sliced()
del self.parent.formatter[self._column]
return None

@property
def validator(self):
self._assert_row_not_slices()
self._assert_row_not_sliced()
return PartialValidatorInterface(self.parent, self._column)

def _assert_row_not_slices(self):
@validator.setter
def validator(self, val):
self._assert_row_not_sliced()
self.parent.validator[self._column] = val
return None

@validator.deleter
def validator(self):
self._assert_row_not_sliced()
del self.parent.validator[self._column]
return None

def _assert_row_not_sliced(self):
if self._row_slice == slice(None):
return
raise ValueError(f"{self!r} is sliced in row axis.")
Expand Down Expand Up @@ -196,6 +253,9 @@ def __repr__(self) -> str:
def set(self, func: _F = _Void):
return self._get_field().set(self._column, func)

def reset(self):
return self._get_field().reset(self._column)

def item(self):
return self._get_field().get(self._column, None)

Expand Down
21 changes: 18 additions & 3 deletions tests/test_table_subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def test_partial_text_color():
assert table["B"].text_color.item() is table.text_color["B"]
assert table.cell.text_color[0, 1].equals("red")

table["B"].text_color.reset()
assert table["B"].text_color.item() is None

def test_partial_background_color():
table = Table(DATA)

Expand All @@ -68,6 +71,9 @@ def test_partial_background_color():
assert table["B"].background_color.item() is table.background_color["B"]
assert table.cell.background_color[0, 1].equals("red")

table["B"].background_color.reset()
assert table["B"].background_color.item() is None

def test_partial_formatter():
table = Table(DATA)

Expand All @@ -76,9 +82,18 @@ def test_partial_formatter():
assert table["B"].formatter.item() is not None
assert table.cell.text[0, 1] == "test"

table["B"].formatter.reset()
assert table["B"].formatter.item() is None

def test_partial_validator():
table = Table(DATA)
table = Table(DATA, editable=True)

table["B"].validator.set(lambda x: False)
def _raise(x):
raise ValueError

table["B"].validator.set(_raise)
with pytest.raises(ValueError):
table.cell[0, 1] = "test"
table.cell[0, 1] = "6"

table["B"].validator.reset()
table.cell[0, 1] = "6"

0 comments on commit d88e3bd

Please sign in to comment.