我用bokeh制作了一个简单的表格,如下所示:
我想为用户创建一个可下载的链接,当用户单击按钮时,可以下载一个excel表格。我尝试将html a 标记添加到bokeh表单中,并将其href属性设置为服务器上Excel工作表的地址。但我知道这是引起SyntaxError的原因。
代码如下:
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>
我还没有实现如何获取生成的Excel工作表的地址。我的想法是将数据框另存为Excel工作表;通过使用os库获取其地址,并设置标签的href属性。但是这种.python格式的html部分会导致错误。
能否请您提出解决方案?有没有办法以bokeh形式编写html?例如, a 标签不能用作窗口小部件。谢谢您的关注。
答案 0 :(得分:1)
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).