Sam De Meyer
Sam De Meyer

Reputation: 2381

How to pass arbitrary `json` data from python to bokeh using `CustomJS`

I am working on a bokeh visualisation, one requirement is that it runs without a server, as such I am using CustomJS to implement interactivity on the client side using javascript. Passing ColumnDataSources to CustomJS works fine to update plots interactively.

What I cannot do however, is just embed some arbitrary json data into the output html. See the self contained example below:

from bokeh.io import output_file
from bokeh.layouts import widgetbox
from bokeh.models import CustomJS
from bokeh.models.widgets import Div, Button
from bokeh.plotting import show

data_for_client = {
    "x": 3,
    "y": {
        "foo": [1, 2, 3],
        "bar": {"some_bar": "doge", "other bar": "much json"}
    },
    "z": [[1, 2], [3, 4]]
}

callback = CustomJS(args={"data_for_client": data_for_client}, code="""
    console.log(data_for_client);
""")

content = widgetbox(
    Button(label="click here", callback=callback),
    Div(text="some complicated interactive stuff based on json content here")
)

output_file("issue.html")
show(content)

In my case, the data_for_client is a complicated deeply nested json, different parts of this json are used depending on user interaction with some widgets.

I do not need to make any modifications to the json data based on user interaction, or attach any listeners or anything special. I just need the content of the json value to be available in the CustomJS callback.

For simple column data, I can wrap my data in a ColumnDataSource and pass that as an arg to CustomJS, however I do not find anything similar for plain json data. The code above fails with message:

ValueError: expected an element of Dict(String, Instance(Model)), got {'data_for_client': {'x': 3, 'y': {'foo': [1, 2, 3], 'bar': {'some_bar': 'doge', 'other bar': 'much json'}}, 'z': [[1, 2], [3, 4]]}}

Upvotes: 1

Views: 1958

Answers (2)

Seb
Seb

Reputation: 1775

You can pass it as a string, I would expect that there is stuff in javascript to read json data from a json string, I know this exists in python at least.

from bokeh.io import show
from bokeh.layouts import widgetbox
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.models.widgets import Div, Button
import json

data_for_client = {
    "x": [3],
    "y": {
            "foo": [1, 2, 3],
            "bar": {"some_bar": "doge", "other bar": "much json"}
        },
    "z": [[1, 2], [3, 4]],
}

json_string = json.dumps(data_for_client)

DumButton = Button(label=json_string)

# Here is the change:
callback = CustomJS(args={"db":DumButton}, code="""
    console.log(db.label);
    var test=JSON.parse(db.label);
    console.log(test);
    for(key in test){console.log(test[key])}
    """)

content = widgetbox(
    Button(label="click here", callback=callback),
)

show(content)

Upvotes: 2

User that hates AI
User that hates AI

Reputation: 468

You need to change the first data_for_client to "data_for_client" in the related line, because otherwise you cannot use a dictionary ({...}) as a dictionary-key:

from bokeh.io import output_file
from bokeh.layouts import widgetbox
from bokeh.models import CustomJS
from bokeh.models.widgets import Div, Button
from bokeh.plotting import show

data_for_client = {
    "x": 3,
    "y": {
        "foo": [1, 2, 3],
        "bar": {"some_bar": "doge", "other bar": "much json"}
    },
    "z": [[1, 2], [3, 4]]
}

# Here is the change:
callback = CustomJS(args={"data_for_client": data_for_client}, code="""
    console.log(data_for_client); 
""")

content = widgetbox(
    Button(label="click here", callback=callback),
    Div(text="some complicated interactive stuff based on json content here")
)

output_file("issue.html")
show(content)

Upvotes: 2

Related Questions