如何在Bokeh中更新多选菜单

时间:2019-10-03 17:29:40

标签: javascript bokeh

我是Bokeh的新手,我正在尝试根据用户在另一个多选菜单中的先前选择来更新多选菜单,但没有成功。没有错误,但是根据“种类”菜单中的选择,以下代码中的“设置”菜单未更新。我可能已经摸索了最后一个回调(第142-146行),但是我不知道如何解决。有想法吗?



# Data handling
import pandas as pd

# Bokeh libraries
from bokeh.plotting import figure, output_file, show
from bokeh.embed import components
from bokeh.models import Toggle, ColumnDataSource, FactorRange, Callback, CustomJS
from bokeh.models.widgets import DataTable, NumberFormatter, TableColumn, HTMLTemplateFormatter
from bokeh.io import curdoc
from bokeh.layouts import row, column
from bokeh.models.widgets import TextInput, MultiSelect

# test data
original_data = dict(species = ['A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','B','C','D','E','A','A'],
length = [2, 10, 20, 40, 60, 80, 70, 50, 15, 36, 76, 74, 72, 44, 36, 18, 40, 64, 40, 64, 40, 30, 120],
weight = [2, 100, 150, 200, 420, 700, 600, 300, 200, 200, 620, 610, 601, 610, 601, 80, 205, 80, 800, 700, 240, 160, 800],
set_number = ['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '3', '4', '1', '5', '1'],
sex_id = [2, 0, 0, 0, 2, 1, 1, 0, 2, 1, 1, 1, 0, 1, 1, 0, 2, 1, 1, 2, 1, 0, 2])

original_data = pd.DataFrame(original_data)
original_data['color'] = 'black'

original_source = ColumnDataSource(original_data)

# Empty source so the plot is empty before the data is selected
source = ColumnDataSource(dict(species = [], set_number = [], length = [], weight = [], sex_id = [], color = []))

### Main plot
plot = figure(title = 'Length-weight scatterplot', x_axis_label = 'length (cm)', y_axis_label = 'weight (g)',
        tools = "pan, hover, box_zoom, reset, save", toolbar_location = "right", 
        plot_width = 450, plot_height = 450)

plot.circle(x = 'length', y = 'weight', color = 'color', fill_alpha = 1, source = source, line_width = 2)

# Select species
available_species = list(set(original_data['species']))
available_species.sort()

species_callback = CustomJS(args = {'source': source, 'original_source': original_source},
code = """
var data = original_source.data;
var source_data = source.data;

var species_data = data['species'];
var length_data = data['length'];
var weight_data = data['weight'];
var sex_id_data = data['sex_id'];
var set_number_data = data['set_number'];
var color_data = data['color'];
var selected_species = cb_obj.value;

var source_length = source_data['length'];
source_length.length = 0;

var source_weight = source_data['weight'];
source_weight.length = 0;

var source_sex_id = source_data['sex_id'];
source_sex_id.length = 0;

var source_species = source_data['species'];
source_species.length = 0;

var source_set_number = source_data['set_number'];
source_set_number.length = 0;

var source_color = source_data['color'];
source_color.length = 0;
for (var i = 0; i < length_data.length; i++) {
if (selected_species.indexOf(species_data[i]) >= 0) 
source_length.push(length_data[i]);
source_weight.push(weight_data[i]);
source_species.push(species_data[i]);
source_set_number.push(set_number_data[i]);
source_sex_id.push(sex_id_data[i]);
source_color.push(color_data[i]);
}
}
source.change.emit();
""")

multiselect_species = MultiSelect(title = 'Species:', value = [], options = available_species, width = 240)
#multiselect_species.js_on_change('value', species_callback)

# Select set (multiselect)
#available_set = list(set(map(str, original_data['set_number'])))
available_set = list(set(original_data['set_number']))
available_set.sort()

set_callback = CustomJS(args = {'source': source, 'original_source': original_source},
code = """
var data = original_source.data;
var source_data = source.data;

var species_data = data['species'];
var length_data = data['length'];
var weight_data = data['weight'];
var sex_id_data = data['sex_id'];
var set_number_data = data['set_number'];
var color_data = data['color'];
var selected_species = cb_obj.value;

var source_length = source_data['length'];
source_length.length = 0;

var source_weight = source_data['weight'];
source_weight.length = 0;

var source_sex_id = source_data['sex_id'];
source_sex_id.length = 0;

var source_species = source_data['species'];
source_species.length = 0;

var source_set_number = source_data['set_number'];
source_set_number.length = 0;

var source_color = source_data['color'];
source_color.length = 0;

 for (var i = 0; i < length_data.length; i++) {
 if (selected_species.indexOf(set_number_data[i]) >= 0) {

 source_length.push(length_data[i]);
 source_weight.push(weight_data[i]);
 source_species.push(species_data[i]);
 source_set_number.push(set_number_data[i]);
 source_sex_id.push(sex_id_data[i]);
 source_color.push(color_data[i]);
 }
 }
 source.change.emit();
 """)

multiselect_set = MultiSelect(title = 'Set:', value = [], options = available_set, width = 240)
multiselect_set.js_on_change('value', set_callback)

# The sets available should be updated depending on the species chosen
multiselect_species.js_on_change('value', species_callback, CustomJS(args = dict(multiselect_set = multiselect_set,
                                                                                  available_set = available_set),
code = """
const var_set = %s
multiselect_set.options = var_set.[cb_obj.value]
multiselect_set.options = multiselect_set.options.join()
""" % available_set))

# Data table
columns = [TableColumn(field = "species", title = "common name", width = 100),
           TableColumn(field = "set_number", title = "set number", width = 100),
           TableColumn(field = "length", title = "length (cm)", width = 100),
           TableColumn(field = "weight", title = "weight (g)", width = 100),
           TableColumn(field = "sex_id", title = "sex", width = 100)]

data_table = DataTable(source = source, columns = columns, sortable = True, editable = True, width = 500, height = 450,
                        fit_columns = True)

# Set up widgets layout
widgets_layout = column(multiselect_species, multiselect_set)

# Set up figures layout
figures_layout = row(plot, data_table)

# Set up page layout
page_layout = row(widgets_layout, figures_layout)

show(page_layout)


```I

1 个答案:

答案 0 :(得分:0)

也许没有Python错误,但是您的代码中有两个JavaScript(JS)语法错误。如果存在JS错误,请始终在浏览器的JS控制台中查看。您还为multiselect_species设置了一个双重回调(我删除了第二个回调,它清除了您的选项)。下面是Bokeh v1.3.0的工作代码

# Data handling
import pandas as pd

# Bokeh libraries
from bokeh.plotting import figure, output_file, show
from bokeh.embed import components
from bokeh.models import Toggle, ColumnDataSource, FactorRange, Callback, CustomJS
from bokeh.models.widgets import DataTable, NumberFormatter, TableColumn, HTMLTemplateFormatter
from bokeh.io import curdoc
from bokeh.layouts import row, column
from bokeh.models.widgets import TextInput, MultiSelect

# test data
original_data = dict(species = ['A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','B','C','D','E','A','A'],
length = [2, 10, 20, 40, 60, 80, 70, 50, 15, 36, 76, 74, 72, 44, 36, 18, 40, 64, 40, 64, 40, 30, 120],
weight = [2, 100, 150, 200, 420, 700, 600, 300, 200, 200, 620, 610, 601, 610, 601, 80, 205, 80, 800, 700, 240, 160, 800],
set_number = ['1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '3', '4', '1', '5', '1'],
sex_id = [2, 0, 0, 0, 2, 1, 1, 0, 2, 1, 1, 1, 0, 1, 1, 0, 2, 1, 1, 2, 1, 0, 2])

original_data = pd.DataFrame(original_data)
original_data['color'] = 'black'

original_source = ColumnDataSource(original_data)

# Empty source so the plot is empty before the data is selected
source = ColumnDataSource(dict(species = [], set_number = [], length = [], weight = [], sex_id = [], color = []))

### Main plot
plot = figure(title = 'Length-weight scatterplot', x_axis_label = 'length (cm)', y_axis_label = 'weight (g)',
        tools = "pan, hover, box_zoom, reset, save", toolbar_location = "right", 
        plot_width = 450, plot_height = 450)

plot.circle(x = 'length', y = 'weight', color = 'color', fill_alpha = 1, source = source, line_width = 2)

# Select species
available_species = list(set(original_data['species']))
available_species.sort()

code = """ 
    var data = original_source.data;
    var source_data = source.data;

    var species_data = data['species'];
    var length_data = data['length'];
    var weight_data = data['weight'];
    var sex_id_data = data['sex_id'];
    var set_number_data = data['set_number'];
    var color_data = data['color'];
    var selected_species = cb_obj.value;

    var source_length = source_data['length'];
    source_length.length = 0;

    var source_weight = source_data['weight'];
    source_weight.length = 0;

    var source_sex_id = source_data['sex_id'];
    source_sex_id.length = 0;

    var source_species = source_data['species'];
    source_species.length = 0;

    var source_set_number = source_data['set_number'];
    source_set_number.length = 0;

    var source_color = source_data['color'];
    source_color.length = 0;
    unique_sets = []
    for (var i = 0; i < length_data.length; i++) {
        if (selected_species.indexOf(species_data[i]) >= 0)  {
            source_length.push(length_data[i]);
            source_weight.push(weight_data[i]);
            source_species.push(species_data[i]);
            source_set_number.push(set_number_data[i]);
            source_sex_id.push(sex_id_data[i]);
            source_color.push(color_data[i]);

            if ( !unique_sets.includes(set_number_data[i]) ) {
                unique_sets.push(set_number_data[i])
            }
        }
    }

    source.change.emit();
    multiselect_set.options = unique_sets
"""

print(source.data['set_number'])

# species_callback = CustomJS(args = {'source': source, 
#                                     'original_source': original_source,
#                                     'multiselect_set': multiselect_set,
#                                     },
#                             code = code)

multiselect_species = MultiSelect(title = 'Species:', value = [], options = available_species, width = 240)
#multiselect_species.js_on_change('value', species_callback)

# Select set (multiselect)
#available_set = list(set(map(str, original_data['set_number'])))
available_set = list(set(original_data['set_number']))
available_set.sort()

set_callback = CustomJS(args = {'source': source, 'original_source': original_source},
code = """
var data = original_source.data;
var source_data = source.data;

var species_data = data['species'];
var length_data = data['length'];
var weight_data = data['weight'];
var sex_id_data = data['sex_id'];
var set_number_data = data['set_number'];
var color_data = data['color'];
var selected_species = cb_obj.value;

var source_length = source_data['length'];
source_length.length = 0;

var source_weight = source_data['weight'];
source_weight.length = 0;

var source_sex_id = source_data['sex_id'];
source_sex_id.length = 0;

var source_species = source_data['species'];
source_species.length = 0;

var source_set_number = source_data['set_number'];
source_set_number.length = 0;

var source_color = source_data['color'];
source_color.length = 0;

 for (var i = 0; i < length_data.length; i++) {
     if (selected_species.indexOf(set_number_data[i]) >= 0) { 
         source_length.push(length_data[i]);
         source_weight.push(weight_data[i]);
         source_species.push(species_data[i]);
         source_set_number.push(set_number_data[i]);
         source_sex_id.push(sex_id_data[i]);
         source_color.push(color_data[i]);
     }
 }
 source.change.emit();
 """)

multiselect_set = MultiSelect(title = 'Set:', value = [], options = available_set, width = 240)
multiselect_set.js_on_change('value', set_callback)


species_callback = CustomJS(args = {'source': source, 
                                    'original_source': original_source,
                                    'multiselect_set': multiselect_set,
                                    },
                            code = code)

# The sets available should be updated depending on the species chosen
multiselect_species.js_on_change('value', species_callback)

# Data table
columns = [TableColumn(field = "species", title = "common name", width = 100),
           TableColumn(field = "set_number", title = "set number", width = 100),
           TableColumn(field = "length", title = "length (cm)", width = 100),
           TableColumn(field = "weight", title = "weight (g)", width = 100),
           TableColumn(field = "sex_id", title = "sex", width = 100)]

data_table = DataTable(source = source, columns = columns, sortable = True, editable = True, width = 500, height = 450,
                        fit_columns = True)

# Set up widgets layout
widgets_layout = column(multiselect_species, multiselect_set)

# Set up figures layout
figures_layout = row(plot, data_table)

# Set up page layout
page_layout = row(widgets_layout, figures_layout)

show(page_layout)