【问题标题】:Dynamically add/remove plot using 'bokeh serve' (bokeh 0.12.0)使用“散景服务”动态添加/删除绘图(散景 0.12.0)
【发布时间】:2016-11-27 15:44:22
【问题描述】:

我的问题与使用 bokeh 0.7.1 的 another thread 非常相似,但在 0.12.0 中,bokeh 服务器的 API 已经发生了足够的变化,我正在努力使该答案适应新版本。

总而言之,我有一个带有时间流图网格的页面,它从不断更新的文件中提取数据。该页面有一个 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)

【问题讨论】:

    标签: python bokeh


    【解决方案1】:

    在 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 时,散景后端都无法找到它。要检查是否是这种情况,请取消注释回调函数中的打印语句。

    我希望这会有所帮助!

    【讨论】:

    • 知道如何为服务器应用程序实现这一点吗?一切都很清楚,但 session.loop_until_closed() 似乎不适用于散景服务。
    • 现在似乎不鼓励使用loop_until_closed()github.com/bokeh/bokeh/pull/7339
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多