16

I am trying to use Bokeh to make an editable DataTable that updates the source data when the data is edited. I started with the standard DataTable example here, and make the editable kwarg to true. Here is where I am at:

from datetime import date from random import randint from bokeh.models import ColumnDataSource, Callback from bokeh.models.widgets import DataTable, DateFormatter, TableColumn from bokeh.io import output_file, output_notebook, show, vform output_notebook() data = dict(dates=[date(2014, 3, i+1) for i in range(10)], downloads=[randint(0, 100) for i in range(10)]) source = ColumnDataSource(data) columns = [TableColumn(field="dates", title="Date", formatter=DateFormatter()), TableColumn(field="downloads", title="Downloads")] callback = Callback(args=dict(Source=source), code=""" console.log( '#cell edited')""") data_table = DataTable(source=source, columns=columns, width=400, height=280, editable=True) data_table.on_change(callback,source) show(vform(data_table)) 

This makes an editable data table, but I can't figure out how to get the callback to update the source data, or to configure the source data so that it automatically does that. I thought there was a way to automatically do that with ColumnDataSource, and after trying that tried to write a callback. However it appears the DataTable doesn't have a callback option, but it oddly has an on_change attribute.

Does anyone know how to do this?

2 Answers 2

4

Update 2019/07/18. Bokeh v1.0.0 and newer versions

The source data is updated with editable=True and the on_change callback is called when the data attribute is updated. But we need an auxiliar variable to keep the old data source.

from bokeh.models import ColumnDataSource from bokeh.models.widgets.tables import ( DataTable, TableColumn, IntEditor ) from bokeh.io import curdoc import copy dict1 = { 'x': [0, 0, 0, 0, 0, 0], 'y': [0, 1, 0, 1, 0, 1] } source = ColumnDataSource(data=dict1) old_source = ColumnDataSource(copy.deepcopy(dict1)) columns = [ TableColumn(field="x", title="x"), TableColumn(field="y", title="y", editor=IntEditor(step=1)) ] data_table = DataTable( source=source, columns=columns, width=800, editable=True, reorderable=False, ) def on_change_data_source(attr, old, new): # old, new and source.data are the same dictionaries print('-- SOURCE DATA: {}'.format(source.data)) print('>> OLD SOURCE: {}'.format(old_source.data)) # to check changes in the 'y' column: indices = list(range(len(old['y']))) changes = [(i,j,k) for i,j,k in zip(indices, old_source.data['y'], source.data['y']) if j != k] print('>> CHANGES: {}'.format(changes)) old_source.data = copy.deepcopy(source.data) print('SOURCE DATA: {}'.format(source.data)) data_table.source.on_change('data', on_change_data_source) curdoc().add_root(data_table) 

Bokeh v0.13.0 and lower versions

This works on Bokeh v0.13.0 and lower versions. :

from bokeh.models import ColumnDataSource from bokeh.models.widgets import DataTable, TableColumn, HTMLTemplateFormatter from bokeh.io import curdoc dict1 = { 'x':[0]*6, 'y':[0,1,0,1,0,1] } source = ColumnDataSource(data=dict1) columns = [ TableColumn(field="x", title="x"), TableColumn(field="y", title="y") ] data_table = DataTable( source=source, columns=columns, width=800, editable=True, ) def on_change_data_source(attr, old, new): print('-- OLD DATA: {}'.format(old)) print('-- NEW DATA: {}'.format(new)) print('-- SOURCE DATA: {}'.format(source.data)) # to check changes in the 'y' column: indices = list(range(len(old['y']))) changes = [(i,j,k) for i,j,k in zip(indices, old['y'], new['y']) if j != k] if changes != []: for t in changes: # t = (index, old_val, new_val) patch = { 'y' : [(t[0], int(t[2])), ] # the new value is received as a string } # source2.patch(patch) # to update the values on another source variable for instance source.on_change('data', on_change_data_source) curdoc().add_root(data_table) 
Sign up to request clarification or add additional context in comments.

Comments

3

The following code will detect the event of clicking (selecting) a row or rows. I put some console.log to output the rows selected.

from datetime import date from random import randint import bokeh import bokeh.plotting data = dict(dates=[date(2014, 3, i+1) for i in range(10)], downloads=[randint(0, 100) for i in range(10)]) source = bokeh.models.ColumnDataSource(data) columns = [bokeh.models.TableColumn(field="dates", title="Date", formatter=bokeh.models.DateFormatter()), bokeh.models.TableColumn(field="downloads", title="Downloads")] source.callback = bokeh.models.CustomJS(args=dict(source=source), code=""" console.log( '#Selected rows:'); var indices = source.selected["1d"].indices; for (var i = 0; i<indices.length; i++){ console.log(i+":"+indices[i]); } """) data_table = bokeh.models.DataTable(source=source, columns=columns, width=400, height=280, editable=True) bokeh.io.output_notebook() bokeh.io.show(data_table) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.