HI,
I have created 10 different layouts in Bokeh which are similar to each other with changes in numbers and content. I want to create a drop down which will help select one of the layouts. I am able to do it with Bokeh server but I want to save it as a single HTML file, which does not seem to be an option.
Certainly, but the HTML file will have to contain everything all together, so it might be fairly large depending on what your data is. Your best bet is to combine all ten sub-layouts in one high level layout like a row or column and then use a CustomJS callback on the the drop down to control the visible property on the sub-layouts appropriately (e.g. make all of them invisible except the one you want to currently display). There are several questions on this forum that have touched on this pattern so I would suggest searching here for “visible” as a first step.
@Vishal_Agarwal I am not sure what you are asking. My answer was above was explicitly for accomplishing what you want using a dropdown: Add everything (all the different layouts) up front as children of one top-level column, and use the JS callback on the dropdown to control the visibility of the children in the column. That does not require a Bokeh server.
That answers the question but I am not getting the right response from the callback. I am not sure if something else is required. Is this the right place to post the code for checking?
I don’t want to duplicate by uploading on stackoverflow. I am attaching the screenshot of the code which is set for JS Callback for the layouts. Can you please check and point out the error!
@Vishal_Agarwal please do not ever post screenshots of code. Besides making it more difficult for people to use to help you (because it cannot be copy and pasted to investigate), it is also completely inaccessible to users that employ screen reader or other assistive technologies. Always paste code as actual text.
You CustomJS code should do the following:
First, loop over every item in layout.children and set .visible = false. for all of them This will hide all the layouts.
Second, based on the value of the Select widget, set the .visible = true for whichever one of the layouts you want to currently be visible.
Note, based on your description of the task, there is nothing to do with sources or data or change events on those.
I am trying to incorporate as you mentioned here. I am not familiar with JS scripts so some help on getting the right code will be helpful. Here is the code:
@Vishal_Agarwal please also make use of the code formatting options in the editor so that your code is intelligible (you can go back and edit your previous post). This is especially important for Python code, since blank space is significant. You can use either the </> icon on the editing toolbar, or add triple backtick ``` fences around the code blocks, to make them appear properly formatted.
from bokeh.layouts import row, column
from bokeh.models import CustomJS, Select
from bokeh.plotting import figure, show
p1 = figure()
p1.circle([1,2,3], [1,3,2], size=20)
p2 = figure(visible=False)
p2.line([1,2,3], [1,3,2], line_width=3)
col = column(p1, p2)
s = Select(value="circle", options=["circle", "line"])
s.js_on_change('value', CustomJS(args=dict(s=s, col=col), code="""
for (const plot of col.children) {
plot.visible = false
}
// Pick one of the column children to make visible.
//
// *You* will have to provide the logic to decide which one of the
// children to make visible, based on the select value. That is a
// policy decision that depends completely on your requirements.
if (s.value == "circle") {
col.children[0].visible = true
}
else if (s.value == "line") {
col.children[1].visible = true
}
"""))
show(row(s, col))
This uses single plots for simplicity, but replace them with any more complicated layout and it is the same.
Thank you so much. This solves the issue.
One final question: If I have to just make the selected value visible, do I need to do a if-else for all the 10/20 different layouts or can I do something like:
if (s.value == "Cust1") {
col.children[0].visible = true
}
else {
index = options.indexof(s.value)
col.children[index].visible = true
}
You could set the name property of each child layout to match one of the options in the select, and then compare the current select value to the child.name. In fact you could accomplish this in one loop. Something like (untested)
for (const child of col.children) {
child.visible = (child.name == s.value)
}
Could be a bug, or could be some usage issue. At this point we’d need a complete Minimal Reproducible Example that can actually be run to reproduce the situation for direct investigation.
@Vishal_Agarwal Bokeh manages the layout and size of its own components. If you start setting CSS width, height, padding, etc. you will almost certainly interfere with that, and all bets are off. If you have need for this level of control you might be better off using a custom Jinja template and having the CustomJS manipulate real DOM elements directly.
Even if I remove the styling, the issue remains. So in that case, I cannot create layouts for drop-down menus. With the use of Jinja Template, I will need to use a server but I want to save it as a single HTML file. Is there a way around it?