Unable to remove callbacks in Bokeh Server (ValueError: callback already ran or was already removed, cannot be removed again)
Unable to remove callbacks in Bokeh Server (ValueError: callback already ran or was already removed, cannot be removed again)
我将 Bokeh Server 与 Django 结合使用来制作一些图表的动画,这些图表显示多个系列随时间的变化。
我目前正在尝试使滑块自动化以推进时间序列。
不幸的是,我遇到了两个问题(可能是相关的)。首先是虽然动画开始(滑块移动并且按钮上的 'play' 更改为 'pause'),但图表没有更新。第二个是任何停止滑块的尝试(单击 'pause' 按钮)都会引发错误:
ValueError: callback already ran or was already removed, cannot be removed again
滑块继续移动。
Django 正在向服务器请求 server_document,将 'pk' 作为参数传递:
views.py
from django.shortcuts import render
from django.http import HttpResponse
from bokeh.embed import server_document
def getchart(request, pk):
key = pk
script = server_document(url="http://localhost:5006/bokeh_server",
arguments={"pk": pk,
},
)
return render(request, 'testserver.html', {'script':script})
bokeh 服务器收到请求,找到与 'pk' 关联的数据,然后构建带有动画的图表。
main.py
from bokeh.io import curdoc
from copy import deepcopy
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import ColumnDataSource, Slider, Button
from bokeh.models.widgets import TableColumn
from bokeh.layouts import column, row
"""
Get arguments from request
"""
args = curdoc().session_context.request.arguments
pk = int(args.get('pk')[0])
"""
get data for graph from file and initialise variables
...
Replaced with example data in data_dict
"""
data_dict={'xdata':[[0, 1, 2, 4, 5, 6, 10, 11, 12], [4, 8, 16, 0, 13, 21, -3, 9, 21]],
'ydata':[[4, 8, 16, 0, 13, 21, -3, 9, 21], [0, 1, 2, 4, 5, 6, 10, 11, 12]]}
no_of_series = len(data_dict.get('xdata'))
length_of_series = len(data_dict.get('xdata')[0])
"""
Initialise ColumnDataSources
"""
graph_source = ColumnDataSource(data=deepcopy(data_dict)) #current data that will be displayed
"""
set graph_source to empty at start
"""
def initialise_graph_data(source):
fullxdata = source.data['xdata']
fullydata = source.data['ydata']
i = 0
while i < len(full_xdata):
source.data['xdata'][i] = fullxdata[i][0:1]
source.data['ydata'][i] = fullydata[i][0:1]
i += 1
initialise_graph_data(graph_source)
"""
format graph
"""
tools="pan, wheel_zoom, reset"
p = figure(tools=tools)
p.multi_line(xs='xdata', ys='ydata', source=graph_source,)
"""
Interactive slider
"""
def animate_update():
tick = slider.value + 1
slider.value = tick
def slider_update(attr, old, new):
tick = slider.value
"""
create display data for each series, starting at 0 and ending at the current tick value
"""
i=0
while i < no_of_series:
graph_source.data['xdata'][i] = data_dict['xdata'][i][0:tick]
graph_source.data['ydata'][i] = data_dict['ydata'][i][0:tick]
i += 1
slider = Slider(start=0, end=length_of_series, value=0, step=1)
slider.on_change('value', slider_update)
def animate():
if button.label == "► Play":
button.label = "❚❚ Pause"
curdoc().add_periodic_callback(animate_update, 200)
else:
button.label = "► Play"
curdoc().remove_periodic_callback(animate_update)
button = Button(label="► Play", width=60)
button.on_click(animate)
layout = row(column(p), button, slider)
curdoc().add_root(layout)
print(graph_source.data['xdata'][i])
在 slider_update()
的末尾,很明显源数据正在按预期更新,但图表没有刷新。
我认为问题是由 main.py 中多次使用 curdoc()
引起的,或者我误用了 server_document()
views.py
您的 main.py
代码有多个错误:
- 相同的列有不同的名称:
xdata
、x_data
、x
- 您不能就地更新数据源列。最好使用
stream
、patch
,使用 ds.data['x'] = ...
更改单个列,或使用 ds.data = ...
更改完整数据。使用后者,您将能够使用字典理解并避免完全存储 no_of_series
remove_periodic_callback
接受 add_periodic_callback
返回的回调 ID
修复这些后,您的代码就可以工作了。
我将 Bokeh Server 与 Django 结合使用来制作一些图表的动画,这些图表显示多个系列随时间的变化。 我目前正在尝试使滑块自动化以推进时间序列。
不幸的是,我遇到了两个问题(可能是相关的)。首先是虽然动画开始(滑块移动并且按钮上的 'play' 更改为 'pause'),但图表没有更新。第二个是任何停止滑块的尝试(单击 'pause' 按钮)都会引发错误:
ValueError: callback already ran or was already removed, cannot be removed again
滑块继续移动。
Django 正在向服务器请求 server_document,将 'pk' 作为参数传递:
views.py
from django.shortcuts import render
from django.http import HttpResponse
from bokeh.embed import server_document
def getchart(request, pk):
key = pk
script = server_document(url="http://localhost:5006/bokeh_server",
arguments={"pk": pk,
},
)
return render(request, 'testserver.html', {'script':script})
bokeh 服务器收到请求,找到与 'pk' 关联的数据,然后构建带有动画的图表。
main.py
from bokeh.io import curdoc
from copy import deepcopy
from bokeh.plotting import figure
from bokeh.embed import components
from bokeh.models import ColumnDataSource, Slider, Button
from bokeh.models.widgets import TableColumn
from bokeh.layouts import column, row
"""
Get arguments from request
"""
args = curdoc().session_context.request.arguments
pk = int(args.get('pk')[0])
"""
get data for graph from file and initialise variables
...
Replaced with example data in data_dict
"""
data_dict={'xdata':[[0, 1, 2, 4, 5, 6, 10, 11, 12], [4, 8, 16, 0, 13, 21, -3, 9, 21]],
'ydata':[[4, 8, 16, 0, 13, 21, -3, 9, 21], [0, 1, 2, 4, 5, 6, 10, 11, 12]]}
no_of_series = len(data_dict.get('xdata'))
length_of_series = len(data_dict.get('xdata')[0])
"""
Initialise ColumnDataSources
"""
graph_source = ColumnDataSource(data=deepcopy(data_dict)) #current data that will be displayed
"""
set graph_source to empty at start
"""
def initialise_graph_data(source):
fullxdata = source.data['xdata']
fullydata = source.data['ydata']
i = 0
while i < len(full_xdata):
source.data['xdata'][i] = fullxdata[i][0:1]
source.data['ydata'][i] = fullydata[i][0:1]
i += 1
initialise_graph_data(graph_source)
"""
format graph
"""
tools="pan, wheel_zoom, reset"
p = figure(tools=tools)
p.multi_line(xs='xdata', ys='ydata', source=graph_source,)
"""
Interactive slider
"""
def animate_update():
tick = slider.value + 1
slider.value = tick
def slider_update(attr, old, new):
tick = slider.value
"""
create display data for each series, starting at 0 and ending at the current tick value
"""
i=0
while i < no_of_series:
graph_source.data['xdata'][i] = data_dict['xdata'][i][0:tick]
graph_source.data['ydata'][i] = data_dict['ydata'][i][0:tick]
i += 1
slider = Slider(start=0, end=length_of_series, value=0, step=1)
slider.on_change('value', slider_update)
def animate():
if button.label == "► Play":
button.label = "❚❚ Pause"
curdoc().add_periodic_callback(animate_update, 200)
else:
button.label = "► Play"
curdoc().remove_periodic_callback(animate_update)
button = Button(label="► Play", width=60)
button.on_click(animate)
layout = row(column(p), button, slider)
curdoc().add_root(layout)
print(graph_source.data['xdata'][i])
在 slider_update()
的末尾,很明显源数据正在按预期更新,但图表没有刷新。
我认为问题是由 main.py 中多次使用 curdoc()
引起的,或者我误用了 server_document()
views.py
您的 main.py
代码有多个错误:
- 相同的列有不同的名称:
xdata
、x_data
、x
- 您不能就地更新数据源列。最好使用
stream
、patch
,使用ds.data['x'] = ...
更改单个列,或使用ds.data = ...
更改完整数据。使用后者,您将能够使用字典理解并避免完全存储no_of_series
remove_periodic_callback
接受add_periodic_callback
返回的回调 ID
修复这些后,您的代码就可以工作了。