Невозможно удалить обратные вызовы на сервере Bokeh (ValueError: обратный вызов уже запущен или уже был удален, не может быть удален снова) - PullRequest
0 голосов
/ 04 апреля 2020

Я использую Bokeh Server с Django, чтобы анимировать некоторые диаграммы, которые показывают, что несколько серий меняются со временем. В настоящее время я пытаюсь автоматизировать слайдер для продвижения временных рядов.

К сожалению, у меня возникли две проблемы (которые, вероятно, связаны). Во-первых, несмотря на то, что анимация начинается (ползунок перемещается, а кнопка «play» меняется на «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() ясно, что исходные данные обновляются, как и ожидалось, но диаграмма не ссылается sh.

Я ожидаю, что проблема вызвана многократным использованием curdoc() в main .py , или я неправильно использую server_document() в views.py

1 Ответ

1 голос
/ 04 апреля 2020

Ваш код main.py содержит несколько ошибок:

  1. Те же столбцы имеют разные имена: xdata, x_data, x
  2. Вы не можете обновить данные исходные столбцы на месте. Лучше использовать stream, patch, изменить один столбец с помощью ds.data['x'] = ... или изменить полные данные с помощью ds.data = .... С последним вы сможете использовать диктовку и избежать сохранения no_of_series полностью
  3. remove_periodic_callback принимает идентификатор обратного вызова, который был возвращен add_periodic_callback

С этими исправленными , твой код будет работать.

...