This is certainly related to can-i-change-which-points-are-selected-programmatically, but the solution there is insufficient.
In my application I generate a heatmap indicating the status of many tasks, with metadata in the tooltips. I want users to be able to click the cell and highlight the corresponding row in a Tabulator widget from Panel. All of this works as expected using hv.streams.Selection1D
I also want users to be able to click rows in the table and have the corresponding cells in the heatmap selected - simply the inverse of the above. This what I am struggling to get working. I want to be able to programmatically tell the heatmap what cells have been selected, and get the same alpha applied to the unselected data.
Below is what I have tried so far as MRE, but I am not sure how to get this to work as needed. In addition to what is below, I have also tried updating the selection_stream
, but this does not propagate the selection back to the heatmap, only to the table.
I am hoping there is an easy way to do this, as I feel like I have overcomplicated everything. Any help is much appreciated!
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn
hv.extension('bokeh')
pn.extension('tabulator')
# setup data
dim = 3
bounds=(-0.5, dim-0.5)
xx, yy = np.meshgrid(range(dim), range(dim))
df = pd.DataFrame({'x': xx.flatten(), 'y': yy.flatten(), 'z': np.random.uniform(size=dim*dim)})
# setup table and heatmap
tb = pn.widgets.Tabulator(df, selectable=True, disabled=True)
heatmap = hv.HeatMap(df, kdims=['x', 'y'], vdims=['z']).opts(tools=['tap', 'box_select'], aspect=1, responsive=True, xlim=bounds, ylim=bounds).redim.range(z=(0, 1.0))
# setup selection stream
selection_stream = hv.streams.Selection1D(source=heatmap)
# callback to update the table selection. need to transpose index
def update_table(index):
tb.selection = [(i % dim) * dim + i // dim for i in index]
return tb
# callback to update the heatmap. this is where the problems are
def update_heatmap(index):
# if no index, return entire heatmap
if not index:
return heatmap
# return a slice of the heatmap using the given indices
# problems:
# - need to reset the bounds and z-range
# - rendered cells are too large until their row/column forces them into a normal size
# - unselected cells simply are not shown, since we filtered them out
# - cannot deselect cells
return heatmap.iloc[index].opts(xlim=bounds, ylim=bounds).redim.range(z=(0, 1.0))
# create simple layout with binds
pn.Column(
pn.bind(update_table, selection_stream.param.index),
pn.bind(update_heatmap, tb.param.selection)
).servable()