ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Bokeh 服务端 Python 回调(on_change / on_event)实战指南:从事件绑定到服务端运行

2026/9/14 17:07:24 拓冰建站 浏览量
Bokeh 服务端 Python 回调(on_change / on_event)实战指南:从事件绑定到服务端运行 Bokeh 服务端 Python 回调on_change / on_event实战指南从事件绑定到服务端运行【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokehPython 回调Python callbacks是 Bokeh 中把 widget 交互与 Python 业务逻辑直接绑定的核心机制你只需在 widget 上调用.on_change()或.on_event()浏览器端的每一次滑动、点击都会回传到 Bokeh server在 Python 进程内执行你的处理函数并自动把结果同步回页面。本文以docs/bokeh/source/docs/user_guide/interaction/python_callbacks.rst为骨架结合本仓库的源码实现与真实示例讲解两种回调触发方式的函数签名、绑定方法、底层执行链路以及它们必须运行在 Bokeh server 场景下的原因并给出可完整复现的服务端示例。何时使用 Python 回调何时改用 JS 回调先明确适用边界。Python 回调本质上是运行在 Bokeh server 进程里的 Python 函数因此只能在 Bokeh server 应用中使用文档原文也强调 You can only use these callbacks in Bokeh server apps。如果你用output_file()生成静态 HTML、或用show()在 notebook 中做纯前端展示Python 回调不会生效——因为没有任何后端进程来接收和调度这些调用。如果交互逻辑不需要 Python 侧参与应当使用 JS 回调CustomJS等相关主题见同目录下的 js_callbacks.rst。一个简单的判断标准需要访问数据库、读写文件、调用重型计算或第三方 Python 库 → 必须用 Python 回调 Bokeh server只是在前端做轻量变换改个颜色、过滤数据→ 优先考虑 JS 回调省去服务器开销。Bokeh 官方提供了大量 Python 回调的服务端示例集中在 examples/server/app如movies、population、crossfilter等完整应用和 examples/reference/models如slider_server.py、radio_group_server.py、dropdown_menu_server.py等针对单个 widget 的最小示例本文后面会用到其中的代码。两种回调触发方式总览Bokeh 的 Python 回调在 Model 的某个属性被修改或某个事件发生时被触发。处理函数的签名由“以何种方式挂载”决定主要有两条路径挂载方式适用对象函数签名触发时机.on_change(attr, *callbacks)所有 widget以及所有Model(attr, old, new)指定属性值发生变化时.on_event(event, *callbacks)部分 widgetButton、Dropdown、CheckboxGroup、RadioGroup 等及 Plot无参或接收一个参数新属性值或事件对象对应交互事件发生时两者在 Python 侧的实现分别位于src/bokeh/util/callback_manager.py的PropertyCallbackManager与EventCallbackManager两个 mixin 类中Model基类在 model.py 中覆写了on_changeDocument则在 document.py 中同样暴露这两个接口。下面逐一深入。用 on_change 监听属性变化所有 widget 都提供.on_change方法。它的第一个参数是要监听的属性名字符串后面可以跟一个或多个处理函数。处理函数必须严格满足三参数签名def my_text_input_handler(attr, old, new): print(Previous label: old) print(Updated label: new) text_input TextInput(valuedefault, titleLabel:) text_input.on_change(value, my_text_input_handler)其中attr是发生变化的属性名old与new分别是变化前后的属性值。上面示例中用户每次修改输入框内容value属性变化时都会打印旧值与新值。一次绑定多个处理函数.on_change接受可变数量的回调参数这在源码中有明确体现model.py 的 docstring 示例widget.on_change(value, callback1, callback2, ..., callback_n)绑定后这些回调会按注册顺序依次被调用。底层实现属性描述符与触发链路从源码看Model.on_change首先通过self.lookup(attr)把属性名解析为对应的属性描述符descriptor再以描述符的规范化名称调用基类PropertyCallbackManager.on_changecallback_manager.py。基类实现中有几个值得注意的细节若只传入一个参数即没有回调函数会抛出ValueError: on_change takes an attribute name and one or more callbacks重复注册同一个回调函数会被去重跳过每个回调在注册时会通过_check_callback校验参数个数是否严格为 3attr, old, new不匹配直接抛ValueErrorsrc/bokeh/util/callback_manager.py的_check_callback函数可用BOKEH_MINIFIED之外的环境变量控制是否执行这类诊断。真正触发回调的是PropertyCallbackManager.triggercallback_manager.py当属性被修改时它取出该属性名下的回调列表逐个以callback(attr, old, new)调用。如果对象已挂载到Document会经由document.callbacks.notify_change走一次文档级的变更通知否则直接就地调用。一个必须了解的边界对ColumnDataSource.data使用stream()或patch()做增量更新时回调收到的old是OldValueUnavailable哨兵值而不是旧数据的完整副本model.py 的 note 明确说明。增量更新刻意不保留旧列的完整拷贝因此回调里不应依赖old的具体内容。服务端实战滑块实时重绘正弦曲线examples/reference/models/slider_server.py 是.on_change的最小可运行示例——滑动滑块改变正弦波频率数据更新后图表自动重绘import numpy as np import pandas as pd from bokeh.io import curdoc from bokeh.layouts import row from bokeh.models import ColumnDataSource, Slider from bokeh.plotting import figure x np.linspace(0, 10, 500) y np.sin(x) df pd.DataFrame({x: x, y: y}) source ColumnDataSource(datadict(xdf.x, ydf.y)) plot_figure figure(titleSlider, height450, width600, toolssave,reset, toolbar_locationbelow) plot_figure.line(x, y, line_width3, sourcesource) slider Slider(start0.1, end10, value1, step.1, titleChange Frequency) def slider_change(attr, old, new): slider_value slider.value # 读取滑块当前值 y_change np.sin(x * slider_value) source.data dict(xx, yy_change) slider.on_change(value, slider_change) layout row(slider, plot_figure) curdoc().add_root(layout) curdoc().title Slider Bokeh Server这里source.data被整体替换为新字典属于普通属性赋值因此old是完整的旧数据字典回调可以安全使用。将该文件作为 Bokeh server 应用运行bokeh serve --show examples/reference/models/slider_server.py--show会在启动服务后自动在浏览器打开页面。此时滑动滑块每一次变化都会触发slider_change服务端重新计算y并推送回浏览器。用 on_event 监听交互事件除属性变化外部分 widget 还提供.on_event方法用于响应“事件”而非“属性变化”。事件由事件类如ButtonClick及其event_name如button_click标识底层定义见 src/bokeh/events.pyButtonClick.event_name button_click、MenuItemClick.event_name menu_item_click、ValueSubmit.event_name value_submit此外还有Tap、DoubleTap、Press、MouseEnter、MouseLeave、Pan、Pinch、Rotate、Scroll、Reset、RangesUpdate、SelectionGeometry等绘图事件类。事件分两类带坐标的PointEvent子类Tap等携带sx/sy屏幕坐标与x/y数据坐标和无坐标的ModelEvent子类ButtonClick等。回调签名无参与带参.on_event的签名规则比.on_change更灵活取决于具体 widget普通Button处理函数不带参数其他支持.on_event的 widget如 Dropdown、CheckboxGroup、RadioGroup处理函数接收一个新值参数。原文档给出的RadioGroup示例def my_radio_handler(new): print(Radio button option str(new) selected.) radio_group RadioGroup(labels[Option 1, Option 2, Option 3], active0) radio_group.on_event(button_click, my_radio_handler)这里on_event的第一个参数是事件名字符串button_click等价于传入ButtonClick事件类。处理函数收到的new是被选中项的索引。EventCallbackManager.on_event的实现callback_manager.py展示了两个关键点如果第一个参数是Event的子类而非字符串会自动转换为其event_nameif not isinstance(event, str) and issubclass(event, Event): event event.event_name所以on_event(ButtonClick, cb)与on_event(button_click, cb)完全等价触发时通过_nargs(callback)检查回调参数个数零参数回调按callback()调用用于普通 Button否则把整个事件对象event传进去callback(event)。源码注释也承认普通 Button 的无参回调属于历史遗留的“混乱”设计未来计划统一让所有回调都接收事件对象。用事件对象做更精细的控制如果你需要比“无参回调”更精细的控制可以直接绑定具体事件类并接收事件对象。例如 examples/server/app/hold_app.py 演示了用ButtonClick控制Document的 hold/unhold 行为from bokeh.events import ButtonClick combine Button(labelhold combine) combine.on_event(ButtonClick, lambda event: doc.hold(combine)) collect Button(labelhold collect) collect.on_event(ButtonClick, lambda event: doc.hold(collect)) unhold Button(labelunhold) unhold.on_event(ButtonClick, lambda event: doc.unhold())运行该应用bokeh serve --show examples/server/app/hold_app.py后点击按钮会暂停/恢复文档的变更推送滑动滑块时可以看到“collect 模式逐一重放事件、combine 模式合并事件”的差异——这是on_event与Document级 API 配合的典型用例。需要说明的是不同 widget 支持的属性与事件各不相同.on_change具体可以监听哪些属性.on_event支持哪些事件均以 bokeh.models 下对应模型的定义及 src/bokeh/events.py 的事件类为准写作代码前应先查阅对应 widget 的文档。Python 回调的运行机制与注意事项把两条路径放到一起看Python 回调的完整生命周期是在 Bokeh server 应用中创建 widget 并调用.on_change/.on_event注册 Python 函数浏览器端用户交互滑动、点击、输入产生前端事件事件通过网络回传到 server触发PropertyCallbackManager.trigger或EventCallbackManager._trigger_event服务端同步执行 Python 回调回调中对source.data等属性的修改再次被打包推送回浏览器页面自动刷新。这个“浏览器 → 服务端 Python → 浏览器”的闭环正是 Python 回调只适用于 Bokeh server 的根本原因——每一步都有代码佐证callback_manager.py中trigger与_trigger_event在对象挂载到Document时都走document.callbacks.notify_change/notify_event通道。几个实践要点签名必须严格匹配.on_change回调必须是(attr, old, new)三参数.on_event回调要么零参数、要么接收新值或事件对象。写错参数个数会在注册时就被_check_callback拦截并抛出ValueError。先注册、后交互回调在对象创建后、交互发生前注册即可重复注册同一回调会被去重。回调中修改数据源最常见的模式是在回调里更新ColumnDataSource.dataBokeh 会自动把变更推送给所有引用该数据源的图形。增量更新的 old 值ColumnDataSource.data走stream()/patch()时old为OldValueUnavailable回调逻辑不应依赖old。交互无需 Python 时用 JS 回调避免不必要的服务器往返降低部署复杂度。小结Python 回调是 Bokeh server 应用实现“交互 → 计算 → 更新”闭环的标准手段.on_change(attr, cb)监听属性变化回调签名(attr, old, new).on_event(event, cb)监听交互事件普通 Button 无参其余带新值或事件对象。二者均由 src/bokeh/util/callback_manager.py 提供底层实现配合bokeh serve运行即可获得端到端的服务端交互体验。动手实践时建议从 examples/reference/models/slider_server.py 和 examples/server/app/hold_app.py 两个示例入手再对照 src/bokeh/events.py 的事件类清单扩展自己的应用。【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考