You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Bokeh中基于滑块值同步更新曲线与水平线?

Bokeh曲线+水平线同步更新的官方推荐方案

你的临时方案(用line()替代Span)不是官方推荐做法,官方更推荐两种更简洁高效的实现方式:

方式一:直接更新Span的location属性

Span是Bokeh专门用于绘制参考线的组件,它的location属性可以直接通过回调(Python或JS)修改,不需要借助ColumnDataSource维护重复数组。

Python回调示例

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, Span, Slider
from bokeh.layouts import column
from bokeh.io import curdoc

# 初始化曲线数据
x = [1,2,3,4,5]
y = [2,4,1,5,3]
source = ColumnDataSource(data=dict(x=x, y=y))

p = figure()
p.line('x', 'y', source=source)

# 初始化水平线
h_line = Span(location=3, dimension='width', line_color='red', line_width=2)
p.add_layout(h_line)

# 滑块回调函数
def update(attr, old, new):
    # 更新曲线数据(示例:y值整体偏移)
    new_y = [val + (new-3) for val in y]
    source.data = dict(x=x, y=new_y)
    # 直接更新水平线位置
    h_line.location = new

slider = Slider(start=1, end=5, value=3, step=0.1, title='水平线位置')
slider.on_change('value', update)

curdoc().add_root(column(slider, p))

JS回调示例(纯前端交互)

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, Span, Slider, CustomJS
from bokeh.layouts import column

x = [1,2,3,4,5]
y = [2,4,1,5,3]
source = ColumnDataSource(data=dict(x=x, y=y))

p = figure()
p.line('x', 'y', source=source)

h_line = Span(location=3, dimension='width', line_color='red', line_width=2)
p.add_layout(h_line)

slider = Slider(start=1, end=5, value=3, step=0.1, title='水平线位置')

# JS回调,同步更新曲线和水平线
callback = CustomJS(args=dict(source=source, h_line=h_line, slider=slider, base_y=y), code="""
    const new_y = base_y.map(val => val + (slider.value - 3));
    source.data = {x: source.data.x, y: new_y};
    h_line.location = slider.value;
""")
slider.js_on_change('value', callback)

show(column(slider, p))

方式二:绑定Span的location到ColumnDataSource字段

如果需要让水平线和其他数据联动(比如水平线值来自数据源的某个统计量),可以把Span的location绑定到数据源的单个值字段,通过更新数据源来同步水平线位置:

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, Span, Slider, CustomJS
from bokeh.layouts import column

x = [1,2,3,4,5]
y = [2,4,1,5,3]
# 数据源新增水平线字段
source = ColumnDataSource(data=dict(x=x, y=y, h_line_y=[3]))

p = figure()
p.line('x', 'y', source=source)

# 绑定location到数据源的h_line_y字段
h_line = Span(location={'field': 'h_line_y'}, dimension='width', line_color='red', line_width=2)
p.add_layout(h_line)

slider = Slider(start=1, end=5, value=3, step=0.1, title='水平线位置')

callback = CustomJS(args=dict(source=source, slider=slider, base_y=y), code="""
    const new_y = base_y.map(val => val + (slider.value - 3));
    source.data = {
        x: source.data.x,
        y: new_y,
        h_line_y: [slider.value]
    };
""")
slider.js_on_change('value', callback)

show(column(slider, p))

为什么不推荐用line()替代Span?

用line()绘制水平线需要生成和x轴长度一致的重复y值数组,会额外占用内存;当x轴数据更新或范围变化时,还要同步维护这个数组,增加了代码复杂度和维护成本——Span就是为绘制这类参考线设计的专用组件,直接用它更高效。

内容的提问来源于stack exchange,提问作者dshin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:02:26