diff --git a/rst/index.rst b/rst/index.rst index 571391ee..8b8f894a 100644 --- a/rst/index.rst +++ b/rst/index.rst @@ -25,8 +25,8 @@ Tables ./main/selections ./main/sort_filter ./main/columnwise_settings - ./main/table_advanced - + ./main/table_fields + ./main/table_view_mode Cooperate with Other Widgets ---------------------------- diff --git a/rst/main/dock_widget.rst b/rst/main/dock_widget.rst index e260dee8..3a6933d2 100644 --- a/rst/main/dock_widget.rst +++ b/rst/main/dock_widget.rst @@ -1,13 +1,21 @@ -================== -Custom Dock widget -================== +======================== +Integrate Custom Widgets +======================== .. contents:: Contents :local: :depth: 2 -Add Dock Widget -=============== +There are several places to integrate your custom widgets to :mod:`tabulous` viewer. + +Dock Widget Area +================ + +Dock widget areas are located outside the central table stack area. Widgets docked in +this area are always visible in the same place no matter which table is activated. + +Add Qt Widgets +-------------- .. code-block:: python @@ -23,10 +31,10 @@ Add Dock Widget Use Magicgui Widget -=================== +------------------- -Basics ------- +Basic usage +^^^^^^^^^^^ .. code-block:: python @@ -38,8 +46,8 @@ Basics viewer.add_dock_widget(f) -Tabulous Types --------------- +:mod:`tabulous` type annotations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: @@ -59,3 +67,56 @@ Tabulous Types return table.apply([np.mean, np.std]) viewer.add_dock_widget(f) + +Table Side Area +=============== + +Every table has a side area that can be used to add table-specific widgets or show +table-specific information. + +Custom widgets +-------------- + +Custom Qt widgets or ``magicgui`` widgets can be added to the side area using +:meth:`add_side_widget` method. + +.. code-block:: python + + table = viewer.tables[0] + table.add_side_widget(widget) + # if you want to give a name to the widget + table.add_side_widget(widget, name="widget name") + +Examples +^^^^^^^^ + +.. code-block:: python + + from magicgui import magicgui + + @magicgui + def func(): + print(table.data.mean()) + + table.add_side_widget(func) + +Undo Stack +---------- + +Undo/redo is implemented for each table. You can see the registered operations in a list +view in the side area. You can open it by pressing ``Ctrl+H``. + +Table Overlay Widget +==================== + +Instead of the side area, you can also add widgets as an overlay over the table. An +overlay widget is similar to the overlay charts in Excel. + +.. code-block:: python + + table = viewer.tables[0] + table.add_overlay_widget(widget) + # if you want to give a label to the widget + table.add_overlay_widget(widget, label="my widget") + # you can give the top-left coordinate of the widget + table.add_overlay_widget(widget, topleft=(5, 5)) diff --git a/rst/main/table_advanced.rst b/rst/main/table_fields.rst similarity index 51% rename from rst/main/table_advanced.rst rename to rst/main/table_fields.rst index 176ae668..f2a70a64 100644 --- a/rst/main/table_advanced.rst +++ b/rst/main/table_fields.rst @@ -1,70 +1,18 @@ -=================== -Working with Tables -=================== +============ +Table Fields +============ .. contents:: Contents :local: - :depth: 2 + :depth: 1 -Side Area -========= +Table operations are very complicated. Providing all the programmatic operations +to interact with table state and data as table methods is confusing. Thus, in +:mod:`tabulous`, these operations are well organized with fields and sub-fields +(For instance, all the methods related to table cells are all in :attr:`cell` +field). -Every table has a side area that can be used to add table-specific widgets or show -table-specific information. - -Custom widgets --------------- - -Custom Qt widgets or ``magicgui`` widgets can be added to the side area using -:meth:`add_side_widget` method. - -.. code-block:: python - - table = viewer.tables[0] - table.add_side_widget(widget) - # if you want to give a name to the widget - table.add_side_widget(widget, name="widget name") - -Examples -^^^^^^^^ - -.. code-block:: python - - from magicgui import magicgui - - @magicgui - def func(): - print(table.data.mean()) - - table.add_side_widget(func) - -Undo Stack ----------- - -Undo/redo is implemented for each table. You can see the registered operations in a list -view in the side area. You can open it by pressing ``Ctrl+H``. - - -Overlay Widget -============== - -Instead of the side area, you can also add widgets as an overlay over the table. An -overlay widget is similar to the overlay charts in Excel. - -.. code-block:: python - - table = viewer.tables[0] - table.add_overlay_widget(widget) - # if you want to give a label to the widget - table.add_overlay_widget(widget, label="my widget") - # you can give the top-left coordinate of the widget - table.add_overlay_widget(widget, topleft=(5, 5)) - -Field Attributes of Tables -========================== - -There are several fields that can be used to interact with table state and data. -Operations via table fields are undoable. +Followings are all the fields that are available in table widgets. ``cell`` field -------------- @@ -242,82 +190,3 @@ Simply delete items if you want to reset the dtype setting. .. code-block:: python del table.dtypes["A"] - - -Use View Modes -============== - -Dual View ---------- - -In dual view mode, table is split into two part and each part can be scrolled -and zoomed independently. This mode is useful to inspect large data. - -Dual view is enabled by setting ``table.view_mode = "horizontal"`` for horizontal -view, and ``table.view_mode = "vertical"`` for vertical one. - -.. code-block:: python - - table = viewer.add_table(data) - table.view_mode = "horizontal" - -To reset dual view, set the property to ``"normal"``. - -.. code-block:: python - - table.view_mode = "normal" - -Dual view can also be turned on by key combo ``Ctrl+K, H`` (horizontal) or -``Ctrl+K, V`` (vertical). Reset it by key combo ``Ctrl+K, N``. - -Popup View ----------- - -In popup view mode, a large popup window appears and the table data is shown -inside it. This mode is useful when you want to focus on seeing or editing one -table, or the table viewer widget is docked in other widget so it is very small. - -Popup view is enabled by setting ``table.view_mode = "popup"`` and can be reset -similar to dual view by ``table.view_mode = "normal"`` - -.. code-block:: python - - table = viewer.add_table(data) - table.view_mode = "popup" - -Dual view can also be turned on by key combo ``Ctrl+K, P``. - -Tile View ---------- - -Tile view is a mode that shows different tables in a same window, while the -structure of table list and tabs are not affected. - -How tiling works -^^^^^^^^^^^^^^^^ - -For instance, if you tiled tables "A" and "B", they will appear in the same -window, but tabs named "A" and "B" still exist in the tab bar. ``viewer.tables[i]`` -also returns the same table as before. When tab "A" or "B" is clicked, the tiled -table with "A" and "B" is shown as ``A|B``. - -You can tile the current table and the table next to it by shortcut ``Ctrl+K, ^``. -You can also programmatically tile tables by calling :meth:`viewer.tables.tile`. - -.. code-block:: python - - viewer.tables.tile([0, 1]) # tile the 0th and 1st tables - viewer.tables.tile([0, 1, 3]) # tile tables at indices [0, 1, 3] - -.. image:: ../fig/tile_tables.png - -Untiling -^^^^^^^^ - -Untiling is also well-defined operation. Let's say tabs "A", "B" and "C" is tiled so -these tabs show tiled view ``A|B|C``. If you untiled "B", "A" and "C" are re-tiled -while "B" returns the original state. Therefore, tabs "A" and "C" shows ``A|C`` and -tab "B" shows ``B``. - -You can untile the current table by shortcut ``Ctrl+K, \``. -You can also programmatically untile tables by calling ``viewer.tables.untile([0, 1, 2])``. diff --git a/rst/main/table_view_mode.rst b/rst/main/table_view_mode.rst new file mode 100644 index 00000000..cf842ed3 --- /dev/null +++ b/rst/main/table_view_mode.rst @@ -0,0 +1,84 @@ +============================== +View Tables in Different Modes +============================== + +.. contents:: Contents + :local: + :depth: 1 + +To efficiently inspect table data, it is very useful to change table view modes. + +Dual View +--------- + +In dual view mode, table is split into two part and each part can be scrolled +and zoomed independently. This mode is useful to inspect large data. + +Dual view is enabled by setting ``table.view_mode = "horizontal"`` for horizontal +view, and ``table.view_mode = "vertical"`` for vertical one. + +.. code-block:: python + + table = viewer.add_table(data) + table.view_mode = "horizontal" + +To reset dual view, set the property to ``"normal"``. + +.. code-block:: python + + table.view_mode = "normal" + +Dual view can also be turned on by key combo ``Ctrl+K, H`` (horizontal) or +``Ctrl+K, V`` (vertical). Reset it by key combo ``Ctrl+K, N``. + +Popup View +---------- + +In popup view mode, a large popup window appears and the table data is shown +inside it. This mode is useful when you want to focus on seeing or editing one +table, or the table viewer widget is docked in other widget so it is very small. + +Popup view is enabled by setting ``table.view_mode = "popup"`` and can be reset +similar to dual view by ``table.view_mode = "normal"`` + +.. code-block:: python + + table = viewer.add_table(data) + table.view_mode = "popup" + +Dual view can also be turned on by key combo ``Ctrl+K, P``. + +Tile View +--------- + +Tile view is a mode that shows different tables in a same window, while the +structure of table list and tabs are not affected. + +How tiling works +^^^^^^^^^^^^^^^^ + +For instance, if you tiled tables "A" and "B", they will appear in the same +window, but tabs named "A" and "B" still exist in the tab bar. ``viewer.tables[i]`` +also returns the same table as before. When tab "A" or "B" is clicked, the tiled +table with "A" and "B" is shown as ``A|B``. + +You can tile the current table and the table next to it by shortcut ``Ctrl+K, ^``. +You can also programmatically tile tables by calling :meth:`viewer.tables.tile`. + +.. code-block:: python + + viewer.tables.tile([0, 1]) # tile the 0th and 1st tables + viewer.tables.tile([0, 1, 3]) # tile tables at indices [0, 1, 3] + +.. image:: ../fig/tile_tables.png + +Untiling +^^^^^^^^ + +Untiling is also well-defined operation. Let's say tabs "A", "B" and "C" is tiled so +these tabs show tiled view ``A|B|C``. If you untiled "B", "A" and "C" are re-tiled +while "B" returns the original state. Therefore, tabs "A" and "C" shows ``A|C`` and +tab "B" shows ``B``. + +You can untile the current table by shortcut ``Ctrl+K, \``. +You can also programmatically untile tables by calling ``viewer.tables.untile([0, 1, 2])``. diff --git a/tabulous/widgets/_component/_header.py b/tabulous/widgets/_component/_header.py index 53ff881a..12ec8cb5 100644 --- a/tabulous/widgets/_component/_header.py +++ b/tabulous/widgets/_component/_header.py @@ -12,7 +12,7 @@ import numpy as np from tabulous.exceptions import TableImmutableError -from ._base import TableComponent +from ._base import Component, TableComponent if TYPE_CHECKING: import pandas as pd @@ -21,6 +21,38 @@ _F = TypeVar("_F", bound=Callable) +class HeaderSectionSpan(Component["_HeaderInterface"]): + def __getitem__(self, index: int) -> int: + header = self.parent._get_header() + return header.sectionSize(index) + + def __setitem__( + self, + index: int | slice | list[int], + span: int | Sequence[int], + ) -> None: + header = self.parent._get_header() + if isinstance(index, (slice, list)): + if isinstance(index, slice): + index = list(range(index.indices(header.count()))) + if isinstance(span, Sequence): + # set span for each section + if len(span) != len(index): + raise ValueError("Size mismatch between destination and spans.") + [header.resizeSection(idx, sp) for idx, sp in zip(index, span)] + else: + [header.resizeSection(idx, span) for idx in index] + else: + header.resizeSection(index, span) + + def resize_to_content(self): + from qtpy.QtWidgets import QHeaderView + + header = self.parent._get_header() + header.resizeSections(QHeaderView.ResizeMode.ResizeToContents) + return None + + class _HeaderInterface(TableComponent): """ Interface to the table {index/columns} header. @@ -157,6 +189,11 @@ def selected(self) -> list[slice]: ] return out + @property + def span(self) -> HeaderSectionSpan: + """Sub-field to interact with section spans.""" + return HeaderSectionSpan(self) + class VerticalHeaderInterface(_HeaderInterface): __doc__ = _HeaderInterface.__doc__.replace("{index/columns}", "index")