From d7a6ced2734652062bb2ccd7107e88753073cd26 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 17 Jan 2023 11:50:54 +0100 Subject: [PATCH 1/2] Ensure updates to Tabulator formatter or editor updates model --- panel/tests/widgets/test_tables.py | 26 ++++++++++++++++++++++++++ panel/widgets/tables.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/panel/tests/widgets/test_tables.py b/panel/tests/widgets/test_tables.py index 083569a4f5..61599f3318 100644 --- a/panel/tests/widgets/test_tables.py +++ b/panel/tests/widgets/test_tables.py @@ -1936,6 +1936,32 @@ def test_tabulator_styling_empty_dataframe(): } } +def test_tabulator_editor_property_change(dataframe, document, comm): + editor = SelectEditor(options=['A', 'B', 'C']) + table = Tabulator(dataframe, editors={'str': editor}) + model = table.get_root(document, comm) + + model_editor = model.columns[-1].editor + assert isinstance(model_editor, SelectEditor) is not editor + assert isinstance(model_editor, SelectEditor) + assert model_editor.options == editor.options + + editor.options = ['D', 'E'] + model_editor = model.columns[-1].editor + assert model_editor.options == editor.options + +def test_tabulator_formatter_update(dataframe, document, comm): + formatter = NumberFormatter(format='0.0000') + table = Tabulator(dataframe, formatters={'float': formatter}) + model = table.get_root(document, comm) + model_formatter = model.columns[2].formatter + assert model_formatter is not formatter + assert isinstance(model_formatter, NumberFormatter) + assert model_formatter.format == formatter.format + + formatter.format = '0.0' + model_formatter = model.columns[2].formatter + assert model_formatter.format == formatter.format def test_tabulator_pandas_import(): # Checks for: https://github.com/holoviz/panel/issues/4102 diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py index ddd5315ae7..82c4f54f0f 100644 --- a/panel/widgets/tables.py +++ b/panel/widgets/tables.py @@ -13,6 +13,7 @@ import numpy as np import param +from bokeh.model import Model from bokeh.models import ColumnDataSource from bokeh.models.widgets.tables import ( AvgAggregator, CellEditor, CellFormatter, CheckboxEditor, DataCube, @@ -43,7 +44,6 @@ DataFrameType = TypeVar('DataFrameType') from bokeh.document import Document - from bokeh.model import Model from bokeh.models.sources import DataDict from pyviz_comms import Comm @@ -110,6 +110,9 @@ def __init__(self, value=None, **params): self._filters = [] self._index_mapping = {} super().__init__(value=value, **params) + self.param.watch(self._setup_on_change, ['editors', 'formatters']) + self.param.trigger('editors') + self.param.trigger('formatters') @param.depends('value', watch=True, on_init=True) def _compute_renamed_cols(self): @@ -230,6 +233,30 @@ def _get_column_definitions(self, col_names: List[str], df: DataFrameType) -> Li columns.append(column) return columns + def _setup_on_change(self, event: param.parameterized.Event): + old, new = event.old, event.new + for model in (old if isinstance(old, dict) else {}).values(): + if not isinstance(model, (CellEditor, CellFormatter)): + continue + change_fn = self._editor_change if isinstance(model, CellEditor) else self._formatter_change + for prop in (model.properties() - Model.properties()): + try: + model.remove_on_change(prop, change_fn) + except ValueError: + pass + for model in (new if isinstance(new, dict) else {}).values(): + if not isinstance(model, (CellEditor, CellFormatter)): + continue + change_fn = self._editor_change if isinstance(model, CellEditor) else self._formatter_change + for prop in (model.properties() - Model.properties()): + model.on_change(prop, change_fn) + + def _editor_change(self, attr: str, new: Any, old: Any): + self.param.trigger('editors') + + def _formatter_change(self, attr: str, new: Any, old: Any): + self.param.trigger('formatters') + def _update_index_mapping(self): if self._processed is None or isinstance(self._processed, list) and not self._processed: self._index_mapping = {} From f8e6d1907a8e09b59d41bdce2754d744210aa285 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 17 Jan 2023 11:58:59 +0100 Subject: [PATCH 2/2] Optimize column definition update --- panel/models/tabulator.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/panel/models/tabulator.ts b/panel/models/tabulator.ts index 5623eea8e4..3344c1c933 100644 --- a/panel/models/tabulator.ts +++ b/panel/models/tabulator.ts @@ -292,7 +292,12 @@ export class DataTabulatorView extends PanelHTMLBoxView { const p = this.model.properties const {configuration, layout, columns, theme, groupby} = p; - this.on_change([configuration, layout, columns, groupby], debounce(() => this.invalidate_render(), 20, false)) + this.on_change([configuration, layout, groupby], debounce(() => this.invalidate_render(), 20, false)) + + this.on_change([columns], () => { + this.tabulator.setColumns(this.getColumns()) + this.setHidden() + }) this.on_change([theme], () => this.setCSS())