I am trying to develop a framework of sorts to compose bokeh components in order to create more sophisticated bokeh models without writing any javascript. I’m curious to hear from others what methods have been used to create such a framework.
I’ve created an example (works well!) below to create a component which is composed of a text box widget and slider widget. The goal is that the slider and text box are “synced”. The TextAndSlider class and use of partial functions are used in such a way in order to have access to nearly anything else that may exist in the program (namely other components). Does this look like a reasonable path towards creating larger programs with more complex components?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Slider, TextInput
from bokeh.plotting import figure
import numpy as np
from functools import partial
class TextAndSlider:
def __init__(self, label, value, start, stop, step, x):
self.label = label
self.value = value
self.start = start
self.stop = stop
self.step = step
self.text_widget = TextInput(title=self.label, value=str(self.value))
self.text_widget.on_change("value", partial(self.on_text_change, self))
self.slider_widget = Slider(title=None, value=self.value, start=self.start, end=self.stop, step=self.step)
self.slider_widget.on_change("value", partial(self.on_slider_change, self))
self.x = x
@staticmethod
def on_text_change(self, attrname, old, new):
# Update the current value
self.value = float(new)
# Update the slider value
self.slider_widget.value = self.value
# Generate the new curve
y = self.value * np.sin(2 * np.pi * self.x)
# Update the figure data
source.data = dict(x=x, y=y)
@staticmethod
def on_slider_change(self, attrname, old, new):
# Update the current value
self.value = float(new)
# Update the text box
self.text_widget.value = str(self.value)
# Generate the new curve
y = self.value * np.sin(2 * np.pi * self.x)
# Update the figure data
source.data = dict(x=x, y=y)
@property
def curdoc(self):
return bkio.curdoc()
@property
def components(self):
return (self.text_widget, self.slider_widget)
# Set up data
n = 1000
x = np.linspace(0, 20, n)
y = 1 * np.sin(2 * np.pi * x / 2)
source = ColumnDataSource(data=dict(x=x, y=y))
# Set up plot
plot = figure(plot_height=400, plot_width=400, title="my plot",
tools="crosshair,pan,reset,save,wheel_zoom",
x_range=[0, 20], y_range=[-2.5, 2.5])
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
# Set up widgets
amplitude = TextAndSlider(label="Amplitude", value=1.0, start=1.0, stop=10.0, step=0.1, x=x)
# Set up layouts and add to document
inputs = column(*amplitude.components)
bkio.curdoc().add_root(row(inputs, plot, width=800))
bkio.curdoc().title = "Sliders"```