2016-07-24 18 views
9

私の質問はbokeh 0.7.1を使用してanother threadとかなり似ていますが、bokehサーバーのAPIは0.12.0で十分に変更されていますその答えを新しいバージョンに適応させる。要約すると、私は、継続的に更新されるファイルからデータを引き出すタイムストリームプロットのグリッドを持つページを持っています。このページには、ファイル内のすべての変数をリストするMultiSelectメニューがあります。私は、メニュー内の異なる変数を選択し、ボタンを押してから、既存の変数のプロットを消して、プロット数が異なる新しいタイムストリームに置き換えたいと思っています。スクリプトをbokeh serve --show script.pyラッパーで実行しています。'bokeh serve'(bokeh 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が削除されるたびに、bokehバックエンドがそれを見つけることができません。この場合にチェックするには、コールバック関数でprintステートメントのコメントを外します。

こちらがお役に立てば幸いです。

+1

サーバーアプリケーションでこれを行うにはどうすればよいですか?すべてがクリアですが、session.loop_until_closed()はbokehサーブには機能していないようです。 –

+0

'loop_until_closed()'の使用法が今や落ちているように見えます:https://github.com/bokeh/bokeh/pull/7339 –

関連する問題