Flask + Bokeh AjaxDataSource

fla*_*rel 11 flask bokeh

使用Flask + Bokeh AjaxDataSource苦苦挣扎:

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

@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])
Run Code Online (Sandbox Code Playgroud)

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

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

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

template = Template('''<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Streaming Example</title>
        {{ js_resources }}
        {{ css_resources }}
    </head>
    <body>
    {{ plot_div }}
    {{ plot_script }}
    </body>
</html>
''')

@app.route("/")
def simple():
    streaming=True
    source = AjaxDataSource(data_url="http://localhost:5000/data", 
                            polling_interval=1000, mode='append')

    fig = figure(title="Streaming Example")
    fig.line( 'x', 'y', source=source)

    js_resources = INLINE.render_js()
    css_resources = INLINE.render_css()

    script, div = components(fig, INLINE)

    html = template.render(
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources
    )

    return encode_utf8(html) 
Run Code Online (Sandbox Code Playgroud)

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

布赖恩

big*_*dot 11

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


更新:自Bokeh以来0.12.15,不需要下面描述的解决方法.AjaxDataSource应该在没有投诉的情况下流入一个空的CDS,前面没有空列.


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

Error: attempted to retrieve property array for nonexistent field 'x'
Run Code Online (Sandbox Code Playgroud)

这是workround的关键,这只是为了确保数据源确实有(空)列xy:

source.data = dict(x=[], y=[])
Run Code Online (Sandbox Code Playgroud)

下面有一个完整的工作脚本.我请你在Bokeh问题跟踪器上用这些信息提出问题,以便可以优先处理这个错误并修复


from flask import Flask, jsonify
from jinja2 import Template
import math

from bokeh.plotting import figure
from bokeh.models import AjaxDataSource
from bokeh.embed import components
from bokeh.resources import INLINE
from bokeh.util.string import encode_utf8


app = Flask(__name__)

x, y = 0, 0

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

template = Template('''<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Streaming Example</title>
        {{ js_resources }}
        {{ css_resources }}
    </head>
    <body>
    {{ plot_div }}
    {{ plot_script }}
    </body>
</html>
''')

@app.route("/")
def simple():
    streaming=True
    source = AjaxDataSource(data_url="http://localhost:5000/data",
                            polling_interval=1000, mode='append')

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

    fig = figure(title="Streaming Example")
    fig.line( 'x', 'y', source=source)

    js_resources = INLINE.render_js()
    css_resources = INLINE.render_css()

    script, div = components(fig, INLINE)

    html = template.render(
        plot_script=script,
        plot_div=div,
        js_resources=js_resources,
        css_resources=css_resources
    )

    return encode_utf8(html)

app.run(debug=True)
Run Code Online (Sandbox Code Playgroud)


tuo*_*tik 6

和OP一样,我也想使用带有Bokeh和Flask的AJAX.但是,AjaxDataSource我不想从服务器连续流式传输数据,而只是当用户与网页上的输入进行交互时,我想从服务器获取新数据.要做到这一点,我用bigreddot的回答为基础,改AjaxDataSourceColumnDataSource和里面添加一个jQuery AJAX调用CustomJS(下面的例子中已经使用Python 3.6.4,1.0.2瓶和背景虚化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)
Run Code Online (Sandbox Code Playgroud)