![](/img/trans.png)
[英]Updating data in Bokeh plot client side using Django channels websocket
[英]Bokeh: Generate graph server side, update graph from JS client side (change data source, axes ...)
我刚刚在 Django 网站上试用了 Bokeh。 那里有很多很好的教程,而且很容易快速运行示例。 例如,我有一个 Django 视图,它提供如下内容:
...
# This is part of a broader Django view, simply adding a graph to it.
# Fetch the x and y data for a histogram using a custom function.
# It takes a queryset, a field in that queryset and returns, two lists one
# containing the unique values that some field took on and the second containing
# the count of times it took on that value.
(values, frequency) = FrequencyData(some_queryset, "some field")
p = figure(height=350,
x_axis_label="Count of Players",
y_axis_label="Number of Events",
background_fill_alpha=0,
border_fill_alpha=0,
tools="pan,wheel_zoom,box_zoom,save,reset")
p.xaxis.ticker = values
p.yaxis.ticker = list(range(max(frequency) + 1))
p.toolbar.logo = None
p.vbar(x=values, top=frequency, width=0.9)
p.y_range.start = 0
graph_script, graph_div = components(p)
context.update({"graph_script": graph_script,"graph_div": graph_div})
...
然后在 Django 模板中,我只有:
{{ graph_div | safe }}
{{ graph_script| safe }}
我有一个可爱的数据直方图,否则会在该视图的表格中显示。 我喜欢。
现在,与许多视图一样,同一个视图有一堆主要用于过滤数据的设置和一个刷新按钮。 刷新按钮触发 AJAX 回调到视图,该视图及时提供 JSON 中的日期,并且页面上的表格和其他所有内容都在 JS 中更新以反映新数据。 所有相当标准的东西。
las,图表不会更新。 这不是新的,我可以找到很多关于它的问题和答案。 但是,紧缩是他们还没有让我满意,而且我已经用完了可找到的答案。 我查看了 Bokeh 文档,但还没有找到乐趣。
因此,要分解桌面上已经不完全令我满意的解决方案:
运行散景服务器。 这将与 Django UWSGI 服务并行,它有一些好处,但恕我直言,这里有点矫枉过正。 我知道它使用 Tornado(和 web 套接字)与客户端 JS 进行通信(包含在{{ graph_script| safe }}
中,当然根据所有文档,它是来自 CDN 的依赖项)。 但这是我的 Django 应用程序获得了 AJAX 对新数据的请求,即使我想添加这一点基础设施(当然我以后可能会有其他好处)我仍然坚持使用 Django 应用程序告诉 Bokeh 服务器该图有新数据。 所以他们也需要谈谈。
使用 散景。 las,这看起来像是向构建图形客户端的范式转变。 确实更具吸引力和回退选项,我只是将数据发送到客户端(通过页面加载时呈现的模板和更新时之后的 AJAX)并呈现图形客户端。 缺点是帮助中的警告(这正在开发中并且可能会更改),以及在某种意义上找不到似乎符合标准 Bokeh 演示和教程的更简单解决方案的挫败感。
配置图形以使用AjaxDataSource 。 老实说,我不太确定如何将它与上面的示例集成,但还没有对此进行深入研究,因为它不令人满意,在它自己的 Ajax 调用和轮询间隔上工作。 当用户更改设置并请求更新时,我已经按需拨打了 Ajax 电话,我希望图表从我的电话返回中获取数据,而不是它自己的数据。 基本上,我将此处的图表插入到现有上下文中,并采用其操作方法。
使用CustomJS 回调。 这真的很有希望。 在服务器端,我可以访问具有js_on_change
方法的对象,但是帮助没有提供关于如何从自己的 JS 代码(尚未访问任何 Bokeh 对象)触发 CustomJS 定义的此类回调的真正线索 function。 简而言之,它是与非 Bokeh 小部件的交互,页面上现有的刷新按钮执行 AJAX 调用,获取数据,现在想要更新图形。 但是那个小部件没有(看似很好的文档或我发现的)用新数据和轴刻度等重新配置图形的方法。
那么我理想的解决方案是什么样的呢? 那么,在我上面的 Python 视图中,我添加了数据:
# as above ... except:
context.update({"graph_script": graph_script,"graph_div": graph_div, "values": values, "frequency": frequency})
然后在 JavaScript 中的客户端,当我获取表数据并对其进行处理以更新表时,我也可以通过某种方式对“graph_div”说类似的话:“嘿,我有新的 x 和 y 数据供您使用以及轴配置”。
简而言之,一点 BokehJS(能够引用图形客户端并更新它)和一点标准 Bokeh(为第一页加载生成图形服务器端)。
这当然需要graph_script
(由bokeh.embed.components 生成)包含图形 object 的名称(由组件提供),它向 JS 公开图形的 BokehJS 类接口。
我可以查看graph_div
并看到它被赋予了一个id
和一个data-root-id
:
<div class="bk-root" id="ce45dc38-0977-4d2c-a8ae-033dafad5fc8" data-root-id="1078">
看似由 Bokeh 分配,当然需要 BokehJS 找到我猜的 div。 我同样可以查看graph_script
并看到它确实在根目录中嵌入了两个变量docs_json
和render_items
并且还打破了浏览器调试器并检查并看到有一个很好的全局 object Bokeh
非常可用,非常希望它可以用于当它通过我们自己的 Ajax 调用(提供更多内容)到达时,将 JS 中的新数据和轴配置提供给现有的 Bokeh 图。
我怀疑如果我在这里找到解决方案,或者有人可以帮助我找到一个解决方案,那么我可能会写一个小教程是值得的; ;-)
我使用你的博客在 django 中使用 json 请求找到答案。我的 web 页面是 bokeh.traimaocv.fr/index/slider slider 向 django 服务器发送请求并使用 8814095863 获取 x 和 y 数据
def get_arg_post(request, list_var):
"""
Récupération des données POST dans une requète
request --> objet HTTPrequest
list-var --> liste des variables à extraire de la requête
valeur retour --> booléen et liste des valeurs
booléen False si les valeurs n'ont pas été trouvées
"""
resultat = []
if len(request.POST) > 0:
for v in list_var:
if v in request.POST:
try:
resultat.append(request.POST[v])
except:
return False, []
else:
return False, []
else:
return False, []
return True, resultat
@csrf_protect
def sinus_slider(request: HttpRequest) -> HttpResponse:
freq = 1
b_ok, val = get_arg_post(request, ['freq'])
if b_ok:
freq = float(val[0])
x = np.linspace(0, 10, 500)
y = np.sin(freq*x)+freq
source = ColumnDataSource(data=dict(x=x, y=y))
plot = figure(y_range=(-10, 10), width=400, height=400,title="Ma Courbe",name="Mes_donnees")
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6,name="Mon_sinus")
amp_slider = Slider(start=0.1, end=10, value=freq, step=.1, title="Amplitude")
callback = CustomJS(args=dict(source=source, amp=amp_slider),
code="""
var csrfToken = '';
var i=0;
var inputElems = document.querySelectorAll('input');
var reponse='';
for (i = 0; i < inputElems.length; ++i) {
if (inputElems[i].name === 'csrfmiddlewaretoken') {
csrfToken = inputElems[i].value;
break;
}
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/index/slider_change", true);
xhr.setRequestHeader('mode', 'same-origin');
var dataForm = new FormData();
dataForm.append('csrfmiddlewaretoken', csrfToken);
dataForm.append('freq', amp.value);
xhr.responseType = 'json';
xhr.onload = function() {
reponse = xhr.response
source.data.x = reponse['x'];
source.data.y = reponse['y'];
const plot = Bokeh.documents[0].get_model_by_name('Mes_donnees')
source.change.emit();
}
xhr.send(dataForm);
""")
amp_slider.js_on_change('value', callback)
layout = row(plot, column(amp_slider))
script1, div1 = components(layout, "Graphique")
pos = div1.find('data-root-id="')
id = int(div1[pos+14:pos+18])
#layout.update()
#script2, div2 = components(amp_slider, "slider freq")
#html2 = file_html(layout, CDN, "my plot")
#html2 = html2.replace("</head>","{% csrf_token %}</head>")
code_html = render(request,"sinus_slider.html", dict(script1=script1, div=div1))
return code_html
@csrf_protect
def sinus_slider_change(request: HttpRequest) -> HttpResponse:
freq = 1
b_ok, val = get_arg_post(request, ['freq'])
if b_ok:
freq = float(val[0])
x = np.linspace(0, 10, 500)
y = np.sin(freq*x)+freq
return JsonResponse(dict(x=x.tolist(),y=y.tolist()))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.