Does anyone know if it is possible to embed a widget within a Tabulator cell? I have a list of colors that are associated with rows in a table and I would like to be able to update them by clicking on a cell in the respective row. I can get what I need with a column of ColorPicker widgets, but if I could embed those widgets within a table then the layout would look a lot cleaner.
Here is an example of what I am trying to achieve. I can set the background colors according to a reference list and set a callback that is triggered when the cell is clicked, but when the cell is clicked I would like to open a ColorPicker widget so that I can change the cell color. Here is the example code below.
import panel as pn
import pandas as pd
pn.extension('tabulator')
# Populating example df with blank 'color' column
data = pd.DataFrame({'A': [1.69052,0.00206,-0.62542,-1.45324],
'B':[-0.46593,-0.00089,-0.17154,0.55458],
'color':[None,None,None,None]})
# Reference list used to set background colors
color_list = ['#7579F0','#75F07D','#F07575','#E175F0']
# Cell styling to set background color
def background_color(col):
if col.name == 'color':
background_color = color_list
else:
background_color = ['']*len(col)
return [f'background-color: {c}' for c in background_color]
# Cell selection callback
def set_color(event):
### Open ColorPicker here to update both color_list and table values ###
print(f'Clicked cell in row {event.row!r} with value {color_list[event.row]}')
# Build Tabulator widget
table = pn.widgets.Tabulator(
data,
editors = {'color':None}
)
table.style.apply(background_color)
table.on_click(set_color,column='color')
table
Does anyone know if what I am trying to do is possible or have an alternative solution? I appreciate any help that I can get.
import panel as pn
import pandas as pd
pn.extension("tabulator")
# Populating example df with initial 'color' column values set to None
data = pd.DataFrame(
{
"A": [1.69052, 0.00206, -0.62542, -1.45324],
"B": [-0.46593, -0.00089, -0.17154, 0.55458],
"color": [None, None, None, None],
}
)
COLOR_LIST = [
"#7579F0",
"#75F07D",
"#F07575",
"#E175F0",
]
def color_to_html(color):
return f"<div style='{color}:white;background:{color};width:4em;height:16px;border-radius:4px;'></div>"
# Dictionary of color names mapped to hex values
color_dict = {color_to_html(color): color for color in COLOR_LIST}
color_dict_reversed = {v: k for k, v in color_dict.items()}
# Cell styling to set background color dynamically
def background_color(col):
if col.name == "color":
result = [
f"color:{c};background-color: {c}"
if c
else "color:white;background-color:white"
for c in col
]
return result
return ["color:white;background-color:white"] * len(col)
# Create Tabulator widget with a dropdown color selector for 'color' column
table = pn.widgets.Tabulator(
data,
editors={"color": {"type": "list", "values": color_dict_reversed}},
)
table.style.apply(background_color)
pn.panel("# Select Color in Table").servable()
table.servable()
Fun fact: I solved this by pointing Chat GPT 4.5 to this discourse page, to the tabulator documentation Tabulator — Panel v1.6.2 and then asked it to solve your problem. To add the styling I did some manual work.
Thank you Marc, this is pretty helpful and very close to what I am trying to achieve. My only problem here is that the available colors are limited to a list, while something like a ColorPicker gives you practically all color options within a well confined area. I think I could supply a list from a color palette and cover the options pretty well, but my concern comes with trying to select unique colors when I have 20+ rows. Although this would probably be an issue either way as it would be difficult to come up with 20+ visually distinct colors regardless. Thanks for this solution.