Bokeh 3.10.0 版本深度解读:WebGL 补丁渲染、LightDark 主题切换与全新架构升级
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
Bokeh3.10.0(2026 年 5 月发布)是 Bokeh 项目的一个重要次要里程碑版本。本次版本围绕渲染性能(WebGL 支持 Patch 图形)、主题系统(LightDark 组件)、架构重构(虚拟 DOM 与 TypeScript 原生编译器)以及 Python 版本策略调整四大主线展开,同时带来导出后端、错误诊断、回调 API 等一系列体验改进。阅读本文后,你将掌握 3.10.0 的全部核心变更点、对应的源码实现位置与可运行的示例用法,能够快速评估升级影响并上手新特性。
一、版本总览与兼容性变化
1.1 版本定位
3.10.0 属于 Bokeh 的 minor(次版本)里程碑,整体保持 API 兼容的前提下,重点完成渲染管线、UI 组件内部实现与构建系统的换代,为后续版本铺路。官方发布说明收录于仓库的 releases/3.10.0.rst。
1.2 终止支持 Python 3.10 与 3.11
本次版本正式放弃对 Python 3.10(:bokeh-pull:14865)与 Python 3.11(:bokeh-pull:15174)的支持。这意味着:
- 升级到 3.10.0 之前,请确认运行环境为Python 3.12 及以上;
- 仍在 Python 3.10/3.11 上的既有应用应保持锁定旧版 Bokeh,或先完成解释器升级再升级 Bokeh;
- 放弃旧版本解释器为后续采用 Python 3.14 新语法(如模板字符串回调)扫清了障碍(见下文 CustomJS 部分)。
从仓库的 CI 与打包配置(pyproject.toml、setup.py)也可以看到项目整体向新版 Python 生态收敛的趋势。
二、WebGL 渲染正式支持 Patch 与 Patches 图形
2.1 变更内容
3.10.0 为Patch与Patches两类图形添加了WebGL 渲染后端(:bokeh-pull:14853)。此前 WebGL 加速仅覆盖 Circle、Line 等常用 glyph,Patch(单块多边形填充)与 Patches(多块多边形集合)在数据量大时依赖 Canvas 2D 绘制,性能受限;本次改动让它们也能走 GPU 管线。
2.2 源码实现
BokehJS 侧的实现位于 bokehjs/src/lib/models/glyphs/webgl,其中:
- patch.ts 与 patches.ts 分别对应单个 Patch 与批量 Patches 的 WebGL 绘制逻辑;
- types.ts 定义了与 patch 相关的 WebGL 着色器/缓冲类型协议。
从源码结构看,这两类 glyph 的 WebGL 实现沿用了 bokehjs 现有 glyph 渲染框架:在初始化时创建 GPU 缓冲区、编译着色器,并在视口变换时同步更新变换矩阵,最终在每一帧通过统一的 WebGL 渲染器批量提交绘制。
2.3 使用建议
该特性对用户透明,无需修改代码:只要浏览器支持 WebGL(或启用webgl=True),绘制 Patch/Patches 时 Bokeh 会自动选择 GPU 渲染路径。适合在地理区域着色、等高线填充、大规模多边形标注等场景获得更流畅的交互体验。仓库中 examples 目录下已有的 patch 相关示例(如 examples/basic/areas/multipolygons.py、examples/topics/geo 等)可直接用于验证渲染效果。
三、新增 LightDark 组件:一键切换明暗主题
3.1 组件定位
3.10.0 新增LightDark组件(:bokeh-pull:14807),用于在浅色与深色主题之间切换。同时(:bokeh-pull:14915)为其增加auto/system 选项,并对 Toggle 一类的组件引入三态(tri-state)处理,使组件支持“浅色 / 深色 / 跟随系统”三种状态。
3.2 源码定义
在 Python 侧,LightDark定义于 src/bokeh/models/widgets/inputs.py#L351-L366:
class LightDark(Switch): """ A switch widget to change between themes (light and dark). """ active = Override(default=None) on_icon = Override(default="light_theme") off_icon = Override(default="dark_theme") indeterminate_icon = Override(default="system_theme") tri_state = Override(default=True)关键设计点:
- 它继承自
Switch(开关类控件),因此天然具备开关的交互语义; active默认被覆盖为None(三态中的“未定/跟随系统”态);- 三种状态分别使用内置图标
light_theme、dark_theme、system_theme标识; tri_state = True开启三态能力,点击会在light → dark → system → light之间循环。
3.3 与文档级颜色方案的联动
主题切换并非孤立组件,它与 Bokeh 文档配置中的color_scheme属性联动:
- 颜色方案枚举定义在 src/bokeh/core/enums.py#L341-L343,取值为
"auto" | "light" | "dark"; - 文档配置项
color_scheme定义于 src/bokeh/document/config.py#L77,默认"auto"。
也就是说,切换LightDark组件会同步改变curdoc().config.color_scheme,进而驱动整个文档的主题渲染。
3.4 实战示例
仓库自带的服务器示例 examples/server/app/light_dark.py 演示了完整联动流程:
from bokeh import events from bokeh.core.enums import ColorScheme from bokeh.io import curdoc from bokeh.layouts import row from bokeh.models import Div, Dropdown, LightDark def on_dropdown_click(event): curdoc().config.color_scheme = event.item def on_config_color_scheme_change(attr, old, new): div.text = f"Current scheme: {new}" color_scheme = ColorScheme.auto curdoc().config.color_scheme = color_scheme curdoc().title = "LightDark" curdoc().config.on_change("color_scheme", on_config_color_scheme_change) menu = [("Dark", ColorScheme.dark), ("Auto", ColorScheme.auto), ("Light", ColorScheme.light)] color_scheme_dropdown = Dropdown(label="Update color scheme", menu=menu) color_scheme_dropdown.on_event(events.MenuItemClick, on_dropdown_click) light_dark = LightDark() div = Div(text=f"Current scheme: {color_scheme}") curdoc().add_root( row([color_scheme_dropdown, light_dark, div], stylesheets=[ ":host { background-color: light-dark(white, black); color: light-dark(black, white);}", ]), )要点:
- 通过
curdoc().config.color_scheme设置初始方案,并注册on_change监听方案变化; LightDark()直接使用默认三态配置;- 外层布局通过 CSS 的
light-dark()函数同时适配两种主题背景/前景色; - 纯前端场景可参考 examples/interaction/widgets/light_dark.py——它用
js_on_change+CustomJS读取cb_obj.document.config.color_scheme,无需 Python 回调。
在各类样式示例(examples/styling/office-style/office_style.py、examples/styling/accessible-style/accessible_style.py、examples/styling/xkcd-style/xkcd_style.py)中也都能看到LightDark(active=True, stylesheets=[...])的用法,说明它可以与自定义stylesheets无缝组合。
四、主题与 UI 组件的 vDOM 化重构
4.1 视图层全面迁移到虚拟 DOM
3.10.0 对 BokehJS 的视图内部与组件实现进行了虚拟 DOM(virtual DOM)重构(:bokeh-pull:14829)。这意味着组件渲染不再直接操作真实 DOM,而是先在虚拟节点上完成差异计算再批量应用到真实 DOM,减少不必要的重排与重绘。
4.2 Tooltip 组件改造
配合 vDOM 迁移,Tooltip 组件被迁移到 vDOM 并大幅简化了主题定制(:bokeh-pull:14955)。此前定制 Tooltip 样式往往需要覆盖深层 CSS 选择器,现在可以通过更直接的样式表路径进行定制,具体样式入口可参考 bokehjs/src/less/tooltips.less 与对应的工具提示示例 examples/interaction/tooltips。
4.3 Tabs 组件重构与无障碍改进
Tabs 组件被重写为 vDOM 实现(:bokeh-pull:15168),并新增:
- 键盘导航:可通过方向键在标签页之间切换;
- 无障碍(accessibility)改进:标签语义、焦点管理、ARIA 属性的生成更规范。
这使依赖 Tab 布局的仪表盘应用(参考 examples/basic/layouts/tabs_scrollable.py)在可访问性上直接受益。
4.4 UI 组件迁移到 adopted CSS stylesheets
UI 组件改为使用adopted CSS stylesheets(:bokeh-pull:14973)。该机制允许样式表被多个文档节点共享、按需注入,降低了组件样式隔离与注入的开销,与 vDOM 渲染管线相辅相成。底层样式源可参见 bokehjs/src/less 目录下的ui.less、buttons.less、menus.less等文件。
五、构建系统:迁移到 TypeScript 原生编译器(tsgo)
5.1 变更内容
BokehJS 的构建系统被重新设计,改用 TypeScript 原生的tsgo(TypeScript-native Go)编译器(:bokeh-pull:14812),取代此前基于 tsc 的编译链路。从源码看,构建任务定义于 bokehjs/make/tasks(含compiler.ts、esm.ts、pack.ts等),构建入口配置位于 bokehjs/make/package.json 与 bokehjs/tsconfig.base.json。
5.2 影响
- 对最终用户透明:发布包中的 BokehJS 产物行为不变;
- 对 BokehJS 开发/自定义扩展(custom extension)开发者:编译速度与内存占用有望改善,且编译语义更贴近 TypeScript 官方原生实现;
- 开发自定义扩展(参考 examples/advanced/extensions)时,建议以 3.10.0 的
bokehjs构建链为准重新编译验证。
六、新增 perform_error_diagnostics 设置项
6.1 设置定义
3.10.0 新增perform_error_diagnostics设置标志(:bokeh-pull:14947),用于控制 Bokeh 是否执行代价较高的错误诊断。其定义位于 src/bokeh/settings.py#L772-L780:
- 设置键:
perform_error_diagnostics - 环境变量:
BOKEH_PERFORM_ERROR_DIAGNOSTICS - 默认值:
True
6.2 开启时的诊断行为
依据 settings.py 的文档注释,当开启(默认)时:
- 在
on_change与on_event中校验回调签名是否正确; - 访问未定义属性时给出近似匹配建议(close-match suggestions)。
这两项行为在源码中有直接落点:回调管理器 src/bokeh/util/callback_manager.py#L202 与属性访问核心 src/bokeh/core/has_props.py#L444 均会检查settings.perform_error_diagnostics()后再决定是否执行额外检查。
6.3 关闭方式与代价
export BOKEH_PERFORM_ERROR_DIAGNOSTICS=false # 关闭关闭后可减少运行时开销,适合对性能敏感的长驻服务,但会失去回调签名校验与属性名拼写提示,开发期排查问题会更困难。建议仅在线上高吞吐场景考虑关闭。
七、CustomJS 构造简化:支持模板字符串(Python 3.14+)
7.1 变更内容
3.10.0 简化了CustomJS的构造方式(:bokeh-pull:14977),在Python 3.14+上支持直接使用模板字符串编写回调代码。CustomJS类定义于 src/bokeh/models/callbacks.py#L104,其code属性承载 JavaScript 源码。
7.2 用法示意
Python 3.14 的模板字符串(t前缀)允许在字符串内直接嵌入表达式,使回调代码中的变量注入更直观。结合 examples/interaction/widgets/light_dark.py 中的既有写法,升级后的典型形态为:
from bokeh.models import CustomJS js = CustomJS(args=dict(source=source), code=t"""\ const data = source.data; // 模板字符串内可直接使用 Python 表达式插值的结果 data.x = data.x.map(v => v * ${scale}); source.change.emit(); """)需要特别注意的是:该语法依赖 Python 3.14 的模板字符串特性,在低于 3.14 的解释器上请继续使用普通字符串拼接或 f-string 转义的方式,这一前提也与本版本放弃 Python 3.10/3.11 支持的策略相呼应。
八、导出系统:Playwright 成为 PNG/SVG 导出的替代后端
8.1 变更内容
3.10.0 新增Playwright 作为 PNG 与 SVG 导出的可选后端(:bokeh-pull:14940),并在实现上将其设为默认优先的导出方案。导出模块源码位于 src/bokeh/io/export.py:
- 后端类型枚举为
"selenium" | "playwright"(ExportBackendType); - 自动探测逻辑(
_get_screenshot_backend相关实现):默认"auto"时优先使用 Playwright,未安装时回退到已被标记弃用的 Selenium 后端; - 可通过环境变量
BOKEH_EXPORT_BACKEND显式指定playwright、selenium或auto; - 若两者都未安装,会提示安装命令:
pip install playwright && playwright install --only-shell chromium。
8.2 使用方式
调用export_png/export_svg(或file_html之外的服务端导出 API)时无需改动代码,默认即走 Playwright。如需显式指定:
from bokeh.io.export import export_png export_png(fig, filename="plot.png", backend="playwright")Playwright 后端简化了浏览器驱动依赖(不再要求额外的 WebDriver 二进制),对 CI 环境中的图像回归测试(仓库的基线测试体系见 bokehjs/test 与 tests)是更轻量的选择。官方文档说明位于 docs/bokeh/source/docs 的 export 相关章节。
九、修复与细节改进
9.1 修复 LogColorMapper 的反向颜色映射
修复了使用LogColorMapper 时反向颜色映射(inverted color mapping)失效的问题(:bokeh-pull:15035)。此前在low_color/high_color与对数色标组合时,边界颜色的映射方向可能错误;该修复保证对数色标下的颜色语义与线性色标一致。相关颜色映射器实现位于 src/bokeh/models/mappers.py,对应示例见 examples/basic/data/color_mappers.py 与 examples/basic/annotations/colorbar_log.py。
9.2 修复表格中 DateFormatter 的 TIMESTAMP 格式
修复了表格中DateFormatter 选择"TIMESTAMP"格式时失效的问题(:bokeh-pull:15221)。此前在 DataTable 列格式化中选择 TIMESTAMP 无法正确输出毫秒时间戳,本次修复恢复其行为。相关格式化器实现可查 src/bokeh/models/widgets/tables.py(DateFormatter),表格示例见 examples/interaction/widgets/data_table.py。
9.3 ContinuousTicker 参数新增 NonNegative 类型约束
为ContinuousTicker上的num_minor_ticks与desired_num_ticks添加了NonNegative类型约束(:bokeh-pull:15227),从类型系统层面禁止传入负数,避免刻度计算出现异常。ContinuousTicker定义位于 src/bokeh/models/tickers.py。
9.4 移除 PNG 图标相关 CSS 并简化图标样式
移除了PNG 图标相关的 CSS,并对图标样式表进行了简化(:bokeh-pull:14579)。图标体系现完全基于 SVG(仓库 bokehjs/src/less/icons 下 80 余个 SVG 文件),图标使用方式保持不变,但样式加载更轻量、更统一。
十、升级清单与总结
面向 3.10.0 的升级,可按以下清单自查:
- Python 版本:确保 ≥ 3.12(3.10/3.11 已不再支持);
- 主题切换:新项目可直接使用
LightDark+curdoc().config.color_scheme实现明暗主题,三态(light/dark/system)默认开启; - 性能:Patch/Patches 大数据量绘制自动获得 WebGL 加速;生产环境可通过
BOKEH_PERFORM_ERROR_DIAGNOSTICS=false关闭高开销诊断; - 导出:PNG/SVG 导出默认改用 Playwright 后端,可显式设置
BOKEH_EXPORT_BACKEND; - 回调:Python 3.14+ 可尝试模板字符串简化
CustomJS编写; - 扩展开发:自定义 BokehJS 扩展需基于新的 tsgo 构建链重新编译,并留意组件 vDOM 化对视图层 API 的影响。
总体而言,3.10.0 在保持 API 稳定的前提下完成了渲染、组件与构建三条技术线的换代:WebGL 覆盖到 Patch 图形、UI 全面 vDOM 化、构建迁移到 tsgo,同时通过LightDark与文档级color_scheme让明暗主题成为一等公民。这些改动既带来了立即可用的体验提升(主题切换、导出简化、诊断开关),也为后续版本在渲染性能与可访问性上的持续演进奠定了架构基础。
【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考