I am creating a plot that will display a range of product based on their specific attributes. I modeled the design of the graph off of Bokeh's "movies" example. I can run my plot by navigating to the source folder and executing "bokeh serve --show shafts" from Andaconda Prompt.
My issue is that I need to save an HTML file so I can distribute it to multiple people without the accompanying database. If I attempt to save an HTML file
"output_file("slider.html", title="slider.py example")"
from either the movies example or my code and then run the plot from the HTML file the sliders do not update the graph. I believe the issue is that when you run the file from "bokeh serve --show shafts" from Andaconda Prompt it is running on the server and is able to access the python code continuously.
Alternatively, when you run it from the HTML it complies all the code into a JASON format and can no longer access the python code. To get around this Bokeh added small Javascript sections that will continue to update off the server.
Bokeh gives multiple example of how to do this but I don't fully grasp what is being updated in the Javascript to update the graph. I'm not extremely familiar with JS so it makes it a little difficult. A simple example they give is:
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure, output_file, show
output_file("callback.html")
x = [j*0.005 for j in range(0, 200)]
y = x
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
callback = CustomJS(args=dict(source=source), code="""
var data = source.data;
var f = cb_obj.value
x = data['x']
y = data['y']
for (i = 0; i < x.length; i++) {
y[i] = Math.pow(x[i], f)
}
source.change.emit();
""")
slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=callback)
layout = column(slider, plot)
show(layout)
The y[i] is clearly what is being updated to update the plot but I can't figure out how it relates to the original python update or why this change can effect the graph which seems to be outside its scope. My real question is in my code, what variables do I need to update in order for the code to run off the Bokeh server.
Bellow is my code. The for control in controls: control.on_change('value'....... is the portion of the code that updates the graph when on the server but I need to replace it with the JavaScript code so it will update when saved as a HTML.
callback = CustomJS(args=dict(source=source), code="""
var data = source.data;
selected = shafts[
((data.Weight2g >= min_weight_slider.value) &&
(data.Weight2g <= max_weight_slider.value) &&
(data.Butt_Frequency >= min_butt_freq_slider.value) &&
(data.Butt_Frequency <= max_butt_freq_slider.value) &&
(data.Tip_Frequency >= min_tip_freq_slider.value) &&
(data.Tip_Frequency <= max_tip_freq_slider.value) &&
(data.Torque >= min_torque_slider.value) &&
(data.Torque <= max_torque_slider.value))
];
data = selected;
source.data = selected;
}
source.change.emit;
""")
min_weight_slider = Slider(title="Minimum Weight", value=40,
start=40.0, end=200.0, step=0.5, callback = callback)
callback.args["min_weight_slider"] = min_weight_slider
max_weight_slider = Slider(title="Maximum Weight", value=200, start=40.0, end=200.0, step=0.5, callback = callback)
callback.args["max_weight_slider"] = max_weight_slider
min_butt_freq_slider = Slider(title="Minimum Butt Frequency", value=180.0, start=100.0, end=500.0, step=10.0, callback = callback)
callback.args["min_butt_freq_slider"] = min_butt_freq_slider
max_butt_freq_slider = Slider(title="Maximum Butt Frequency", value=500.0, start=100.0, end=500.0, step=10.0, callback = callback)
callback.args["max_butt_freq_slider"] = max_butt_freq_slider
min_tip_freq_slider = Slider(title="Minimum Tip Frequency", value=180, start=100, end=500, step=10, callback = callback)
callback.args["min_tip_freq_slider"] = min_tip_freq_slider
max_tip_freq_slider = Slider(title="Maximum Tip Frequency", value=400, start=100, end=500, step=10, callback = callback)
callback.args["max_tip_freq_slider"] = max_tip_freq_slider
min_torque_slider = Slider(title="Minimum Torque", value=2, start=1, end=20, step=0.1, callback = callback)
callback.args["min_torque_slider"] = min_torque_slider
max_torque_slider = Slider(title="Maximum Torque", value=15, start=1, end=20, step=0.1, callback = callback)
callback.args["max_torque_slider"] = max_torque_slider
x_axis = Select(title="X Axis", options=sorted(axis_map.keys()), value="Butt_Frequency")
callback.args["x_axis"] = x_axis
y_axis = Select(title="Y Axis", options=sorted(axis_map.keys()), value="Tip_Frequency")
callback.args["y_axis"] = y_axis
def select_shafts():
selected = shafts[
(shafts.Weight2g >= min_weight_slider.value) &
(shafts.Weight2g <= max_weight_slider.value) &
(shafts.Butt_Frequency >= min_butt_freq_slider.value) &
(shafts.Butt_Frequency <= max_butt_freq_slider.value) &
(shafts.Tip_Frequency >= min_tip_freq_slider.value) &
(shafts.Tip_Frequency <= max_tip_freq_slider.value) &
(shafts.Torque >= min_torque_slider.value) &
(shafts.Torque <= max_torque_slider.value)
]
return selected
#updates the
def update():
df = select_shafts()
#re-names the above function
x_name = axis_map[x_axis.value]
y_name = axis_map[y_axis.value]
p.xaxis.axis_label = x_axis.value
p.yaxis.axis_label = y_axis.value
p.title.text = "%d shafts selected" % len(df)
source.data = dict(
x=df[x_name],
y=df[y_name],
color=df["color"],
Manufacture=df["Manufacture"],
Model = df["Model"],
Type = df["Type"],
Weight = df["Weight"],
Flex=df["Flex"],
Butt_Frequency = df["Butt_Frequency"],
Tip_Frequency = df["Tip_Frequency"],
Torque=df["Torque"],
Weight2G = df["Weight2g"],
Availability = df["Availability"],
alpha=df["alpha"]
)
controls = [min_weight_slider, max_weight_slider, min_butt_freq_slider, max_butt_freq_slider, min_tip_freq_slider, max_tip_freq_slider, min_torque_slider, max_torque_slider,x_axis, y_axis]
#for control in controls:
#control.on_change('value', lambda attr, old, new: update())
sizing_mode = 'fixed' # 'scale_width' also looks nice with this example
inputs = widgetbox(*controls, sizing_mode=sizing_mode)
#Widget box produced with bokeh
l = layout([
[inputs, p]
], sizing_mode=sizing_mode)
update() # initial load of the data
curdoc().add_root(l)
curdoc().title = "Shafts"
show(l)
Thanks so much in advance
I wanted to add an update on my attempts to solve my problem. I am realizing that when the program runs on the Bokeh server it is able to continually update the source data that the plot function has access to. When the program runs the JS function it can only update the values inside individual dictionary entry keys.
I am attempting to modify this code to mock my need.