You’re pretty close.
The biggest thing is that you’re mixing up python-callbacks with CustomJS callbacks. With CustomJS, you don’t need to embed it in a python function, you just assign the callback to the widget via js_on_change. You kinda did half and half.
The second thing is the callback code itself → you are really trying to make the function work for both cases: one) where user changes text input you want the slider to update, and two) where user changes slider you want the text to update. That’s totally possible to do (and my solution below does that), but you COULD simplify things by creating two callbacks, one to execute when the slider changes, and one to execute when the text input changes.
The tricky part with making it “one callback” is that you need a way to identify what widget was changed, and then update the other one. My solution is to use/abuse the “name” attribute (which is available on all bokeh models AFAIK).
import numpy as np
from bokeh.layouts import column, row
from bokeh.models import CustomJS, Slider
from bokeh.plotting import ColumnDataSource, figure, show
from bokeh.models.widgets import TextInput
x = np.linspace(0, 10, 500)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(y_range=(-10, 10), width=400, height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
#adding "name" attributes to the models can really help identify them later... see thecallback
offset_slider = Slider(start=-5, end=5, value=0, step=1, title="Offsetslider",name='slideyboi')
offset_text = TextInput(value="0.1", title="offsettext",name='textyboi')
thecallback = CustomJS(args=dict(source=source,os=offset_slider,ot=offset_text),
code="""
const data = source.data;
//useful: built into CustomJS funcs is the cb_obj variable, which refers to the object that is being altered/used
//so we can figure out what's being altered (i.e the slider or the text) via the name attribute attached to cb_obj
console.log(cb_obj.name)
// now just your basic if statement to handle either case
if (cb_obj.name == 'slideyboi'){
ot.value = os.value.toString()}
else if (cb_obj.name =='textyboi'){
os.value = parseInt(ot.value)}
const B = parseFloat(os.value);
// everything below here is unaltered
const x = data['x'];
const y = data['y'];
for (let i = 0; i < x.length; i++) {
y[i] = B + Math.sin(2*x[i]);
}
source.change.emit();
""")
offset_slider.js_on_change('value', thecallback)
offset_text.js_on_change('value', thecallback)
layout = row(plot,column(offset_slider,offset_text))
show(layout)
