web-dev-qa-db-ja.com

「ボケサーブ」(ボケ0.12.0)を使用してプロットを動的に追加/削除します

私の質問は 別のスレッド ボケ0.7.1を使用するのと非常に似ていますが、ボケサーバーのAPIは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)
16
Adam Anderson

同様の問題は、Bokeh Githubページで回答されました ここ

基本的に、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ステートメントのコメントを解除します。

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

11
sweaver23