首页 > 解决方案 > 在 Bokeh DataTable 错误中引用行/列的 JS 代码

问题描述

我在这里这里使用了以前的帖子来尝试运行代码,借此我可以在 Bokeh 的 DataTable 中选择一行,并获取行号。但是我发现使用该代码,一旦我超过第 6 行或第 7 行,生成的行号是错误的 - 例如,我可以单击第 17 行,它会说它是第 6 行。我该如何解决这个问题?

请注意这篇文章中的代码,只有当我将“源”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)


标签: javascriptchartsbokeh

解决方案


我的回调代码不适用于具有滚动条的表。从那时起,我将它更新为更强大的东西(但它仅适用于文档中的第一个表格小部件)

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)是否可以丢弃回调的第二次执行。


推荐阅读