Flask + Bokeh AjaxDataSource

时间:2016-05-07 02:51:59

标签: flask bokeh

使用Flask + Bokeh AjaxDataSource挣扎:

我有一个返回json数据的函数:

var SIDEBAR = new function() {
    this.on = function(nameElement){
        this.menu = nameElement;
        return this;
    };
    /*more code*/
    this.getElements = function() {
        /*more code*/
        return [];
    };
     /*more code*/
    this.addElements = function() {
        var elementsData = this.getElements();
         /*more code*/
    };
}();

var sid = SIDEBAR.on('test');
sid.load();

我可以使用它与Bokeh AjaxDataSource一起创建流图:

@app.route("/data", methods=['POST'])
def get_x():
    global x, y
    x = x + 0.1
    y = math.sin(x)
    return flask.jsonify(x=[x], y=[y])

但是,当我尝试将其嵌入到烧录页面中时,AjaxDataSource不会查询服务器。该图无法呈现,没有错误。请注意,如果我使用静态图而不是AjaxDataSource,它会很好地绘制。这是相关的代码:

source = AjaxDataSource(data_url="http://localhost:5000/data", polling_interval=1000, mode='append')
p = figure()
p.line('x', 'y', source=source)                                                                       
show(p)

如果有人有任何想法,我会感激不尽。

布赖恩

2 个答案:

答案 0 :(得分:11)

首先,作为一个温和的建议,请始终发布完整的可运行的代码示例。只需几分钟就可以重现所有缺少必需的导入,因为一旦有可运行的脚本,只需要几秒钟来诊断。

更新:由于Bokeh 0.12.15不需要下面描述的解决方法。 AjaxDataSource应该在没有投诉的情况下流式传输到空白的CDS中,前面没有创建空列。

最近,一些BokehJS代码路径变得更加“严格”,几乎在每个实例中都很好,但是这似乎与AjaxDataSource的一个不良交互没有被注意到。 FWIW当我运行示例时,我确实在浏览器 JS控制台中看到错误:

Error: attempted to retrieve property array for nonexistent field 'x'

这是workround的关键,这只是为了确保数据源确实有({1}}和x的列(空):

y

下面有一个完整的工作脚本。我请你在Bokeh issue tracker上用这些信息提出问题,以便可以优先处理和修复此错误

source.data = dict(x=[], y=[])

答案 1 :(得分:6)

与OP一样,我也想将AJAX与Bokeh和Flask一起使用。但是,不是使用AjaxDataSource从服务器连续流式传输数据,而是仅当用户与网页上的输入进行交互时才希望从服务器获取新数据。为实现这一目标,我使用了bigreddot的answer作为基础,将AjaxDataSource更改为ColumnDataSource并在CustomJS内添加了一个jQuery AJAX调用(以下示例已经过使用Python 3.6.4,Flask 1.0.2和Bokeh 0.13.0创建:

import json

from flask import Flask, jsonify, request
from jinja2 import Template
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS, Select
from bokeh.embed import components
from bokeh.resources import INLINE
from bokeh.layouts import column
from bokeh.util.string import encode_utf8

app = Flask(__name__)

N_DATAPOINTS = 20
DEFAULT_VARIABLE = 'bar'
MY_DATABASE = {
    'foo': [i**1 for i in range(N_DATAPOINTS)],
    'bar': [i**2 for i in range(N_DATAPOINTS)],
    'baz': [i**3 for i in range(N_DATAPOINTS)]}


@app.route("/get_new_data", methods=['POST'])
def get_new_data():
    app.logger.info(
        "Browser sent the following via AJAX: %s", json.dumps(request.form))
    variable_to_return = request.form['please_return_data_of_this_variable']
    return jsonify({variable_to_return: MY_DATABASE[variable_to_return]})


SIMPLE_HTML_TEMPLATE = Template('''
<!DOCTYPE html>
<html>
    <head>
        <script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
        {{ js_resources }}
        {{ css_resources }}
    </head>
    <body>
    {{ plot_div }}
    {{ plot_script }}
    </body>
</html>
''')


@app.route("/")
def simple():
    x = range(N_DATAPOINTS)
    y = MY_DATABASE[DEFAULT_VARIABLE]

    source = ColumnDataSource(data=dict(x=x, y=y))

    plot = figure(title="Flask + JQuery AJAX in Bokeh CustomJS")
    plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)
    callback = CustomJS(args=dict(source=source), code="""
    var selected_value = cb_obj.value;
    var plot_data = source.data;

    jQuery.ajax({
        type: 'POST',
        url: '/get_new_data',
        data: {"please_return_data_of_this_variable": selected_value},
        dataType: 'json',
        success: function (json_from_server) {
            // alert(JSON.stringify(json_from_server));
            plot_data.y = json_from_server[selected_value];
            source.change.emit();
        },
        error: function() {
            alert("Oh no, something went wrong. Search for an error " +
                  "message in Flask log and browser developer tools.");
        }
    });
    """)

    select = Select(title="Select variable to visualize",
                    value=DEFAULT_VARIABLE,
                    options=list(MY_DATABASE.keys()),
                    callback=callback)

    layout = column(select, plot)
    script, div = components(layout)
    html = SIMPLE_HTML_TEMPLATE.render(
        plot_script=script,
        plot_div=div,
        js_resources=INLINE.render_js(),
        css_resources=INLINE.render_css())

    return encode_utf8(html)

app.run(debug=True, host="127.0.0.1", port=5002)