我需要在网络浏览器上显示来自流数据的嵌入的多个图表,以便快速显示。我对Javascript的了解是空的,我想在此测试之前知道一点Bokeh,但看起来我没有。
我对你可以找到的几十个例子感到非常困惑,但是不能使用最后一个Bokeh库(0.12.6)。 已经看了Streaming two line graphs using bokeh,但是我需要使用Flask。
我帮助了解解决问题的正确方向,而不是重写我的代码。
到目前为止,我找到了一个解决方案,但它耗费了大量CPU(这是一个正常工作的代码,很抱歉:我已将其剥离了)。同样,它只刷新整个页面而不是图形。在Chromium 58(Linux)中没问题,在Firefox 53(Linux)中非常慢。
将Bokeh更改为另一个库是一种选择。烧瓶是强制性的。
app.py:
from bokeh.models import (FactorRange, LinearAxis, Grid, Range1d)
from bokeh.models.glyphs import Line
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models.sources import ColumnDataSource
from flask import Flask, render_template
import random
app = Flask(__name__)
class SingleLine():
def __init__(self, color, elem_number):
self.color = color
self.elem_number = elem_number
self.data = {"time": [], "value": []}
for i in range(1, elem_number + 1):
self.data['time'].append(i)
self.data['value'].append(random.randint(1,100))
def create_line_plot(self, plot, x_name, y_name):
source = ColumnDataSource(self.data)
glyph = Line(x=x_name, y=y_name, line_color=self.color)
plot.add_glyph(source, glyph)
#----------------------------------------------------------------------------------
class CompleteGraph():
def __init__(self, lines_list, x_name, y_name, title, height=300, width=1000):
self.lines_list = lines_list
self.x_name = x_name
self.y_name = y_name
self.title = title
self.height = height
self.width = width
def create_graph(self):
xdr = FactorRange(factors=self.lines_list[0].data[self.x_name])
ydr = Range1d(start=0, end=max(self.lines_list[0].data[self.y_name]) * 1.5)
plot = figure(title=self.title, x_range=xdr, y_range=ydr,
plot_width=self.width, plot_height=self.height,
h_symmetry=False, v_symmetry=False,
tools=[],
responsive=True)
for l in self.lines_list:
l.create_line_plot(plot, self.x_name, self.y_name)
xaxis = LinearAxis()
yaxis = LinearAxis()
plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker))
plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker))
return components(plot)
@app.route("/")
def chart():
elem_number = 30
line1 = SingleLine(color="#ff0000", elem_number=elem_number)
line2 = SingleLine(color="#00ff00", elem_number=elem_number)
line3 = SingleLine(color="#00ff00", elem_number=elem_number)
# first graph
lines_list = [line1, line2]
lg1 = CompleteGraph(lines_list, "time", "value", "title graph 1")
# second graph
lines_list = [line1, line3]
lg2 = CompleteGraph(lines_list, "time", "value", "title graph 2")
script1, div1 = lg1.create_graph()
script2, div2 = lg2.create_graph()
return render_template("test_stackoverflow.html",
div1=div1, script1=script1,
div2=div2, script2=script2,
)
if __name__ == "__main__":
app.run(port=5000, debug=True)
和相应的模板:
test_stackoverflow.html
<html>
<head>
<style>
#wrapper { display: flex; }
#left { flex: 0 0 50%; }
#right { flex: 1; }
#wide { flex: 0 0 90% }
</style>
<title>Multiple realtime charts with Bokeh</title>
<link href="http://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min" rel="stylesheet">
<link href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.css" rel="stylesheet">
<script src="http://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
(function worker() {
$.ajax({
url: '/',
success: function(data) {
location.reload();
},
complete: function() {
// Schedule the next request when complete
setTimeout(worker, 1000);
}
});
})();
</script>
</head>
<body>
<div id="wrapper">
<div id="left">
<h1>left graph</h1>
{{ div1 | safe }}
{{ script1 | safe }}
</div>
<div id="right">
<h1>right graph</h1>
{{ div2 | safe }}
{{ script2 | safe }}
</div>
</div>
</body>
</html>
欢迎任何帮助。
答案 0 :(得分:3)
如果有人对它感兴趣,这里有一个工作(非常原始)的例子,基于bokeh /examples/howto/ajax_source.py
<强> app.py:强>
import numpy as np
from datetime import timedelta
from functools import update_wrapper, wraps
from math import sin, cos
from random import random
from six import string_types
from bokeh.plotting import figure
from bokeh.models.sources import AjaxDataSource
from bokeh.embed import components
from flask import Flask, jsonify, make_response, request, current_app, render_template
#########################################################
# Flask server related
#
# The following code has no relation to bokeh and it's only
# purpose is to serve data to the AjaxDataSource instantiated
# previously. Flask just happens to be one of the python
# web frameworks that makes it's easy and concise to do so
#########################################################
def crossdomain(origin=None, methods=None, headers=None,
max_age=21600, attach_to_all=True,
automatic_options=True):
"""
Decorator to set crossdomain configuration on a Flask view
For more details about it refer to:
http://flask.pocoo.org/snippets/56/
"""
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, string_types):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, string_types):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
@wraps(f)
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
requested_headers = request.headers.get(
'Access-Control-Request-Headers'
)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
elif requested_headers:
h['Access-Control-Allow-Headers'] = requested_headers
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
app = Flask(__name__)
x = list(np.arange(0, 6, 0.1))
y1 = [sin(xx) + random() for xx in x]
y2 = [sin(xx) + random() for xx in x]
@app.route('/data', methods=['GET', 'OPTIONS', 'POST'])
@crossdomain(origin="*", methods=['GET', 'POST'], headers=None)
def hello_world():
x.append(x[-1]+0.1)
y1.append(sin(x[-1])+random())
y2.append(cos(x[-1])+random())
return jsonify(x=x[-500:],
y1=y1[-500:],
y2=y2[-500:],
)
@app.route("/")
def main_graph():
source = AjaxDataSource(data=dict(x=[], y1=[], y2=[], y3=[]),
data_url='http://127.0.0.1:5050/data',
polling_interval=1000)
p = figure()
p.line(x='x', y='y1', source=source)
p.x_range.follow = "end"
p.x_range.follow_interval = 10
script1, div1 = components(p)
# -------------------------------------
p = figure()
p.line(x='x', y='y2', source=source)
p.x_range.follow = "end"
p.x_range.follow_interval = 10
script2, div2 = components(p)
return render_template("test_stackoverflow.html",
div1=div1, script1=script1,
div2=div2, script2=script2,
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5050, debug=True)
<强> test_stackoverflow.html:强>
<!DOCTYPE html>
<html>
<head>
<style>
#wrapper { display: flex; }
#left { flex: 0 0 50%; }
#right { flex: 1; }
#wide { flex: 0 0 90% }
</style>
<title>Multiple realtime charts with Bokeh</title>
<link href="http://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min" rel="stylesheet">
<link href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.css" rel="stylesheet">
<script src="http://cdn.pydata.org/bokeh/release/bokeh-0.12.6.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.6.min.js"></script>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
<div id="wrapper">
<div id="left">
<h1>left graph</h1>
{{ div1 | safe }}
{{ script1 | safe }}
</div>
<div id="right">
<h1>right graph</h1>
{{ div2 | safe }}
{{ script2 | safe }}
</div>
</div>
</body>
</html>