ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Bokeh 自定义扩展实战:用 Surface3d 封装 vis.js 实现 3D 曲面可视化

Bokeh 自定义扩展实战:用 Surface3d 封装 vis.js 实现 3D 曲面可视化 Bokeh 自定义扩展实战用 Surface3d 封装 vis.js 实现 3D 曲面可视化【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh本文以 Bokeh 仓库中的 surface3d 示例 为主线系统讲解如何通过 Bokeh 的**自定义扩展Custom Extensions**机制把第三方 JavaScript 库vis.js Graph3d封装为可在 Python 侧直接实例化的 Bokeh 模型并通过 Bokeh Server 的ColumnDataSource与周期性回调实现数据驱动的实时 3D 渲染。读完本文你将掌握自定义扩展的 Python/TypeScript 双端模型结构、属性定义与信号同步机制并能独立编写、运行自己的扩展型 Bokeh Server 应用。示例背景为什么需要自定义扩展Bokeh 内置了大量绘图能力但任何 Web 工具、组件或框架都不可能全部原生内置。官方在 templates/index.html 中明确指出借助自定义扩展 Bokeh Server可以轻松把 Python 数据分析工具NumPy、SciPy、Pandas 等与几乎任意的 Web 库连接起来。surface3d 正是这一理念的样板它包装了 vis.js 的 Graph3d 组件做出一张不断被 Server 端回调刷新的 3D 曲面图并且支持鼠标拖拽旋转、滚轮/捏合缩放。在 examples/server/app/README.md 的示例总览表中它的定位被概括为通过 Bokeh 自定义扩展包装第三方 JavaScript 库的动态 3D 图。环境准备与启动官方 README 强调运行本示例不需要任何额外安装的包或步骤——因为 Bokeh 本身、NumPy用于计算曲面数据都属于既有依赖vis.js 库则在应用模板中通过 CDN 加载见下文模板与静态资源。在仓库根目录下进入示例父目录examples/server/app直接执行bokeh serve --show surface3d--show会在启动 Server 后自动在浏览器新标签页打开应用。这与 examples/server/app/README.md 中描述的统一运行方式一致bokeh serve --show 脚本名或目录名。若希望指定端口或地址可追加--port等标准 Server 参数。该示例同样被 tests/examples.yaml 登记在测试清单中注明其因无Bokeh.index无法计算 bbox 而走特殊检查路径说明它是仓库内被 CI 覆盖的正式示例之一。扩展的 Python 侧surface3d.py自定义扩展的核心是成对出现的模型Python 侧定义属性与默认值TypeScript 侧定义浏览器渲染行为。先看 surface3d.py。继承LayoutDOM让扩展可参与 Bokeh 布局class Surface3d(LayoutDOM): __implementation__ surface3d.ts源码注释给出选基类的通用法则想让扩展拥有 DOM 视图、可被放入 Bokeh 布局 → 继承LayoutDOM本示例所选想创建自定义工具 → 继承Tool想创建自定义字形 → 继承Glyph。特殊的类属性__implementation__指向实现浏览器侧逻辑的 TypeScript 文件。从 Bokeh 的编译机制看见 src/bokeh/util/compiler.py__implementation__支持多种写法内联的TypeScript(...)/JavaScript(...)字符串或通过FromFile读取.ts、.js、.css、.less独立文件扩展名决定了语言类型。本示例直接赋文件路径字符串等价于从文件读取实现。属性声明Python 与浏览器的自动通信协议Bokeh 属性Property是类属性定义模型字段及其类型由框架在 Python 与浏览器之间自动序列化传输并附带类型校验。Surface3d 声明了四组属性data_source Instance(ColumnDataSource) # 可被 Server 端 Python 代码更新 x String() # 指定 ColumnDataSource 中用作 x 的列名 y String() z String() options Dict(String, Any, defaultDEFAULTS) # 透传给 vis.js Graph3d 的选项字典值得注意的设计点data_source使用Instance(ColumnDataSource)使其可以持有真实数据列并在 Server 端被更新后自动向浏览器推送变更事件x/y/z是列名字符串而非数值数据本身存在ColumnDataSource中模型只声明取哪一列实现了解耦options是Dict(String, Any)Any允许任意 JSON 值从而把 vis.js Graph3d 的全部可配置项开放给 Python 使用者。默认选项DEFAULTSPython 侧DEFAULTS字典与 TypeScript 侧的OPTIONS常量一一对应注释要求二者保持一致DEFAULTS { width: 600px, height: 600px, style: surface, showPerspective: True, showGrid: True, keepAspectRatio: True, verticalRatio: 1.0, legendLabel: stuff, cameraPosition: { horizontal: -0.35, vertical: 0.22, distance: 1.8, }, }含义速览width/height固定组件尺寸源码注释特别说明尺寸在options中固定若进一步改造可做成响应式style为曲面绘制风格showPerspective开启透视投影showGrid显示网格keepAspectRatio保持宽高比verticalRatio垂直方向比例legendLabel图例标签cameraPosition设置初始相机位置水平角、仰角、距离。这些选项可在实例化时通过options属性覆盖。扩展的 TypeScript 侧surface3d.ts浏览器侧的实现在 surface3d.ts 中由四个部分组成结构与 Python 模型严格对应。1. 外部库类型声明由于仓库不内置 vis.js 类型定义文件顶部用declare namespace vis手工声明了Graph3d与DataSet的最小类型接口便于 TypeScript 编译并注明其假设 vis.js 已被加载例如在自定义应用模板中。2. View 子类负责渲染export class Surface3dView extends LayoutDOMView { private _graph: vis.Graph3d override render(): void { super.render() this._graph new vis.Graph3d(this.shadow_el, this.get_data(), to_object(this.model.options)) } ... }注释解释了 Bokeh 视图的通用约定View 默认创建div元素即el多数视图会忽略它改画 HTML canvas而这里直接利用该divshadow DOM 中的shadow_el挂载 Graph3dchild_models返回空数组表明该布局项没有子 Bokeh 模型。3. 信号连接数据变更自动重绘override connect_signals(): void { super.connect_signals() this.connect(this.model.data_source.change, () this._graph.setData(this.get_data())) }这是Python 改数据 → 浏览器自动更新的关键一环监听data_source的change信号触发时把最新数据转换为 vis.js 的DataSet并调用setData重绘。4. 数据适配Bokeh → vis.jsget_data(): vis.DataSet { const data new vis.DataSet() const source this.model.data_source for (let i 0; i source.get_length()!; i) { data.add({ x: source.get(this.model.x)[i], y: source.get(this.model.y)[i], z: source.get(this.model.z)[i], }) } return data }其职责单一遍历ColumnDataSource每一行按x/y/z属性指定的列名取出数值组装成{x, y, z}对象加入DataSet——即源码注释所说把 Bokeh 数据源适配为 vis.js DataSet 格式。5. 模型子类与属性注册export class Surface3d extends LayoutDOM { static __name__ Surface3d // 必须与 Python 类名一致TS 编译时会自动填充 static { this.prototype.default_view Surface3dView this.defineSurface3d.Props(({Unknown, Str, Dict, Ref}) ({ x: [ Str ], y: [ Str ], z: [ Str ], data_source: [ Ref(ColumnDataSource) ], options: [ Dict(Unknown), OPTIONS ], })) } }要点__name__必须与 Python 类名精确一致否则模型无法序列化/反序列化源码注释特别提醒避免手写拼错default_view把模型与 View 绑定define块注册的属性与 Python 侧一一对应类型对应关系为String → Str、Dict(String, Any) → Dict(Unknown)、Instance(ColumnDataSource) → Ref(ColumnDataSource)在 JS 类型系统尚不丰富的场景可用Unknown作为通配类型。数据驱动与动画main.py 的完整链路main.py 把以上模型组装成可运行的应用展示了Python 计算 → ColumnDataSource → 浏览器 3D 图的完整数据流。生成曲面网格数据x np.arange(0, 300, 20) y np.arange(0, 300, 20) xx, yy np.meshgrid(x, y) xx xx.ravel() yy yy.ravel() def compute(t): value np.sin(xx/50 t/10) * np.cos(yy/50 t/10) * 50 50 return dict(xxx, yyy, zvalue)在 0~300 范围内每 20 取一个采样点形成 15×15 的网格compute(t)以时间参数t驱动正弦/余弦乘积得到随时间变化的波浪曲面。实例化扩展并挂载到文档source ColumnDataSource(datacompute(0)) surface Surface3d(xx, yy, zz, data_sourcesource) curdoc().add_root(surface) curdoc().title Surface3d注意Surface3d的x/y/z传入的是列名字符串与 Python 侧属性定义呼应。周期性回调驱动动画from bokeh.driving import count count() def update(t): source.data compute(t) curdoc().add_periodic_callback(update, 100)count()来自 src/bokeh/driving.py是一个驱动函数装饰器每次调用回调时自动注入递增的计数值作为t从而让compute(t)持续演化curdoc().add_periodic_callback(update, 100)每100 毫秒触发一次update实现见 src/bokeh/document/document.py每次执行source.data compute(t)该赋值触发ColumnDataSource的change信号进而驱动 TypeScript 侧connect_signals中注册的回调 →setData→ 3D 图无刷新重绘。这就是Server 端 Python 每 100ms 算一帧、浏览器端同步重绘的完整闭环。模板与静态资源如何加载第三方库templates/index.html 是一个继承 Bokeh 默认模板{% extends base %}的自定义应用模板承担两项任务加载 vis.js在preamble块中引入 CDN 脚本script srchttps://cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.js/script这正是surface3d.ts中new vis.Graph3d(...)能全局使用vis的前提源码注释也说明未来 Bokeh 模型将能自动指定并加载外部脚本当前阶段需要模板手动引入页面说明文案在contents块中输出标题与两段说明并通过{{ super() }}保留 Bokeh 默认内容即渲染出的 3D 图。文中还预告了交互方式——拖拽旋转、滚动/捏合缩放。同目录的__init__.py为空文件作用是让examples/server/app/surface3d成为可导入的 Python 包保证main.py中from .surface3d import Surface3d的相对导入成立。扩展机制总结与可复用模板把整个示例抽象成自定义扩展四件套即可复用到任意第三方 JS 库文件角色关键内容surface3d.pyPython 模型继承LayoutDOM声明__implementation__与 Bokeh 属性surface3d.ts浏览器实现LayoutDOMView子类渲染、connect_signals监听数据变更、模型属性注册main.py应用逻辑构造数据源、实例化扩展、add_periodic_callback驱动动画templates/index.html应用模板加载第三方库脚本、定制页面文案写作自定义扩展时的通用要点均有源码佐证基类选择决定能力边界布局型用LayoutDOM工具型用Tool字形型用Glyph见 surface3d.py 注释Python 与 TypeScript 的类名必须一致属性需两侧成对声明通过connect_signals监听ColumnDataSource.change即可在 Server 端任意更新数据而无需触碰 DOM__implementation__支持内联代码与外部文件两种形态.ts/.js/.css/.less均可作为实现文件见 src/bokeh/util/compiler.py。借助这套机制Bokeh 生态之外的 Web 可视化库尤其是各类 3D、地图、富交互组件都能被封装成 Python 友好的 Bokeh 模型与 NumPy 计算、ColumnDataSource数据流和 Server 端回调无缝衔接——这正是 surface3d 示例希望传递的核心价值。【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表