Dynamic update of Tabulator style

I would like to be able to specify Tabulator row colors based on cell values containing hex color strings.
I have tried to bind the table to an updating method and apply a watcher but none of the methods have succeeded.

import pandas as pd
import panel as pn

class App():
    def __init__(self):
        self.df = pd.DataFrame({'X':[1,2,3],
                                            'color': ['#000000', '#888888', '#ffffff']})
        self.table = pn.widgets.Tabulator(name ='sdfsd', value=self.df, show_index=False)
        self._init_widget()

    def _set_row_color(self, row):
        return [f"background-color: {row['color']}"] * len(row)

    def _init_widget(self):
        self.table = pn.widgets.Tabulator(value=self.df, show_index=False)

    def _update_style(self, new_df):
        # Triggered by bind, but the new style never takes effect
        self.table.style = new_df.style.apply(self._set_row_color, axis=1)
        return self.table

class WatchingApp(App):

    def _init_widget(self):
        super()._init_widget()
        self.table.param.watch(self._update_style, ['value'], onlychanged=False)

    def _update_style(self, event):
        self.table.style = event.new.style.apply(self._set_row_color, axis=1)
        #MISSING RE-RENDERING OF THE TABLE HERE!

app = App()
pn.Column(WatchingApp().table,
          pn.bind(app._update_style, app.table)).show()
1 Like

You really only have to apply the styling once, and instead of applying it to the DataFrame you should apply it to the Tabulator itself:

import pandas as pd
import panel as pn

class App(pn.viewable.Viewer):

    def __init__(self):
        self.df = pd.DataFrame({'X':[1,2,3], 'color': ['#000000', '#888888', '#ffffff']})
        self.table = pn.widgets.Tabulator(name ='sdfsd', value=self.df, show_index=False)
        self.table.style.apply(self._set_row_color, axis=1)
        
    def __panel__(self):
        return self.table
    
    def _set_row_color(self, row):
        return [f"background-color: {row['color']}"] * len(row)

app = App()

app.show()

Now whenever you update table.value it will automatically apply the styler.

A few other notes:

  1. It’s never necessary to return the same object from a pn.bind callback:
    def _update_style(self, new_df):
        # Triggered by bind, but the new style never takes effect
        self.table.style = new_df.style.apply(self._set_row_color, axis=1)
        return self.table

pn.bind(app._update_style, app.table)

This is pointless since self.table is always the same, while pn.bind callbacks are meant for situations where the output changes each time. If you want to apply some side-effect use one of two patterns.

self.table.param.watch(self._update_style, 'value')

Or if you’re using Parameterized classes you could do:

class App(pn.viewable.Viewer):

    def __init__(self):
        self.df = pd.DataFrame({'X':[1,2,3], 'color': ['#000000', '#888888', '#ffffff']})
        self.table = pn.widgets.Tabulator(name ='sdfsd', value=self.df, show_index=False)
        self.table.style.apply(self._set_row_color, axis=1)
        
    def __panel__(self):
        return self.table
   
    def _set_row_color(self, row):
        return [f"background-color: {row['color']}"] * len(row)

    @pn.depends('table.value', watch=True)
    def _update_style(self):
        self.table.style.[DO SOMETHING]

However in your case neither of these approaches are necessary at all.

1 Like

Hi @ncvangilse

Welcome to the community :+1:

Your code looks #orsome :wink:

Thank you very much for your elaborated answer.
I tried all the approaches, but I could still not make any of them work.

Inserting a print statement in the _set_row_color and _update_style revealed that:

  1. self.table.style.apply(...) only fires at startup

  2. self.table.param.watch(...) fires _update_style, but _set_row_color is never fired (left untouched in Tabulator.style._todo?)

  3. The depends decorator never fires at all (but it’s possible I didnt get the Parameterized class right)

The first example is really the only one you should need. Can you maybe tell me how you are updating the data on the tabulator?

I update the data by double clicking on a field in the color column, enter a new hex value and hit Enter