Bokeh 3.2.0 版本全解析:新 glyph 体系、WebGL 增强与交互工具升级
Bokeh 3.2.0 版本全解析:新 glyph 体系、WebGL 增强与交互工具升级
Bokeh 是 Python 生态中面向浏览器的交互式数据可视化库。本篇文章围绕 Bokeh 3.2.0(2023 年 6 月发布的 minor milestone)展开,系统梳理该版本引入的七项核心变更:Python 3.8 支持终止、Axis.axis_label_orientation 轴标签方向控制、服务器资源自定义 host/port、HSpan/VSpan/HStrip/VStrip 四条带状与跨度 glyph、环形类图形的 WebGL 渲染、CustomJS 的 ES Module 支持,以及 TapTool 键修饰符与单轴缩放能力。阅读本文后,你将能结合源码理解每个新特性的适用场景、配置参数与底层实现,并据此把升级后的能力直接运用到自己的绘图项目中。
版本概况
官方发布说明(见 docs/bokeh/source/docs/releases/3.2.0.rst)将 3.2.0 定义为 Bokeh 项目的一个 minor milestone,发布于 2023 年 6 月。该版本同时带有 1 项破坏性变更(移除 Python 3.8 官方支持)与 6 项功能增强,覆盖绘制基元(glyphs)、渲染管线(WebGL)、回调机制(CustomJS)与交互工具(TapTool、缩放工具)等多个层面。
说明:本文引用的源码均来自当前仓库。仓库的 pyproject.toml 目前要求
requires-python = ">=3.12",即仓库已演进到比 3.2.0 更新的开发状态;3.2.0 自身的运行时要求以当时发布说明为准(移除 3.8,保留 3.9 及以上)。
移除 Python 3.8 官方支持
* Official support for Python 3.8 was removed (:bokeh-pull:`12720`)
从 3.2.0 开始,Bokeh 官方不再支持 Python 3.8。这意味着:
- 使用 Python 3.8 的环境将无法获得官方对 Bokeh 3.2.0 的安装与运行保障;
- 项目维护者不再针对 3.8 进行 CI 测试与问题修复;
- 计划升级 Bokeh 的团队,需要将 Python 版本至少提升到 3.9(3.2.0 时代)或更高。
这是一项典型的"向前看"策略:Bokeh 借此可以更早使用新版 Python 的语言特性与标准库能力,同时把维护资源集中到更现代的运行时上。升级前请先核对自身环境的 Python 版本,再执行安装。
轴标签方向:新增 Axis.axis_label_orientation
* Added support for ``Axis.axis_label_orientation`` property (:bokeh-pull:`13044`)
3.2.0 为 Axis 模型新增了 axis_label_orientation 属性,用于独立控制轴标签(axis label,即整条轴的名字,如 "x values") 的朝向,而不再依赖刻度标签(tick label)的方向设置。
在源码 src/bokeh/models/axes.py 中,该属性的定义如下:
axis_label_orientation = Either(Enum(LabelOrientation), Float)(default="parallel", help="""
What direction the axis label text should be oriented. If a number
is supplied, the angle of the text is measured from horizontal.
""")
要点解析:
- 取值类型:既可以传入枚举字符串,也可以传入数值(弧度,从水平方向起算的角度)。
- 默认值:
"parallel",即轴标签默认与轴线平行。 - 枚举取值:
LabelOrientation枚举在 bokehjs/src/lib/core/enums.ts 中定义为vertical、horizontal、parallel、normal四种。其中parallel表示与轴线平行,normal表示与轴线垂直(法线方向)。 - 与
major_label_orientation的关系:后者(src/bokeh/models/axes.py)控制的是刻度标签的方向,默认"horizontal";3.2.0 之前轴标签方向只能依附于刻度标签的设置,现在二者可以分别定制。
实际使用示例:
from bokeh.plotting import figure, show
p = figure(width=600, height=400)
p.line([1, 2, 3], [1, 4, 9])
# 将 x 轴标签旋转 90 度(pi/2 弧度,即垂直方向)
p.xaxis.axis_label = "Time (s)"
p.xaxis.axis_label_orientation = 3.14159 / 2
# 或者使用枚举字符串
p.yaxis.axis_label = "Value"
p.yaxis.axis_label_orientation = "vertical"
show(p)
这一特性非常适合轴标签较长、空间紧张,或希望轴标签与刻度标签采用不同排版风格的仪表盘场景。
服务器资源:正确解析自定义 host 与 port
* Correctly resolve custom host and port with server resources (:bokeh-pull:`13041`)
bokeh serve 启动应用时,前端页面需要按正确的 host 与 port 加载 JavaScript 等静态资源。此前在自定义 host/port 场景下,server resources(服务器资源 URL 的解析逻辑)可能出现解析偏差;3.2.0 修复了该问题,使服务器资源在自定义 host 与 port 下能正确解析。
对使用方的直接影响:
- 在非默认地址(例如
bokeh serve app.py --address 0.0.0.0 --port 5100)部署时,页面中嵌入的bokeh资源 URL 会与服务器实际监听地址保持一致; - 通过反向代理或自定义域名访问时,资源加载更加可靠;
- 该修复位于服务器端资源生成逻辑中,通常无需修改用户代码,升级后即自动生效。
新增带状与跨度 glyph:HSpan、VSpan、HStrip、VStrip
* Added support for ``HSpan``, ``VSpan``, ``HStrip`` and ``VStrip`` glyphs (:bokeh-pull:`12677`)
3.2.0 一次性引入了四个新的基础绘制基元,用于标记"无限延伸的参考线/参考带"。它们的类定义位于 src/bokeh/models/glyphs.py:
| Glyph | 含义 | 关键数据属性 | 可继承的视觉属性 |
|---|---|---|---|
HSpan | 无限宽的水平线 | y | line_props(线条) |
VSpan | 无限高的垂直线 | x | line_props(线条) |
HStrip | 无限宽的水平带 | y0、y1 | line_props、fill_props(填充)、hatch_props(阴影) |
VStrip | 无限高的垂直带 | x0、x1 | line_props、fill_props、hatch_props |
从源码结构看:
HSpan与VSpan继承Glyph, LineGlyph,只有单一坐标(y或x),用于绘制阈值线、均值线、分界线等;HStrip与VStrip继承Glyph, LineGlyph, FillGlyph, HatchGlyph,接受一对坐标(y0/y1或x0/x1),用于绘制预警区间、合格范围、时间段高亮等带状区域;- 与
Span/Band注解(annotation)不同,这四个是真正的 glyph,可以直接挂在figure的add_glyph上,并参与统一的渲染与数据驱动流程(数据字段默认分别为y、x、y0/y1、x0/x1,参考_args定义)。
示例:用 HStrip 标记"正常水位区间",用 VSpan 标记"维护窗口":
from bokeh.plotting import figure, show
p = figure(width=700, height=400)
# 水平参考带:水位在 0.2 ~ 0.8 之间为正常
p.hstrip(y0=0.2, y1=0.8, fill_color="green", fill_alpha=0.15,
line_color="green", hatch_pattern="/")
# 垂直参考线:x = 3 处为关键节点
p.vspan(x=3, line_color="red", line_width=2, line_dash="dashed")
p.line([0, 1, 2, 3, 4], [0.1, 0.5, 0.9, 0.4, 0.7], line_width=2)
show(p)
对应参考示例文件可查阅 examples/reference/models/HSpan.py、examples/reference/models/VSpan.py、examples/reference/models/HStrip.py 与 examples/reference/models/VStrip.py。
WebGL 渲染扩展:Annulus、Wedge、AnnularWedge
* Added support for WebGL rendering of ``Annulus``, ``Wedge`` and ``AnnularWedge`` (:bokeh-pull:`12704`)
Bokeh 的 WebGL 后端用于加速大规模图形的绘制。3.2.0 将 Annulus(圆环)、Wedge(扇形)与 AnnularWedge(环形扇形)纳入 WebGL 渲染范围,意味着这些图形在大数据量场景下可以获得 GPU 加速,显著改善交互流畅度。
从 BokehJS 源码可以印证该实现的接入方式:以 Annulus 为例,bokehjs/src/lib/models/glyphs/annulus.ts 中通过动态导入 ./webgl/annulus 模块并注册 AnnulusGL 渲染器类,从而在检测到 WebGL 可用时切换到 GPU 路径。
对使用方的意义:
- 无需改代码:WebGL 是自动启用的加速通道,只要浏览器支持且数据规模触发条件满足,渲染即自动走 GPU 路径;
- 适用场景:需要绘制大量圆环/扇形/环形扇形(例如环形图、极坐标散点海量数据)时收益明显;
- 效果一致:WebGL 与 Canvas 路径在视觉结果上保持一致,可放心用于生产图表的渲染加速。
CustomJS 支持 ES Modules(import/export 语法)
* Added support for ES modules (import and export syntax) to ``CustomJS`` (:bokeh-pull:`12812`)
3.2.0 之前,CustomJS 的回调代码被包装成普通 JavaScript 函数体执行,无法使用 import/export 等 ES Module 语法。3.2.0 起,CustomJS 支持将代码解释为 ES module:模块默认导出一个函数,该函数在回调触发时被调用。
源码 src/bokeh/models/callbacks.py 中,CustomJS.code 属性的文档明确描述了两种解释方式:
- JS 函数模式:代码被作为函数体执行,
args中传入的命名对象以参数形式可用,另有cb_obj(触发回调的对象)、cb_data(工具相关数据,如HoverTool的鼠标坐标与 hovered glyph 索引)、cb_context(文档上下文)三个内置参数; - ES module 模式:代码是一个导出默认函数的 JavaScript 模块,可通过
module属性切换,从而支持在回调内部import其他模块,复用外部 JS 库能力。
示例(module 模式):
from bokeh.models import CustomJS, Button
button = Button(label="Click me")
button.js_on_click(CustomJS(
module=True, # 启用 ES module 模式
code="""
import { someUtility } from "./my-utils.js";
export default function() {
someUtility();
console.log("callback executed as an ES module");
}
""",
))
使用注意(来自源码文档的警告):CustomJS 的显式用途是在浏览器中执行原始 JavaScript 代码;如果代码的任何部分来自不可信的用户输入,必须在使用前做充分的输入清洗,防止注入风险。
TapTool 键修饰符支持
* Added support for configuring ``TapTool`` with key modifiers. Also allowed reporting key
modifiers in ``TapTool``'s callbacks and pointer event (e.g. ``Tap``) callbacks (:bokeh-pull:`13132`)
3.2.0 为 TapTool 引入了 modifiers 属性,可以要求用户按住特定组合键时才触发该工具,同时允许在 TapTool 的回调与指针事件(如 Tap 事件)回调中报告键修饰符状态。这为实现"Ctrl+点击才选中"等精细交互提供了官方支持。
源码定义见 src/bokeh/models/tools.py:
modifiers = Modifiers(default={}, help="""
Allows to configure a combination of modifier keys, which need to
be pressed during the selected gesture for this tool to trigger.
For example, to accept tap events only when ``Ctrl`` and ``Shift``
keys are pressed, use:
tool = TapTool(modifiers=dict(ctrl=True, shift=True))
plot.add_tools(tool)
or alternatively using a concise syntax:
tool = TapTool(modifiers="ctrl+shift")
plot.add_tools(tool)
""")
两种等价写法:
from bokeh.models import TapTool
from bokeh.plotting import figure, show
p = figure(width=600, height=400)
p.circle([1, 2, 3], [1, 4, 9], size=20)
# 写法一:字典形式
p.add_tools(TapTool(modifiers=dict(ctrl=True, shift=True)))
# 写法二:字符串缩写形式(由 _parse_modifiers 解析,"+" 连接各键)
# p.add_tools(TapTool(modifiers="ctrl+shift"))
show(p)
源码 src/bokeh/models/tools.py 中的 _parse_modifiers 负责把 "alt"、"ctrl"、"shift" 等字符串缩写解析为布尔映射;遇到未知按键名会抛出 ValueError。
注意事项(源码 .. warning:: 明确提示):
- 平台相关性:键修饰符配置是平台相关的特性,例如在移动设备上可能完全无法使用;
- 该模式同样应用于
WheelPanTool、WheelZoomTool等其他工具(见 src/bokeh/models/tools.py),3.2.0 中TapTool也加入了这一行列。
缩放工具的单轴(维度)缩放
* Added support for zoom of individual axes to ``WheelZoomTool``, ``ZoomInTool``
and ``ZoomOutTool`` (:bokeh-pull:`13049`)
3.2.0 让 WheelZoomTool、ZoomInTool、ZoomOutTool 三个缩放工具支持对单个轴进行缩放,而不是只能整体缩放。
以 WheelZoomTool 为例,源码 src/bokeh/models/tools.py 中 dimensions 属性的说明为:
dimensions = Enum(Dimensions, default="both", help="""
Which dimensions the wheel zoom tool is constrained to act in. By default
the wheel zoom tool will zoom in any dimension, but can be configured to
only zoom horizontally across the width of the plot, or vertically across
the height of the plot.
""")
- 默认
"both":任意方向缩放; - 配置为
"width":只允许沿水平方向(x 轴)缩放; - 配置为
"height":只允许沿垂直方向(y 轴)缩放。
同时,工具的类文档指出:WheelZoomTool 会激活 Plot 的边框区域用于"单轴缩放",例如在垂直边框或 y 轴区域滚动滚轮,将只对垂直方向生效而保持水平维度固定。
使用示例:
from bokeh.models import WheelZoomTool
from bokeh.plotting import figure, show
p = figure(width=600, height=400)
p.line([1, 2, 3], [1, 4, 9])
# 只允许水平方向缩放
wheel_zoom = WheelZoomTool(dimensions="width")
p.add_tools(wheel_zoom)
show(p)
这一能力在"只想在某个轴上探索数据细节"(如长序列时间轴的水平缩放、只关注数值纵向分布)的场景中非常实用,避免缩放时另一个轴跟着抖动。
小结
Bokeh 3.2.0 作为 2023 年年中的 minor 版本,在四个方面形成了清晰的技术主线:
- 绘制基元扩展:
HSpan/VSpan/HStrip/VStrip补齐了"参考线/参考带"的原生 glyph 能力,参考示例见 examples/reference/models 目录; - 渲染性能增强:环形类图形(
Annulus、Wedge、AnnularWedge)接入 WebGL 渲染管线; - 交互精细化:
TapTool支持键修饰符组合触发与修饰符上报,三个缩放工具支持按轴缩放,轴标签方向可独立配置; - 工程化推进:移除 Python 3.8 支持、修复自定义 host/port 下的服务器资源解析、
CustomJS支持 ES Module 语法以复用现代前端模块体系。
升级到 3.2.0 及之后版本时,只需重点关注 Python 版本下限这一破坏性变更;其余六项功能均为向后兼容的能力增强,可平滑接入现有绘图代码。
更多推荐
所有评论(0)