2

I've taken the below code from another source - it is not my own code.

The code allows you to select a cell in the data table, and the 'downloads' data for that cell will chart based on the row of the cell selected.

How do I expand this code such that if I have multiple variables (eg. 'downloads' and 'uploads') and so more columns in the data table, I can chart data based on that cell (so where row AND column are important)? Alternatively, how can I define as a variable the column number of a selected cell (in the same way selected_row below can be used to define the row number)?

from datetime import date from random import randint from bokeh.models import ColumnDataSource, Column from bokeh.plotting import figure, curdoc from bokeh.models.widgets import DataTable, DateFormatter, TableColumn, Div import numpy as np data = dict(dates = [date(2014, 3, i + 1) for i in range(10)], downloads = [randint(0, 100) for i in range(10)]) d_source = ColumnDataSource(data) columns = [TableColumn(field = "dates", title = "Date", formatter = DateFormatter()), TableColumn(field = "downloads", title = "Downloads")] data_table = DataTable(source = d_source, columns = columns, width = 400, height = 280) def table_select_callback(attr, old, new): selected_row = new[0] download_count = data['downloads'][selected_row] chart_data = np.random.uniform(0, 100, size = download_count) p = figure(title = 'bla') r = p.line(x = range(len(chart_data)), y = chart_data) root_layout.children[1] = p d_source.selected.on_change('indices', table_select_callback) root_layout = Column(data_table, Div(text = 'Select Date')) curdoc().add_root(root_layout) 

2 Answers 2

1

This is the final working code (run from command line with bokeh serve --show app.py):

from datetime import date from random import randint from bokeh.models import ColumnDataSource, Column, TableColumn, DateFormatter, DataTable, CustomJS from bokeh.plotting import figure, curdoc source = ColumnDataSource(dict(dates = [date(2014, 3, i + 1) for i in range(10)], downloads = [randint(0, 100) for i in range(10)])) columns = [TableColumn(field = "dates", title = "Date", formatter = DateFormatter()), TableColumn(field = "downloads", title = "Downloads")] data_table = DataTable(source = source, columns = columns, width = 400, height = 280, editable = True, reorderable = False) info_source = ColumnDataSource(dict(row = [], column = [])) source_code = """ var grid = document.getElementsByClassName('grid-canvas')[0].children; var row, column = ''; for (var i = 0,max = grid.length; i < max; i++){ if (grid[i].outerHTML.includes('active')){ row = i; for (var j = 0, jmax = grid[i].children.length; j < jmax; j++) if(grid[i].children[j].outerHTML.includes('active')) { column = j; source2.data = {row: [row], column: [column]}; } } }""" callback = CustomJS(args = dict(source = source, source2 = info_source), code = source_code) source.selected.js_on_change('indices', callback) def py_callback(attr, old, new): source.selected.update(indices = []) print(info_source.data) source.selected.on_change('indices', py_callback) curdoc().add_root(Column(data_table)) 
Sign up to request clarification or add additional context in comments.

Comments

0

My suggestion is to use my another post that uses a JS callback to access the row and column of the selected cell. This must be a JS callback as it uses HTML elements to walk through. So the steps are as follows:

  1. Define a new ColumnDataSource that will contain the row and column number of a clicked cell

    info_source = ColumnDataSource(dict(row = [], column = []))

  2. Use the JS callback from this post to fill in the row and column values in this new table_info_source like this:

callback=CustomJS(args=dict(source=d_source,source2=info_source),code=source_code)
source.selected.js_on_change('indices', callback)

  1. Inside the JS callback store the row and column index like this:

    source2.data = {row:[row],column:[column]};

  2. Access the info_source.data information in your Python callback to draw a plot

    print (info_source.data)

Both callback are attached to the same source but usually JS callback is executed first so the data should be available in the Python callback on time. Having the index of row and column of the clicked cell you should be able to retrieve the required data and draw your chart.

4 Comments

Thanks for the response - it seems from what you’ve said in the first bit above there is not, but just to clarify is there any way at all to do this without requiring JS or is that the only option? The team I am working in has expressed a preference for no JS usage
To my knowledge JS callback is the only option to get the column index of a clicked cell. Please note that Bokeh app in the browser is made of BokehJS models so it's 100% JS and it's kind of weird requirement to forbid JS. BokehJS is periodically synchronised with Python models via the Bokeh server. Eventually the info_source.data containing the column index will be accessible in Python code where you can do whatever you need.
I've tried following those instructions but the code I currently have (posted below) is printing {'column': [], 'row': []} - it's not populating with the information. What have I done wrong?
I updated my answer and your code accordingly. Apparently the BokehJS can detect changes only when a new object is assigned to the ColumnDataSource.data.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.