Can multiple DataTables share common callback (and tell which DT called them?)

Hello,
I’m totally new to Bokeh. It’s quite an impressive tool! I searched and couldn’t answer my question, and I apologize in advance if I’ve missed the answer elsewhere.

I’d like to use a Bokeh server to show multiple DataTables, laid out on the same page, each DataTable showing a different subset of an underlying data model. I won’t know in advance how many of these DataTables I will need, so I’d like for each DT to share a common python callback that could determine which specific DataTable had triggered the callback, and then process the data accordingly.

But it appears that the “on_change(attr, old, new)” callback signature doesn’t provide any way for me to determine which DataTable triggered the callback. That makes it hard for me to figure which part of my underlying model has been updated.

Did I miss some obvious way to do this? I looked at the identifiers “name” and “tags” but couldn’t find a way to access those parameters from within the on_change callback.

Thanks!

Well, right after asking the question, I figured out a hack that sort of works: I can create a “hidden” column of data that is not displayed in the DataTable. Then when the on_change event occurs, I can dispatch off of the value of this hidden column.

This seems to work, but feels like a work-around. Did I miss a better way to solve for this?

1 Like

I don’t think there’s a better way to do what you describe. Since the callback is going to be triggered by a data change on your data source, and not triggered by the DataTables themselves, the information about which widget was used to make the change does not seem to have a way to persist.

While I’m not sure of the details of how you’re using your hidden columns, I could see achieving this by comparing columns of the updated data source to reference copies (your ‘hidden columns’) and looking for changes… but this seems like it would be a scaling problem if your data got very large at all, so I suppose it depends on your use case.

Is it at ALL possible to split the data source? e.g. given lists x, y, and z, could you have a CDS with x and y, and one with x and z?

Carolyn, thanks for the reply.

After experimenting, I came up with another alternative - using closures and a factory function. I think I can create a callback that accepts the expected arguments but which also has access to a “nonlocal” closure variable containing the necessary section identification. I haven’t tried it in the real code, but a quick test seems to work. Something like this:


def callback_factory(section_id):
    section = section_id
    def callback(attr, old, new):
        do_shared_callback(section_id, attr, old, new)
    return callback

def do_shared_callback(section_id, attr, old, new):
    print(f"ready to do actual callback with section_id ={section_id}, new = {new}")
    return

    cb1 = callback_factory(1)
    cb2 = callback_factory(2)
    data_table_1.source.on_change('data', cb1)
    data_table_2.source.on_change('data', cb2)

1 Like

FYI you also use functools.partial to “bake in” an extra parameter to switch off, instead of a closure.

2 Likes