What I want to achieve here:
source
property, because it will generate larger HTML filefrom bokeh.models import Div, CustomJS
from bokeh.layouts import column
from bokeh.plotting import show
from bokeh.io import curdoc
dummy_div = Div(text="")
init_code = CustomJS(code="""
window.sharedData = { initialized: true };
console.log("Data initialized in Div change");
""")
#dummy_div.js_on_change("text", init_code)
button1 = Button(label="Log Complex Data", button_type="success")
button1.js_on_click(CustomJS(code="""
console.log("Current shared data:", window.sharedData);
"""))
# button_N = ...
layout = column(dummy_div, button1)
curdoc().add_root(layout)
curdoc().on_event('document_ready', lambda event: init_code.execute(curdoc()))
show(layout)
Is it possible to implement something like this with this library?
Context:
This part is not needed for answering the above question, I've just wanted to show the use-case because some people wish not to give a simple answer without this
I've a complex hierarchy of ColumnDataSource-es and other data for a very specific step logic stored in form of a dict, what I need to use on JS side. I can not pass the ColumnDataSource objects separately, because the number of ColumnDataSource-s to be used is unknown in advance. There is a dynamic logic how the buttons should be generated and how they should read this hierarchy, the logic is dependent on a number of timeframe keys within the dict. I need to pass this dict to each generated button. Since the DataSource is wrapped, duplication occurs.
This is how I need to organize the data for the step logic:
js_data[aggr_interval] = {
'data_source' : ColumnDataSource(dataframe),
'candle_data' : dataframe.to_dict(orient="list"),
}
This is the step logic:
time_tracker = ColumnDataSource(data=dict(trigger_date=[max_dt]))
# I'VE A LOT OF THESE BUTTONS
# THE ARGUMENT LIST CAN NOT BE FIXED HERE
# I'VE TO PUT DATA SOURCES INTO A HIERARCHY WITH TIMEFRAME KEYS (candle_data_and_sources )
# AND IMPLEMENT A DYNAMIC LOGIC ON JS SIDE
# THE TIMEFRAMES ARE NOT KNOWN IN ADVANCE
# THIS IS WHAT DUPLICATES THE DATA
# AND INCREASES THE SIZE OF THE GENERATED HTML
step_buttons['prev'].js_on_click(CustomJS(
args = dict(
candle_data_and_sources = candle_data_and_sources,
time_tracker = time_tracker,
direction = -1,
min_dt = min_dt,
max_dt = max_dt,
),
code = JS_CODE_STEP_LOGIC,
))
JS_CODE_STEP_LOGIC = """
const trigger_date = new Date(time_tracker.data['trigger_date'][0]);
let new_date = new Date(trigger_date);
new_date.setDate(new_date.getDate() + 1 * direction);
if (direction < 0){
new_date = new Date(Math.max(min_dt, new_date));
} else if (direction > 0){
new_date = new Date(Math.min(max_dt, new_date));
}
time_tracker.data['trigger_date'][0] = new_date.toISOString();
// I NEED TO DO THE FOLLOWING LOGIC FOR EACH TIMEFRAME
// THE NUMBER/VALUE OF TIMEFRAMES HERE ARE DYNAMIC
// THEREFORE THEY ARE ADDRESSING THE DATASOURCE IN THE HIERARCHY
for (const [timeframe, data] of Object.entries(candle_data_and_sources)) {
const filtererd_obejcts = {};
for (const [key, value] of Object.entries(data['candle_data'])) {
if(!filtererd_obejcts[key]){
filtererd_obejcts[key] = [];
}
}
for (let i = 0; i < data['candle_data'].trigger_dt.length; i++) {
if (new Date(data['candle_data'].trigger_dt[i]) <= new_date) {
for (const [key, value] of Object.entries(data['candle_data'])) {
filtererd_obejcts[key].push(value[i]);
}
}
}
data['data_source'].data = filtererd_obejcts;
data['data_source'].change.emit();
}
time_tracker.change.emit();
"""
on_event
is for registering actual Python callbacks, which are only possible when you are deploying a Bokeh server application (the running Bokeh server, connected to the page via websocket, is the Python process that would run your Python callback code).
When you are generating static HTML output, it is only possible to use JavaScript callbacks, e.g. with js_on_event
, since there is no Python process around to run any Python code at the time the page HTML is loaded. In this case, you want:
curdoc().js_on_event('document_ready', init_code)
Edit: I will add, for completeness: there are some folks working on making "Python" callbacks possible in the browser without a running Bokeh server, by using things like PyScript and Web Assembly. I don't know any more than to comment about their existence, though.