Skip to content

NicePool

NicePool is a DataFrame-driven plot pool: pre-filter dropdowns, plot-type controls, optional table, named presets, and linked selection between table rows and Plotly points.

NicePool demo

Embed

import pandas as pd
from nicewidgets.nicepool import NicePool, NicePoolConfig

df = pd.DataFrame(
    [
        {'pool_row_id': 'a', 'accept': True, 'channel': 0, 'roi_id': 1, 'velocity_mean': 1.2},
        {'pool_row_id': 'b', 'accept': True, 'channel': 1, 'roi_id': 1, 'velocity_mean': 2.4},
    ]
)

pool = NicePool(
    df,
    config=NicePoolConfig(
        unique_row_id_col='pool_row_id',
        show_table_widget=True,
        enable_config_persistence=False,
        dark_mode=False,
    ),
    on_row_selected=lambda row_id, row: print(row_id, row),
)
pool.build()

DataFrame contract (also in the class docstring):

  • unique unique_row_id_col (default pool_row_id)
  • optional categorical pre-filters (accept, channel, roi_id auto-detected)
  • at least one numeric column for the y-axis

Demo: examples/nicepool/ (also /nicepool in the combined demo).

Configuration

Pass a NicePoolConfig for filters, table visibility, presets, persistence, and initial plot layout. Prefer initial_plot_config for deterministic first paint without reading disk.

API

nicewidgets.nicepool.nice_pool.NicePool

Bases: PlotPoolController

General-purpose DataFrame plotting and selection widget.

NicePool preserves the original plot-pool GUI behavior while exposing a small stable API for host applications and scripts. It renders pre-filter dropdowns, plot-type controls, named presets, an optional data table, and one or more linked Plotly plots.

DataFrame contract
  • df must contain the column named by config.unique_row_id_col (default "pool_row_id") with unique, non-empty string-able values. This column links table rows, plot points, and :meth:select_points_by_row_ids.
  • Categorical pre-filter columns are taken from config.pre_filter_columns when given, otherwise auto-detected from config.auto_pre_filter_columns (accept, channel, roi_id). Missing columns are ignored.
  • At least one numeric column is needed for the y-axis.

See examples/nicepool for a runnable demo built on this contract.

Parameters:

Name Type Description Default
df DataFrame

Source DataFrame satisfying the contract above.

required
config NicePoolConfig | None

Optional NicePool configuration. Defaults to NicePoolConfig().

None
on_row_selected Callable[[str, dict[str, object]], None] | None

Optional callback (row_id, row_dict) -> None invoked when a table row is selected. Overrides config.on_table_row_selected.

None
on_refresh_requested Callable[[], DataFrame] | None

Optional callback () -> pd.DataFrame invoked by the refresh button; its return value replaces the data. Overrides config.on_refresh_requested.

None
Source code in src/nicewidgets/nicepool/nice_pool.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
class NicePool(PlotPoolController):
    """General-purpose DataFrame plotting and selection widget.

    ``NicePool`` preserves the original plot-pool GUI behavior while exposing a
    small stable API for host applications and scripts. It renders pre-filter
    dropdowns, plot-type controls, named presets, an optional data table, and
    one or more linked Plotly plots.

    DataFrame contract:
        - ``df`` must contain the column named by ``config.unique_row_id_col``
          (default ``"pool_row_id"``) with unique, non-empty string-able values.
          This column links table rows, plot points, and
          :meth:`select_points_by_row_ids`.
        - Categorical pre-filter columns are taken from
          ``config.pre_filter_columns`` when given, otherwise auto-detected from
          ``config.auto_pre_filter_columns`` (``accept``, ``channel``,
          ``roi_id``). Missing columns are ignored.
        - At least one numeric column is needed for the y-axis.

    See ``examples/nicepool`` for a runnable demo built on this contract.

    Args:
        df: Source DataFrame satisfying the contract above.
        config: Optional NicePool configuration. Defaults to ``NicePoolConfig()``.
        on_row_selected: Optional callback ``(row_id, row_dict) -> None`` invoked
            when a table row is selected. Overrides ``config.on_table_row_selected``.
        on_refresh_requested: Optional callback ``() -> pd.DataFrame`` invoked by
            the refresh button; its return value replaces the data. Overrides
            ``config.on_refresh_requested``.
    """

    def __init__(
        self,
        df: pd.DataFrame,
        *,
        config: NicePoolConfig | None = None,
        on_row_selected: Callable[[str, dict[str, object]], None] | None = None,
        on_refresh_requested: Callable[[], pd.DataFrame] | None = None,
    ) -> None:
        cfg = config if config is not None else NicePoolConfig()
        pre_filter_columns = resolve_pre_filter_columns(
            [str(column) for column in df.columns],
            explicit_columns=cfg.pre_filter_columns,
            auto_columns=cfg.auto_pre_filter_columns,
        )
        table_callback = on_row_selected if on_row_selected is not None else cfg.on_table_row_selected
        refresh_callback = on_refresh_requested if on_refresh_requested is not None else cfg.on_refresh_requested
        controller_config = PlotPoolConfig(
            pre_filter_columns=list(pre_filter_columns),
            unique_row_id_col=cfg.unique_row_id_col,
            db_type=cfg.db_type,
            app_name=cfg.app_name,
            config_path=cfg.config_path,
            plot_state=cfg.plot_state,
            initial_plot_config=cfg.initial_plot_config,
            on_table_row_selected=table_callback,
            on_refresh_requested=refresh_callback,
            show_save_button=cfg.show_save_button,
            show_selection_feedback=cfg.show_selection_feedback,
            show_table_widget=cfg.show_table_widget,
            enable_config_persistence=cfg.enable_config_persistence,
            dark_mode=cfg.dark_mode,
            enable_plot_presets=cfg.enable_plot_presets,
            plot_preset_path=cfg.plot_preset_path,
        )
        super().__init__(df, config=controller_config)
        self.nicepool_config = cfg
        self.pre_filter_columns = tuple(pre_filter_columns)

    def build(self, parent: ui.element | None = None, *, container: ui.element | None = None) -> ui.element:
        """Build the NicePool UI.

        Args:
            parent: Optional NiceGUI parent element.
            container: Optional legacy container argument.

        Returns:
            Root NiceGUI element containing the widget.
        """
        target = container if container is not None else parent
        if target is None:
            root = ui.column().classes(NICEPOOL_ROOT_CLASSES)
        else:
            with target:
                root = ui.column().classes(NICEPOOL_ROOT_CLASSES)
        super().build(container=root)
        return root

    def relayout_plots(self) -> None:
        """Rebuild Plotly figures after the widget container resizes.

        Returns:
            None.
        """
        super().relayout_plots()

    def set_dataframe(self, df: pd.DataFrame) -> None:
        """Replace the source DataFrame and refresh the widget.

        Args:
            df: New source DataFrame.
        """
        self.update_df(df)

    def set_dark_mode(self, enabled: bool) -> None:
        """Set the Plotly layout theme from a dark-mode flag.

        Args:
            enabled: Whether dark mode is enabled.

        Returns:
            None.
        """
        super().set_dark_mode(enabled)

    def select_points_by_row_ids(
        self,
        row_ids: set[str] | list[str] | tuple[str, ...],
    ) -> None:
        """Programmatically select points matching any of the given row ids.

        Args:
            row_ids: Values from ``unique_row_id_col`` identifying rows to highlight.

        Returns:
            None.
        """
        super().select_points_by_row_ids(row_ids)

build

build(
    parent: element | None = None,
    *,
    container: element | None = None,
) -> ui.element

Build the NicePool UI.

Parameters:

Name Type Description Default
parent element | None

Optional NiceGUI parent element.

None
container element | None

Optional legacy container argument.

None

Returns:

Type Description
element

Root NiceGUI element containing the widget.

Source code in src/nicewidgets/nicepool/nice_pool.py
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def build(self, parent: ui.element | None = None, *, container: ui.element | None = None) -> ui.element:
    """Build the NicePool UI.

    Args:
        parent: Optional NiceGUI parent element.
        container: Optional legacy container argument.

    Returns:
        Root NiceGUI element containing the widget.
    """
    target = container if container is not None else parent
    if target is None:
        root = ui.column().classes(NICEPOOL_ROOT_CLASSES)
    else:
        with target:
            root = ui.column().classes(NICEPOOL_ROOT_CLASSES)
    super().build(container=root)
    return root

relayout_plots

relayout_plots() -> None

Rebuild Plotly figures after the widget container resizes.

Returns:

Type Description
None

None.

Source code in src/nicewidgets/nicepool/nice_pool.py
102
103
104
105
106
107
108
def relayout_plots(self) -> None:
    """Rebuild Plotly figures after the widget container resizes.

    Returns:
        None.
    """
    super().relayout_plots()

set_dataframe

set_dataframe(df: DataFrame) -> None

Replace the source DataFrame and refresh the widget.

Parameters:

Name Type Description Default
df DataFrame

New source DataFrame.

required
Source code in src/nicewidgets/nicepool/nice_pool.py
110
111
112
113
114
115
116
def set_dataframe(self, df: pd.DataFrame) -> None:
    """Replace the source DataFrame and refresh the widget.

    Args:
        df: New source DataFrame.
    """
    self.update_df(df)

set_dark_mode

set_dark_mode(enabled: bool) -> None

Set the Plotly layout theme from a dark-mode flag.

Parameters:

Name Type Description Default
enabled bool

Whether dark mode is enabled.

required

Returns:

Type Description
None

None.

Source code in src/nicewidgets/nicepool/nice_pool.py
118
119
120
121
122
123
124
125
126
127
def set_dark_mode(self, enabled: bool) -> None:
    """Set the Plotly layout theme from a dark-mode flag.

    Args:
        enabled: Whether dark mode is enabled.

    Returns:
        None.
    """
    super().set_dark_mode(enabled)

select_points_by_row_ids

select_points_by_row_ids(
    row_ids: set[str] | list[str] | tuple[str, ...],
) -> None

Programmatically select points matching any of the given row ids.

Parameters:

Name Type Description Default
row_ids set[str] | list[str] | tuple[str, ...]

Values from unique_row_id_col identifying rows to highlight.

required

Returns:

Type Description
None

None.

Source code in src/nicewidgets/nicepool/nice_pool.py
129
130
131
132
133
134
135
136
137
138
139
140
141
def select_points_by_row_ids(
    self,
    row_ids: set[str] | list[str] | tuple[str, ...],
) -> None:
    """Programmatically select points matching any of the given row ids.

    Args:
        row_ids: Values from ``unique_row_id_col`` identifying rows to highlight.

    Returns:
        None.
    """
    super().select_points_by_row_ids(row_ids)

nicewidgets.nicepool.config.NicePoolConfig dataclass

Bases: PlotPoolConfig

Configuration for the public NicePool widget.

Parameters:

Name Type Description Default
pre_filter_columns Sequence[str] | None

Explicit categorical columns to expose as pre-filter controls. Missing columns are ignored by the widget.

None
unique_row_id_col str

Column containing stable row identifiers.

'pool_row_id'
db_type str

Logical dataframe type used to scope optional saved plot configuration.

'default'
app_name str | None

Optional application name for optional configuration persistence.

None
config_path Path | None

Optional explicit configuration path used when persistence is enabled.

None
plot_state PlotState | None

Optional fallback plot state when no startup config applies.

None
initial_plot_config dict[str, Any] | None

Optional inline plot config dict (layout + plot_states). Takes precedence over session persistence when set.

None
on_table_row_selected Callable[[str, dict[str, object]], None] | None

Optional row-selection callback used by the underlying table view.

None
on_refresh_requested Callable[[], DataFrame] | None

Optional callback used by the refresh button.

None
show_save_button bool

Whether to render the save-config button.

False
show_selection_feedback bool

Whether to render the selection feedback row.

False
show_table_widget bool

Whether to render the optional DataFrame table.

False
auto_pre_filter_columns Sequence[str]

Candidate columns used when pre_filter_columns is None.

(lambda: DEFAULT_AUTO_PRE_FILTER_COLUMNS)()
table_font_size_px int | None

Reserved for future table style integration.

None
enable_config_persistence bool

Whether to load/save plot configuration.

False
dark_mode bool

Initial Plotly layout theme for generated figures.

False
enable_plot_presets bool

Whether to show and persist named plot presets.

True
plot_preset_path Path | None

Optional explicit path for named plot presets.

None
Source code in src/nicewidgets/nicepool/config.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@dataclass
class NicePoolConfig(PlotPoolConfig):
    """Configuration for the public ``NicePool`` widget.

    Args:
        pre_filter_columns: Explicit categorical columns to expose as
            pre-filter controls. Missing columns are ignored by the widget.
        unique_row_id_col: Column containing stable row identifiers.
        db_type: Logical dataframe type used to scope optional saved plot
            configuration.
        app_name: Optional application name for optional configuration
            persistence.
        config_path: Optional explicit configuration path used when persistence
            is enabled.
        plot_state: Optional fallback plot state when no startup config applies.
        initial_plot_config: Optional inline plot config dict (layout + plot_states).
            Takes precedence over session persistence when set.
        on_table_row_selected: Optional row-selection callback used by the
            underlying table view.
        on_refresh_requested: Optional callback used by the refresh button.
        show_save_button: Whether to render the save-config button.
        show_selection_feedback: Whether to render the selection feedback row.
        show_table_widget: Whether to render the optional DataFrame table.
        auto_pre_filter_columns: Candidate columns used when
            ``pre_filter_columns`` is ``None``.
        table_font_size_px: Reserved for future table style integration.
        enable_config_persistence: Whether to load/save plot configuration.
        dark_mode: Initial Plotly layout theme for generated figures.
        enable_plot_presets: Whether to show and persist named plot presets.
        plot_preset_path: Optional explicit path for named plot presets.
    """

    pre_filter_columns: Sequence[str] | None = None
    unique_row_id_col: str = "pool_row_id"
    db_type: str = "default"
    app_name: str | None = None
    config_path: Path | None = None
    plot_state: PlotState | None = None
    initial_plot_config: dict[str, Any] | None = None
    on_table_row_selected: Callable[[str, dict[str, object]], None] | None = None
    on_refresh_requested: Callable[[], pd.DataFrame] | None = None
    show_save_button: bool = False
    show_selection_feedback: bool = False
    show_table_widget: bool = False
    auto_pre_filter_columns: Sequence[str] = field(default_factory=lambda: DEFAULT_AUTO_PRE_FILTER_COLUMNS)
    table_font_size_px: int | None = None
    enable_config_persistence: bool = False
    dark_mode: bool = False
    enable_plot_presets: bool = True
    plot_preset_path: Path | None = None