2016-07-24 13 views
9

Мой вопрос очень похож на another thread с использованием bokeh 0.7.1, но API для серверов bokeh изменился достаточно в 0.12.0, что я борюсь чтобы адаптировать этот ответ к новой версии.Динамическое добавление/удаление участка с использованием «bokeh serve» (bokeh 0.12.0)

Подводя итог, у меня есть страница с сеткой временного графика, потянув данные из файла, который постоянно обновляется. На странице есть меню MultiSelect, в котором перечислены все переменные в моем файле. Я хочу иметь возможность выбирать различные переменные в меню, нажимать кнопку, а затем отображать графики существующей переменной и заменяться новыми временными потоками, где количество графиков может отличаться. Я запускаю свой сценарий с помощью обертки bokeh serve --show script.py.

В моей первоначальной попытке я назначил обработчик события кнопке, которая очищает «curdoc», а затем добавляет графики для вновь выбранных переменных из MultiSelect. Это выполняется, но количество графиков не обновляется. Очевидно, что мне не хватает вызова, который сообщает серверу как-то обновить макет страницы.

import numpy as np 

from bokeh.driving import count 
from bokeh.plotting import figure, curdoc 
from bokeh.layouts import gridplot 
from bokeh.models import Slider, Column, Row, ColumnDataSource, MultiSelect, Button 
from netCDF4 import Dataset 
import datetime 

# data 
#data = Dataset('/daq/spt3g_software/dfmux/bin/output.nc', 'r', format='NETCDF4') 
data = Dataset('20160714_warm_overbiased_noise.nc', 'r', format='NETCDF4') 
vars = data.variables.keys()[1:11] 

# plots 
d = {('y_%s'%name):[] for name in vars} 
d['t'] = [] 
source = ColumnDataSource(data=d) 

figs = [figure(x_axis_type="datetime", title=name) for name in vars] 
plots = [f.line(x='t', y=('y_%s'%f.title.text), source=source, color="navy", line_width=1) for f in figs] 
grid = gridplot(figs, ncols=3, plot_width=500, plot_height=250) 

# UI definition 
npoints = 2000 
slider_npoints = Slider(title="# of points", value=npoints, start=1000, end=10000, step=1000.) 
detector_select = MultiSelect(title="Timestreams:", value=[], options=vars) 
update_detector_button = Button(label="update detectors", button_type="success") 

# UI event handlers 
def update_detector_handler(): 
    global figs, plots, grid, source 
    d = {('y_%s'%name):[] for name in detector_select.value} 
    d['t'] = [] 
    source = ColumnDataSource(data=d) 

    figs = [figure(x_axis_type="datetime", title=name) for name in detector_select.value] 
    plots = [f.line(x='t', y=('y_%s'%f.title.text), source=source, color="navy", line_width=1) for f in figs] 
    grid = gridplot(figs, ncols=3, plot_width=500, plot_height=250) 
    curdoc().clear() 
    curdoc().add_root(Column(Row(slider_npoints, Column(detector_select, update_detector_button)), grid)) 

update_detector_button.on_click(update_detector_handler) 

# callback updater 
@count() 
def update(t): 
    data = Dataset('20160714_warm_overbiased_noise.nc', 'r', format='NETCDF4') 
    #data = Dataset('/daq/spt3g_software/dfmux/bin/output.nc', 'r', format='NETCDF4') 

    npoints = int(slider_npoints.value) 
    new_data = {('y_%s'%f.title.text):data[f.title.text][-npoints:] for f in figs} 
    new_data['t'] = data['Time'][-npoints:]*1e3 

    source.stream(new_data, npoints) 

# define HTML layout and behavior 
curdoc().add_root(Column(Row(slider_npoints, Column(detector_select, update_detector_button)), grid)) 
curdoc().add_periodic_callback(update, 500) 

ответ

6

Аналогичная проблема ответили на странице Bokeh Github here.

По существу, вместо того, чтобы возиться с curdoc(), вы вместо этого изменяете дочерние объекты макета, например. someLayoutHandle.children.

Простой пример использования кнопки переключения для добавления и удаления график:

from bokeh.client import push_session 
from bokeh.layouts import column, row 
from bokeh.models import Toggle 
from bokeh.plotting import figure, curdoc 
import numpy as np 
# Create an arbitrary figure 
p1 = figure(name = 'plot1') 

# Create sin and cos data 
x = np.linspace(0, 4*np.pi, 100) 
y1 = np.sin(x) 
y2 = np.cos(x) 

# Create two plots 
r1 = p1.circle(x,y1) 

# Create the toggle button 
toggle = Toggle(label = 'Add Graph',active=False) 

mainLayout = column(row(toggle,name='Widgets'),p1,name='mainLayout') 
curdoc().add_root(mainLayout) 
session = push_session(curdoc()) 
# Callback which either adds or removes a plot depending on whether the toggle is active 
def toggleCallback(attr): 
    # Get the layout object added to the documents root 
    rootLayout = curdoc().get_model_by_name('mainLayout') 
    listOfSubLayouts = rootLayout.children 

    # Either add or remove the second graph 
    if toggle.active == False: 
     plotToRemove = curdoc().get_model_by_name('plot2') 
     listOfSubLayouts.remove(plotToRemove) 

    if toggle.active == True: 
     if not curdoc().get_model_by_name('plot2'): 
      p2 = figure(name='plot2') 
      plotToAdd = p2 
      p2.line(x,y2) 
      # print('Remade plot 2') 
     else: 
      plotToAdd = curdoc().get_model_by_name('plot2') 
     listOfSubLayouts.append(plotToAdd) 

# Set the callback for the toggle button 
toggle.on_click(toggleCallback) 

session.show() 
session.loop_until_closed() 

часть, которая дала мне больше всего хлопот было убедиться, что сюжет я хотел бы добавить была частью curdoc(), который поэтому определение относится к функции обратного вызова. Если он не находится в обратном вызове, каждый раз, когда plot2 удаляется, он не может быть найден бэкэндом bokeh. Чтобы убедиться, что это так, раскомментируйте оператор печати в функции обратного вызова.

Надеюсь, это поможет!

+1

Любая идея, как это сделать для серверного приложения? Все ясно, но session.loop_until_closed(), похоже, не работает для боке. –

+0

Похоже, что использование 'loop_until_closed()' теперь обескуражено: https://github.com/bokeh/bokeh/pull/7339 –

Смежные вопросы