E. Erfan
E. Erfan

Reputation: 1411

how to combine bokeh widgets and html tags?

I have made a simple form with bokeh which looks as follows:

Form made by bokeh

I would like to make a downloadable link for the user, that when the user clicked on the button, an excel sheet can be downloaded. I tried adding an html a tag to the bokeh form, and set its href attribute to the address of the Excel sheet on the server. But I understood it caused SyntaxError.

The code is as follows:

from bokeh.io import output_file, show
from bokeh.layouts import widgetbox
from bokeh.models.widgets import TextInput, Button, Div
from bokeh.layouts import layout, column, row
from bokeh.io import curdoc ## to assign callback to widget

from bokeh import events    ## for event handling
from bokeh.models import CustomJS

import numpy as np
import pandas as pd

text_input_mean = TextInput(value="0.0", title="Enter mean:")
text_input_vaiance = TextInput(value="0.0", title="Enter variance:")
text_input_rows = TextInput(value="5", title="Enter num rows:")
text_input_columns = TextInput(value="5", title="Enter num columns:")

button = Button(label = "Generate Dataframe", button_type = "success")

text_output = TextInput(title = 'Python result is shown here: ')

div = Div(text="""Making a form with bokeh mainly to handle events.""",
width=500, height=50)

layout = column(div, row(text_input_mean, text_input_vaiance),   row(text_input_rows, text_input_columns),
            button, text_output)


def my_text_input_handler(attr, old, new):
    print("Previous label: " + old)
    print("Updated label: " + new)
text_input_mean.on_change("value", my_text_input_handler)

def button_click_handler():
    text_output.__setattr__('value', str(text_input_mean.value))
    text_output.value = str(text_input_mean.value)


def generate_normal_df():
    mean = text_input_mean.value
    variance = text_input_vaiance.value
    row_num = x
    col_num = y
    return pd.DataFrame(np.random.normal(loc = mean, scale = variance,  size=(row_num, col_num)))

button.on_click(button_click_handler)
curdoc().add_root(layout)

# this part causes error!
<html>
<b> End of page </b>
# a tag goes here
<\html>

I haven't implemented how to get the address of the generated excel sheet yet. My idea is to save the dataframe as an Excel sheet; by using os library get its adress, and set the href attribute of a tag. But the html part in this .python form causes error.

Can you please kindly mention what the solution should be? Is there a way to write html in bokeh form? An a tag is not available as a widget for instance. Thank you for your attention.

Upvotes: 0

Views: 979

Answers (1)

Tony
Tony

Reputation: 8287

You can create a pure JS callback for your button and download the data as JSON without involving the server like this:

from bokeh.io import show
from bokeh.plotting import figure
from bokeh import events
from bokeh.models import CustomJS, Div, Button, RadioButtonGroup
from bokeh.layouts import column, row
import numpy as np

data = {'mean': np.arange(10), 'variance': np.random.random(10) }
button = Button(label = "Download JSON", button_type = "success")

js_download = """
var filename = 'results.json';
var blob = new Blob([JSON.stringify(data)], { type: 'text/json;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, filename);
} else {
var link = document.createElement("a");
if (link.download !== undefined) { // feature detection
    // Browsers that support HTML5 download attribute
    var url = URL.createObjectURL(blob);
    link.setAttribute("href", url);
    link.setAttribute("download", filename);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link); } }"""

button.callback = CustomJS(args = dict(data = data), code = js_download)
show(button)

Just pass the right object(s) to the callback to extract the data from (e.g. your dataframe). If you prefer to get the data from the server then the simplest way is to use JS fetch() method in your callback like in this example. In this case you need to run your code as: bokeh serve --show my_app_directory and you need there a /static directory where you save your file and serve it from back to the browser. Or otherwise you could use something like AjaxDataSource or pure AJAX in the JS callback to fetch json from the server and let the user download it as in example above (tested on Bokeh v1.0.4).

Upvotes: 2

Related Questions