2016-07-24 44 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 –