How to get a list of Bokeh widget events and attributes (which can be used to trigger a Python callback)

醉酒当歌 提交于 2019-11-30 15:24:28

This answer was given for Bokeh v1.0.4 and may not be compliant with the latest documentation

JavaScript callbacks and Python callbacks, are very powerful tools in Bokeh and can be attached to any Bokeh model element. Additionally you can extend Bokeh functionality by writing your own extensions with TypeScript (eventually compiled to JS)

JS callbacks can be added using either of both methods:

Model.js_on_event('event', callback)
Model.js_on_change('attr', callback)

Python callbacks are mainly used for widgets:

Widget.on_event('event, onevent_handler)
Widget.on_change('attr', onchange_handler)
Widget.on_click(onclick_handler)

The exact function signature for event handlers very per widget and can be:

onevent_handler(event)
onchange_handler(attr, old, new) 
onclick_handler(new)
onclick_handler()

The attr can be any widget class (or it's base class) attribute. Therefore you need always to consult the Bokeh reference pages. Also expanding the JSON Prototype helps to find out which attributes are supported e.g. looking at Div we cannot see directly the id, name, style or text attributes which come from its base classes. However, all of these attributes are present in the Div's JSON Prototype and hence are supported by Div:

{
  "css_classes": [],
  "disabled": false,
  "height": null,
  "id": "32025",
  "js_event_callbacks": {},
  "js_property_callbacks": {},
  "name": null,
  "render_as_text": false,
  "sizing_mode": "fixed",
  "style": {},
  "subscribed_events": [],
  "tags": [],
  "text": "",
  "width": null
}

Coming back to your question: Many times you can achieve the same result using different approaches.

To my knowledge, there is no nice method that lists all supported events per widget but reading documentation and digging into the base classes helps a lot.

Using methods described above it is possible to check which widget attributes you can use in your callbacks. When it comes to events I advice you to look at and explore the bokeh.events class in your IDE. You can find there extended description for every event. In time it will come naturally when using your programmer's intuition to select the right event that your widget supports (so no button_click for Plot and no pan event for Button but the other way around).

Decision to which widget (model element) attach the callback and which method to choose or to which event bound the callback is yours and depends mainly on: which user action should trigger your callback?

So you can have a JS callback attached to any widget (value change, slider move, etc...), any tool (TapTool, HoverTool, etc...), data_source (clicking on glyph), plot canvas (e.g. for clicks on area outside a glyph) or plot range (zoom or pan events), etc...

Basically you need to know that all Python objects have their equivalents in BokehJS so you can use them the same way in in both domains (with some syntax differences, of course).

This documentation shows for example that ColumnDataSource has a "selected" property so for points you can inspect source.selected.indices and see which point on the plot are selected or like in your case: which table rows are selected. You can set a breakpoint in code in Python and also in the browser and inspect the Python or BokehJS data structures.

And here is your code (slightly modified for "pure Bokeh" v1.0.4 as I don't have Jupiter Notebook installed)

from bokeh.layouts import column
from bokeh.models import ColumnDataSource, DataTable, TableColumn
from bokeh.plotting import figure, curdoc
from bokeh.io import show, output_notebook
from bokeh.sampledata.sea_surface_temperature import sea_surface_temperature

# output_notebook()
def modify_doc(doc):
    df = sea_surface_temperature.copy()
    source = ColumnDataSource(data = df)
    source_table = ColumnDataSource(data = {"alpha": [s for s in "abcdefgh"],
                                            "num": list(range(8))})

    plot = figure(x_axis_type = 'datetime', y_range = (0, 25),
                  y_axis_label = 'Temperature (Celsius)',
                  title = "Sea Surface Temperature at 43.18, -70.43")
    plot.line('time', 'temperature', source = source)

    def callback(attr, old, new):  # here new is an array containing selected rows
        if new == 0:
            data = df
        else:
            data = df.rolling('{0}D'.format(new[0])).mean()  # asuming one row is selected

        source.data = ColumnDataSource(data = data).data

    table = DataTable(source = source_table,
                      columns = [TableColumn(field = "alpha", title = "Alpha"),
                                 TableColumn(field = "num", title = "Num")])
    source_table.selected.on_change('indices', callback)

    doc().add_root(column(table, plot))

modify_doc(curdoc)
# show(modify_doc)

Result:

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!