Skip to main content

Additional widgets for Bokeh

Project description

BokehGarden

BokehGarden is a Bokeh based web application framework focused on plotting heavy interactive applications.

Additionally, it provides several improved widgets for Bokeh, that can be used both within the framework and in a standalone bokeh app:

  • Interactive colorbar
    • That allows adjusting the color map by pan and zoom
  • File download button
    • That goes directly over http (not websocket)
  • File upload field
    • That goes directly over http (not websocket), meaning that it does not have a size limit, nor does it encode the file as base64.
  • Autocomplete input field
    • That fixes the text entry vs drop down selection problem
  • Drop down menu
    • That encodes the entry id:s as json, allowing e.g. integers to be used as id:s, not just strings.

Example application

bokeh serve examples/weather_data_explorer.py

Framework usage

The BokehGarden application framework consists of a very old idea, applied to Bokeh, combined with a small tool. The simple idea is to create complex UI element from simpler ones by subclassing Bokeh widget classes. In the subclass, the __init__() method is used to populate the widget with parts and bind callbacks.

class MyForm(bokeh_garden.application.AppWidget, bokeh.layouts.Column):
    def __init__(self, app=None, **kw):
        self._app = app

        self._foo = bokeh.models.Slider(title="Select a value for foo", value=0, start=0, end=100, step=1)
        self._bar = bokeh.models.NumericInput(title="Bar value", mode="float", value=10)
        self._submit_button = bokeh.models.Button(label="Submit", button_type="primary")
        self._submit_button.on_event(bokeh.events.ButtonClick, self.on_submit)
        
        bokeh.layouts.Column.__init__(
          self, self._foo, self._bar, self._submit_button, **kw)

    def on_submit(self, event):
        print("Submitted values:", self._foo.value, self._bar.value)

The above widget could be used as any other Bokeh widget in your plots.

Additionally, it could be used inside another widget of the same type, to recursively build up complex interfaces. But there is a better way: Layouts and overlays.

Layouts

A layout is a simple dictionary based data structure that defines the layout of an application declaratively. It is equivalent to instantiating widgets, sending them as parameters when instantiating other widgets to gradually build up the application layout, just like you normally do in bokeh. However, since it's data, not code, it's easy to store and modify the structure. Example layout data as python code:

layout = {"widget": "bokeh_garden.tabs.Tabs",
          "Data": {"widget": "weather.DataLoaderForm"},
          "Sea": {"widget": "bokeh.models.layouts.Column",
                  "children": [{"widget": "weather.WavesPlot", "unit": "kn"},
                               {"widget": "weather.CurentsPlot", "unit": "kn"}]},
          "Air": {"widget": "bokeh.models.layouts.Row",
                  "children": [{"widget": "weather.PressurePlot", "tags": ["wind_map"]},
                               {"widget": "weather.WindPlot", "tags": ["wind_map"]}]}}

At each level, the layout data structure consists of a dictionary

{"widget": "modulename.submodulename.classname", "initparamname1": "initparamvalue1", "initparamname2": "initparamvalue2", ... } 

with the key widget holding the name (and module path) of a widget class, and other keys being used to generate the parameters to __init__() for that class. For scalar values, these are just passed as-is, but for dictionaries and lists, they are first instantiated the same way recursively.

__init__() will also receive an instance of bokeh_garden.application.Application as a first argument. This instance is shared among all widgets created using the layout. The layout above would translate into

layout_instance = bokeh_garden.tabs.Tabs(
    app,
    Data=weather.DataLoaderForm(app),
    Sea=bokeh.models.layouts.Column(
        *[weather.WavesPlot(app, unit="kn"),
          weather.CurentsPlot(app, unit="kn")]),
    Air=bokeh.models.layouts.Row(
        *[weather.PressurePlot(app, tags=["wind_map"]),
          weather.WindPlot(app, tags=["wind_map"])]))

For larger applications is recomended to store this data structure as a separate yaml file. This makes getting a quick overview over the layout easier, as well as eases merging layout changes in git. Here's the same data as above but in yaml syntax:

layout:
  widget: bokeh_garden.tabs.Tabs
  Data:
    widget: weather.DataLoaderForm
  Sea:
    widget: bokeh.models.layouts.Column
    children:
      - widget: weather.WavesPlot
        unit: kn
      - widget: weather.CurentsPlot
        unit: kn
  Air:
    widget: bokeh.models.layouts.Row
    children:
      - widget: weather.PressurePlot
        tags: ["wind_map"]
      - widget: weather.WindPlot
        tags: ["wind_map"]

Overlays

The layout structure above lets you build complex applications from highly independent widgets. But it has one drawback: What if you need to make widgets in different parts of the application interact? Overlays solve this problem.

Overlays are ordinary python classes that has access to the document and the widget tree, can query the widget tree and modify the widgets in it by e.g. binding callbacks or adding drawing operations (layers) to plots.

class WindMapLinkedMapExtentsOverlay(object):
    def __init__(self, app):
        self.app = app

        x_range = None
        y_range = None
        for figure in self.app.doc.select({"tags": ["wind_map"]}):
            if x_range is None:
                x_range = figure.x_range
                y_range = figure.y_range
            else:
                figure.x_range = x_range
                figure.y_range = y_range

overlays = [{"widget": WindMapLinkedMapExtentsOverlay},
            ...]

Instantiating your layout

Once you have built a layout and defined your overlays, you can instantiate them as a Bokeh application using the following code (notebook version):

bokeh.io.output_notebook()
application = bokeh_garden.application.Application(layout, overlays)
bokeh.io.show(application)

or (application file for bokeh serve):

application = bokeh_garden.application.Application(layout, overlays)
application(bokeh.plotting.curdoc())

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bokeh_garden-0.0.15.tar.gz (26.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bokeh_garden-0.0.15-py3-none-any.whl (32.1 kB view details)

Uploaded Python 3

File details

Details for the file bokeh_garden-0.0.15.tar.gz.

File metadata

  • Download URL: bokeh_garden-0.0.15.tar.gz
  • Upload date:
  • Size: 26.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for bokeh_garden-0.0.15.tar.gz
Algorithm Hash digest
SHA256 0c8c28bc16914c5f90fdfd3380107bd0fd05577ff88726604e37405331a30e41
MD5 f96a159b646f419f63e0e27376ec0e04
BLAKE2b-256 ce2511406872cc7353fc5771fa982bff873f7936ab71afd0d09e6c3ffb566385

See more details on using hashes here.

File details

Details for the file bokeh_garden-0.0.15-py3-none-any.whl.

File metadata

  • Download URL: bokeh_garden-0.0.15-py3-none-any.whl
  • Upload date:
  • Size: 32.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for bokeh_garden-0.0.15-py3-none-any.whl
Algorithm Hash digest
SHA256 9621831f29b171cf4d4a1ec32ea47d1b0c5a563398a01603b6b85c46cc46fdf8
MD5 38ff5f5f1281b24b2a2772a7b78275e3
BLAKE2b-256 cebbf762bf1860a60cd1747c7ef125f199b8d2024fedf2febf0cfbf6c84c616b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page