2016-07-24 61 views
9

我的问题与使用散景0.7.1的another thread非常相似,但散景服务器的API在0.12.0中发生了足够的变化,我挣扎着以适应新版本的答案。总结一下,我有一个页面,其中包含一个时间流图的网格,它从一个不断更新的文件中提取数据。该页面有一个MultiSelect菜单,其中列出了我的文件中的所有变量。我希望能够在菜单中选择不同的变量,按下一个按钮,然后让现有变量的图消失并由新的时间流替换,其中图的数量可能不同。我正在使用bokeh serve --show script.py包装器运行我的脚本。使用“散景服务”动态添加/删除绘图(散景0.12.0)

在我最初的尝试中,我给一个按钮分配了一个事件处理函数,它清除了'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被删除时,它都无法通过散景后端找到。要检查是否属于这种情况,请取消注释回调函数中的print语句。

我希望这有助于!

+1

任何想法如何使这发生在服务器应用程序?一切都很清楚,但session.loop_until_closed()似乎不适用于散景服务。 –

+0

现在看起来'loop_until_closed()'的用法现在不受欢迎:https://github.com/bokeh/bokeh/pull/7339 –