Have tried to replicate the issue with the below example. The chart updates based on selection, and multiple selection allow for work to be done before rendering the plot. The positive values are plotting as expected, and if the result of the multiple selection is positive, the chart is correctly rendered. However if the result is negative, the chart does not render correctly. Below run with bokeh serve.
import bokeh
import pandas as pd
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import DataTable, TableColumn
from bokeh.plotting import figure, show
from bokeh.layouts import column, row, layout
from bokeh.io import curdoc
import datetime
import numpy as np
import pytztoday = datetime.datetime.now(pytz.timezone(‘Europe/London’))
df = pd.DataFrame([{‘a’:2},{‘a’:-3},{‘a’:5}])
df[‘Indexer’] = np.arange(len(df))
today_list = [today]ds = ColumnDataSource(df)
columns = [TableColumn(field =‘a’, title = ‘a’, width = 50)]
dt = DataTable(source = ds, columns = columns, height = 100, width = 100, fit_columns = True)plot = figure(x_axis_type=‘datetime’,plot_width=400, plot_height=400)
dot_ds = ColumnDataSource ({‘x’: pd.to_datetime(today_list), ‘y’: [0]})
dot = plot.circle(x=‘x’, y=‘y’, source = dot_ds, alpha = 0.5, size=5, color=“navy”)
dot.visible = Falseinfo_source = ColumnDataSource(dict(indexer_1 = , indexer_2 = , indexer_3= ))
#Below JS stores click indexes in a column data source which is then retrievable in python callback for future work:
code = ‘’’
///Sorting function to required to order selections: const median = arr => { const mid = Math.floor(arr.length / 2), nums = [...arr].sort((a, b) => a - b); return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; }; var inds = cb_obj.indices var data = source.data if (inds.length == 1){ var selected_index = cb_obj.indices[0]; var indexer = data['Indexer'][selected_index]; info_source.data = {indexer_1:[indexer],indexer_2:[''], indexer_3:['']} } if (inds.length == 2){ var selected_index_1 = cb_obj.indices[0] var selected_index_2 = cb_obj.indices[1]; var selected_index_b = Math.min(selected_index_2, selected_index_1) var selected_index_a = Math.max(selected_index_2, selected_index_1) var indexer_1 = data['Indexer'][selected_index_a] var indexer_2 = data['Indexer'][selected_index_b]; info_source.data = {indexer_1:[indexer_1],indexer_2:[indexer_2], indexer_3:['']} } if (inds.length == 3){ var selected_index_1 = cb_obj.indices[0] var selected_index_2 = cb_obj.indices[1] var selected_index_3 = cb_obj.indices[2] ; var selected_index_c = Math.min(selected_index_3, selected_index_2, selected_index_1) var selected_index_b = median([selected_index_3, selected_index_2, selected_index_1]) var selected_index_a = Math.max(selected_index_3, selected_index_2, selected_index_1) console.log(selected_index_a) console.log(selected_index_b) console.log(selected_index_c) var indexer_1 = data['Indexer'][selected_index_b] var indexer_2 = data['Indexer'][selected_index_a] var indexer_3 = data['Indexer'][selected_index_c] ; info_source.data = {indexer_1:[indexer_1],indexer_2:[indexer_2],indexer_3:[indexer_3]} } '''
callback = CustomJS(args = {‘source’ : ds, ‘info_source’: info_source}, code = code)
ds.selected.js_on_change(‘indices’, callback)def py_callback(attr, old, new):
indexer_1 = (info_source.data['indexer_1'][0]) indexer_2 = (info_source.data['indexer_2'][0]) indexer_3 = (info_source.data['indexer_3'][0]) #check for three clicks if indexer_3 != '': dot_value = df['a'][indexer_3] - df['a'][indexer_2] - df['a'][indexer_1] elif indexer_2 != '': dot_value = df['a'][indexer_2] - df['a'][indexer_1] else: dot_value = df['a'][indexer_1] dot_ds.data = {'x': [pd.to_datetime(today_list)] ,'y': [dot_value]} dot.visible = True
ds.selected.on_change(‘indices’, py_callback)
col = column(plot, dt)
curdoc().add_root(col)