Composition of Models

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"```

Have you looked at Panel.holoviz.org? It provides mechanisms for making such components, including compositional containers, Python classes automatically viewable with widgets for their parameters, and a ReactiveHTML way to wrap JS code that mirrors the JS variables into Python attributes.

I have looked into panel, but I had a hard time locating useful documentation. I see it uses something called the Param library as a core tool, but struggled to understand best-practices on building larger apps. From what I’ve read, it looks mostly like a Bokeh wrapper? Is this an unfair take?

Hi @astrobc1

For me Bokeh is a server, a plotting library and an app and dashboarding framework. The primary focus is the plotting library.

For me Panel is an app and dashboarding framework that stands on the shoulders of Bokeh.

With Panel you can easily use all of Pythons most data viz libraries (Matplotlib, Seaborn, Plotnine, Bokeh, Plotly, HoloViews, Altair, Deckgl, VTK and more) and some Javascript ones (ECharts, Highcharts). And more are being added all the time.

Panel also increasingly provides other components to make it easy to create awesome analytics apps . For example the Video, TextToSpeech, SpeechToText, Perspective and Tabulator components.

Panel also provide easy to use Templates like Bootstrap, Material Golden, React or Fast.

Both Bokeh and Panel are awesome. But they are different to work with and their focus is different.


Regarding building larger apps. Panel is young and the focus has been to build reference documentation for the components so far.

My attempt to showcase how awesome Panel is for building larger apps has been to build awesome-panel-org. You can find lots of examples there of apps with code. And also an awesome-list linking to other awesome panel apps. I hope the site will develop into a “best practice” for building larger apps with Panel.

image

Hi Marc! Thanks for pointing me to the awesome panel website, I’ve always been really impressed by your work with Streamlit and Panel.

Digging more through the Panel website and the awesome panel website, panel indeed looks extremely powerful, almost too powerful! I’m struggling to locate a starting point for implementing something like the code above, and the API info here seems to hint there are several “routes” one can take to achieve this. From the github repo for the awesome panel website, it looks like you’ve created your own classes/types to more easily develop larger apps?

Is there a similar enough example (to mine) available on your awesome panel website I could piggy-back off of? Should I create a top-level “Parametrized” class, and make any widgets extend that?