我已经使用过先前的帖子here和here来尝试运行代码,从而可以在Bokeh的DataTable中选择一行,并获取行号。但是,我发现使用该代码后,越过第6或7行,生成的行号是错误的-例如,我可能单击第17行,并说它是第6行。如何解决此问题?
请注意this中的代码,仅当我将“源” ColumnDataSource的范围从10增加到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))
我附上了运行代码时遇到的错误的图片。如您所能,我单击了第16行,它说的是行索引10。
或者我的其他代码(引用了许多我已经从本地工作服务器上的数据创建的不同数据框等):
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)
答案 0 :(得分:1)
我的回调代码不适用于具有滚动条的表。从那时起,我将其更新为更健壮的功能(但仅适用于文档中的第一个表小部件)
from bokeh.io import show
from bokeh.layouts import widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import DataTable,TableColumn
column_list = ['col1','col2','col3']
source = ColumnDataSource(data = {key:range(20) for key in column_list})
columns = [TableColumn(field=col, title=col) for col in column_list]
data_table = DataTable(source=source, columns=columns, width=400, height=280,selectable=True)
source_code = """
var grid = document.getElementsByClassName('grid-canvas')[0];
var active_row = grid.querySelectorAll('.active')[0];
if (active_row!=undefined){
var active_row_ID = Number(active_row.children[0].innerText);
for (var i=1, imax=active_row.children.length; i<imax; i++){
if (active_row.children[i].className.includes('active')){
var active_col_ID = i-1;
}
}
console.log('row',active_row_ID);
console.log('col',active_col_ID);
var active_cells = grid.querySelectorAll('.active');
for (i=0, imax=active_cells.length;i<imax;i++){
active_cells[i].classList.remove('active');
}
cb_obj.indices = [];
}
"""
source.selected.js_on_change('indices', CustomJS(args={'source':source},code= source_code) )
show(widgetbox(data_table))
您可以使用active_row_ID和active_col_ID
cb_obj.indices = []
在其中允许连续两次单击同一单元格时触发回调。但这会再次触发回调。
这就是为什么在active_cells
上进行最后一个循环以从活动单元格的类名中剥离.active
的原因,这将使active_row
在第二次执行中未定义。
if (active_row!=undefined)
可以丢弃第二次执行回调。