Flask + 散景 AjaxDataSource
Flask + Bokeh AjaxDataSource
努力使用 Flask + Bokeh AjaxDataSource:
我有一个函数 returns json data:
@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])
我可以将它与 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)
但是,当我尝试将其嵌入 flask 页面时,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)
如果有人有任何想法,我将不胜感激。
布莱恩
首先,作为一个温和的建议,请始终post 完成 运行可用的代码示例。花了几分钟时间来重现所有缺少的必要导入,因为一旦有 运行nable 脚本,诊断只需要几秒钟。
更新:由于 Bokeh 0.12.15
,下面描述的解决方法应该不需要。 AjaxDataSource
应该毫无怨言地流式传输到一个没有预先创建空列的空 CDS 中。
最近一些 BokehJS 代码路径变得更加 "strict" 这几乎在每个实例中都很好,但似乎这与 AjaxDataSource
留下了一个没有被注意到的不良交互。 FWIW 当我 运行 示例时,我确实在 浏览器 JS 控制台中看到错误:
Error: attempted to retrieve property array for nonexistent field 'x'
这是解决方法的关键,它只是为了确保数据源确实有 x
和 y
:
的(空)列
source.data = dict(x=[], y=[])
下面有一个完整的工作脚本。请使用此信息在 Bokeh issue tracker 上提出问题,以便优先处理和修复此错误
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)
与 OP 一样,我也想将 AJAX 与 Bokeh 和 Flask 一起使用。但是,我不想使用 AjaxDataSource
从服务器连续流式传输数据,而是希望仅当用户与网页上的输入交互时才从服务器获取新数据。为此,我以 bigreddot 的 为基础,将 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)
努力使用 Flask + Bokeh AjaxDataSource:
我有一个函数 returns json data:
@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])
我可以将它与 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)
但是,当我尝试将其嵌入 flask 页面时,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)
如果有人有任何想法,我将不胜感激。
布莱恩
首先,作为一个温和的建议,请始终post 完成 运行可用的代码示例。花了几分钟时间来重现所有缺少的必要导入,因为一旦有 运行nable 脚本,诊断只需要几秒钟。
更新:由于 Bokeh 0.12.15
,下面描述的解决方法应该不需要。 AjaxDataSource
应该毫无怨言地流式传输到一个没有预先创建空列的空 CDS 中。
最近一些 BokehJS 代码路径变得更加 "strict" 这几乎在每个实例中都很好,但似乎这与 AjaxDataSource
留下了一个没有被注意到的不良交互。 FWIW 当我 运行 示例时,我确实在 浏览器 JS 控制台中看到错误:
Error: attempted to retrieve property array for nonexistent field 'x'
这是解决方法的关键,它只是为了确保数据源确实有 x
和 y
:
source.data = dict(x=[], y=[])
下面有一个完整的工作脚本。请使用此信息在 Bokeh issue tracker 上提出问题,以便优先处理和修复此错误
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)
与 OP 一样,我也想将 AJAX 与 Bokeh 和 Flask 一起使用。但是,我不想使用 AjaxDataSource
从服务器连续流式传输数据,而是希望仅当用户与网页上的输入交互时才从服务器获取新数据。为此,我以 bigreddot 的 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)