I've used previous posts here and here to try and run code whereby I can select a row in a DataTable in Bokeh, and get the row number. However I'm finding that using that code, once I get past row 6 or 7 the row number generated is wrong - for example I could be clicking on row 17 and it'll say it's row 6. How do I resolve this issue?
Note for the code in this post, the error only occurs once I increase the range in the 'source' ColumnDataSource from 10 to 20.
from random import randint
from datetime import date
from bokeh.models import ColumnDataSource, TableColumn, DateFormatter, DataTable, CustomJS
from bokeh.layouts import column
from bokeh.models.widgets import TextInput
from bokeh.plotting import curdoc
source = ColumnDataSource(dict(dates = [date(2014, 3, i + 1) for i in range(20)], downloads = [randint(0, 100) for i in range(20)]))
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)
text_row = TextInput(value = None, title = "Row index:", width = 420)
text_column = TextInput(value = None, title = "Column Index:", width = 420)
text_date = TextInput(value = None, title = "Date:", width = 420)
text_downloads = TextInput(value = None, title = "Downloads:", width = 420)
test_cell = TextInput(value = None, title = "Cell Contents:", width = 420)
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 }
}
}
text_row.value = String(row);
text_column.value = String(column);
text_date.value = String(new Date(source.data['dates'][row]));
text_downloads.value = String(source.data['downloads'][row]);
test_cell.value = column == 1 ? text_date.value : text_downloads.value; """
def py_callback(attr, old, new):
print(test_cell.value)
print(text_date.value)
source.selected.update(indices = [])
source.selected.on_change('indices', py_callback)
callback = CustomJS(args = dict(source = source, text_row = text_row, text_column = text_column, text_date = text_date, text_downloads = text_downloads, test_cell = test_cell), code = source_code)
source.selected.js_on_change('indices', callback)
curdoc().add_root(column(data_table, text_row, text_column, text_date, text_downloads, test_cell))
I've attached a picture of the error I get when running the code. As you can
I've clicked on row 16 and it is saying row index 10.
Alternatively my other code (references a number of different dataframes etc. that I have already created from data on my local work server):
import pandas as pd
pd.options.mode.chained_assignment = None
import datetime as dt
import math
import random
import pandas as pd
import itertools
import pickle
from bokeh.layouts import layout
from collections import OrderedDict
from bokeh.models import ColumnDataSource, Column, TableColumn, DateFormatter, DataTable, CustomJS, DataRange1d
from bokeh.plotting import figure, curdoc
source = ColumnDataSource(dict(products=dfNew['Products'], prices=dfNew['Current Prices']))
columns = [TableColumn(field="products", title="Products"), TableColumn(field="prices", title="Current Prices")]
data_table = DataTable(source=source, columns=columns, width=400, height=350, editable=True, reorderable=False)
location_source = ColumnDataSource(dict(row=[], column=[]))
prodPx = OrderedDict()
pVal = 0
for i in products:
key = str(i)
prodPx[key] = [(dfNew['Current Prices'])[pVal]]
pVal += 1
noProd = OrderedDict()
kVal = 0
for i in products:
key = str(kVal)
noProd[key] = [i]
kVal += 1
prodpx_source = ColumnDataSource(prodPx)
noprod_source = ColumnDataSource(noProd)
#initial chart
x = new_dates
y = df[products[0]]
sourceChart = ColumnDataSource(data=dict(x=x, y=y))
chart = figure(title=ccy + ' Charting', x_axis_type='datetime', plot_width = 1200, plot_height=500)
chart.line('x', 'y', source=sourceChart, line_width=3, line_alpha=0.6)
#callbacks
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]};
}
}
}
source.change.emit();
source2.change.emit();
source3.change.emit();
source4.change.emit();
"""
#js callback
callback = CustomJS(args=dict(source=source, source2=location_source, source3=prodpx_source,
source4=noprod_source), code=source_code)
source.selected.js_on_change('indices', callback)
#python callback
def py_callback(attr, old, new):
row = str((location_source.data['row'][0]))
chartVals = (noprod_source.data[row][0])
try:
yVar = df[chartVals]
except:
yVar = df[totalProducts[0]]
sourceChart.data = dict(x=x, y=yVar)
source.selected.update(indices=[])
print(location_source.data)
source.selected.on_change('indices', py_callback)
layout = layout([data_table], [chart])
curdoc().add_root(layout)