Python callback for HoverTool

Hi all,

I am currently upgrading my bokeh app from 0.12 to the most recent version, and have decided to switch to a server implementation. I have a side panel with lots of controls that allow to tweak the plot, these used to be managed cumbersomely using quite large CustomJS callbacks. I am now rewriting them into python callbacks. For Button or RadioButtonGroup there is the on_click function which allows to do:

UI_item=RadioButtonGroup(...)
def callback_UI_item(active):
    #callback code here
UI_item.on_click(callback_UI_item)

My question is, how do I do the same for a HoverTool? In particular, I would like to do something like this, but it gives me an error (ValueError: expected an instance of type Callback, got <function callback_hover at 0x7fc9aa44c7b8> of type function):

p1=figure(...)
main_dataset=p1.circle(..., source=data_source)

def callback_hover(cb_data):
	print(cb_data.Index)

hovertool=HoverTool(callback=callback_hover, renderers=[main_dataset])
p1.add_tools(hovertool)

TIA

The docstring for HoverTool.callback says: “A callback to run in the browser whenever the input’s value changes.”. The crucial part is “in the browser”, so you have to use a subclass of Callback (most often it’s CustomJS) and you can’t trigger Python functions with it. Well, not directly - you could use e.g. some ColumnDataSource created just for that and use it to notify the backend about the hover.

Thanks. Can you tell me more about your intermediate ColumnDataSource solution? Do you mean to initialise it as empty, fill it with hover data using a CustomJS callback, and then adding a python callback to that source? If yes, I think I got it except for the very last step, how would I add a callback to a ColumnDataSource to trigger on data change?

Yes, you’re correct. And it would be something like data_source.on_change('data', my_callback).

Does the datasource needs to be attached to a renderer?

I tried to implement the above solution but I do not get the server callback when the datasource is updated. The CustomJs callback get triggered and the datasource is updated. This is part of the code I used:

hover_details_data_source = ColumnDataSource(
{
‘indices’:
}
)
hover_details_data_source.on_change(‘data’, on_hover_callback)

code = “”"
console.log('indices: ’ + cb_data.index.indices)
const indices = cb_data.index.indices
if (indices.length){
data_source.data[‘indices’] = cb_data.index.indices
data_source.change.emit();
}
“”"
callback = CustomJS(
args={
‘data_source’: hover_details_data_source
},
code=code
)

hover = plot.select(“hover”)
hover.callback = callback

def on_hover_callback(attr, old, new):
print(‘Here’)

@Gilles please open a brand new question topic, and please make sure to use code formatting so that the code is intelligible (either with the </> icon on the editing toolbar, or triple backtick ``` fences around the code blocks)