使用“bokeh serve”(bokeh 0.12.0)动态添加/删除绘图

2024-05-23 19:14:04 发布

您现在位置:Python中文网/ 问答频道 /正文

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

总而言之,我有一个带有时间流图网格的页面,它从一个不断更新的文件中提取数据。页面有一个多选菜单,其中列出了我的文件中的所有变量。我希望能够在菜单中选择不同的变量,按一个按钮,然后让现有变量的图消失并被新的时间流替换,其中的图的数量可能不同。我正在用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)

Tags: nameinfromimportsourcefordatatitle
1条回答
网友
1楼 · 发布于 2024-05-23 19:14:04

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语句的注释。

我希望这有帮助!

相关问题 更多 >