贡献 BokehJS#
BokehJS 是浏览器端的客户端运行时库,Bokeh 用户最终与之交互。该库主要用 TypeScript 编写,是 Bokeh 绘图系统独有的特性之一。
所有 Bokeh 可视化的核心构建块是基于 Bokeh 的 模型 对象。这些模型代表了 绘图 元素,例如坐标轴、字形 或 部件。
在 Python 端,Bokeh 将每个绘图元素对象的属性序列化为 JSON 数据。在浏览器端,BokehJS 反序列化此 JSON 数据,并基于此信息创建 JavaScript 对象。然后,BokehJS 使用这些 JavaScript 对象来渲染可视化。
Python 和 JavaScript 的结合使您能够在 Python 中定义可视化,同时利用在浏览器中运行的 JavaScript 提供的所有交互性。此外,这种结合使您能够使用 Python 处理几乎所有数据,并将大型和流式服务器端数据与交互式 JavaScript 可视化结合使用。
除了使用 BokehJS 基于 JSON 数据创建可视化之外,您还可以将 BokehJS 用作独立的 JavaScript 库。有关直接使用 BokehJS 创建可视化的更多信息,请参阅 BokehJS。
源代码位置#
Bokeh 仓库包含 Bokeh 的 Python 代码以及 BokehJS 的 JavaScript 代码。BokehJS 源代码位于此 monorepo 仓库的 bokehjs 目录中。
以下所有说明和 shell 命令均假定 bokehjs/
是您的当前目录。
提示
将您的 IDE 的工作文件夹设置为 bokehjs
目录。这样,您的 IDE 中的某些工具可能更适合 BokehJS 源代码。
BokehJS 的 CSS#
BokehJS 的 CSS 定义包含在 bokehjs/src/less/ 目录中的多个 .less
文件中。Bokeh DOM 元素的所有 CSS 类都以 bk-
为前缀。例如:.bk-plot
或 .bk-tool-button
。
代码风格指南#
BokehJS 没有明确的风格指南。在提交到 Bokeh 仓库之前,请务必运行 node make lint
或 node make lint --fix
。此外,请查看周围的代码,并尽量与现有代码保持一致。
在开发 BokehJS 时需要记住的一些指南和提示
不要使用
for-in
循环,尤其是在没有hasOwnProperty()
保护的情况下。请改用for-of
循环,并结合core/util/object
模块中的keys()
、values()
和/或entries()
。默认情况下,字符串使用双引号(
"string"
)。在单引号有助于避免转义引号的情况下使用单引号(例如case '"': return """
)。对于多行、标记和插值字符串,请使用模板字面量(模板字符串)(例如
`Bokeh ${Bokeh.version}`
)。
始终使用 ESLint 检查您的 BokehJS 代码:从 bokehjs 目录中,运行 node make lint
以检查您的代码。运行 node make lint --fix
以让 ESLint 自动修复一些问题。有关更多详细信息,请参阅 bokehjs/eslint.js 中定义的规则。
提示
如果您使用 VSCode,您可以使用以下配置为您的工作区直接在编辑器中使用 ESLint
"eslint.format.enable": true,
"eslint.lintTask.enable": true,
"eslint.debug": false,
"eslint.quiet": false,
"eslint.options": {
"cache": true,
"extensions": [".ts"],
"overrideConfigFile": "./eslint.js"
},
"eslint.workingDirectories": [
"./bokehjs"
]
这需要安装 VSCode 的 ESLint 扩展和 ESLint 版本 8 或更高版本。
开发要求#
要在本地构建和测试 BokehJS,请按照 设置开发环境 中的说明进行操作。这样,所有必需的软件包都应在您的系统上安装和配置。
开发 BokehJS 需要以下最低版本
Node.js 18+
npm 8+
Chrome/Chromium 浏览器 118+ 或同等版本
Bokeh 官方支持以下平台进行开发和测试
Linux Ubuntu 22.04+ 或同等版本
Windows 10 (或 Server 2019)
MacOS 10.15
可以使用不同的平台和版本来开发 BokehJS。但是,事情可能无法按预期工作,并且某些测试将无法运行。
构建 BokehJS#
为了构建,BokehJS 依赖于类似于 gulp 的自定义工具。所有命令都以 node make
开头。
使用 node make help
列出 BokehJS 构建系统的所有可用命令。以下是最常用的命令
node make build
:构建整个库,包括扩展编译器。node make dev
:构建库,但不包括扩展编译器。这比node make build
快,但不适用于生产代码或打包。node make test
:运行所有 BokehJS 测试。要仅运行特定测试,请参阅 选择特定 BokehJS 测试。node make lint
:使用 ESLint 检查 BokehJS 代码风格。运行node make lint --fix
以让 ESLint 自动修复一些问题。
node make
:当 package.json
更改时,node make
会自动运行 npm install
。
测试#
Bokeh 仓库包含多个测试套件。这些测试有助于确保 BokehJS 作为其自身的库以及与 Bokeh 的所有其他组件组合使用时,功能保持一致。
要了解有关在本地运行 BokehJS 测试的更多信息,请参阅 运行 JavaScript 测试。
要了解有关为 BokehJS 添加和更新测试的更多信息,请参阅 编写 JavaScript 测试 (BokehJS)。
BokehJS 中的模型和视图#
BokehJS 中可视化的基本构建块是模型和视图
- 模型
模型是一种数据结构,可能有也可能没有视觉表示。BokehJS 的模型及其属性与 Bokeh Python 代码中的模型和相应属性 相匹配。Bokeh 使用 默认值测试 来确保模型在 Python 和 BokehJS 之间保持兼容。
- 视图
视图定义了模型的视觉表示。任何影响浏览器中事物外观的模型都需要相应的视图。
对于每个模型,模型定义和相应的视图应位于 bokehjs/src/lib/models 目录中的同一文件中。
提示
在更新或添加新模型和视图时,请查看当前类似的模型和视图是如何实现的。
模型和视图的基类#
BokehJS 模型通常扩展一个基类。例如:Axis
模型扩展 GuideRenderer
,Circle
模型扩展 XYGlyph
。
模型对应的视图扩展相应的基视图类。例如:AxisView
扩展 GuideRendererView
,CircleView
扩展 XYGlyphView
。
假设您想为 Bokeh 工具栏定义一种新的操作按钮工具,称为 NewActionTool
。您的新按钮的模型将继承自 ActionTool
,其对应的视图将继承自 ActionToolView
import {ActionTool, ActionToolView} from "./action_tool"
模型#
BokehJS 模型需要 namespace
和 interface
。至少,这包括 Attrs
和 Props
。还有更多属性可以使用,例如 Mixins
或 Visuals
。
export namespace NewActionTool {
export type Attrs = p.AttrsOf<Props>
export type Props = ActionTool.Props & {
some_property: p.Property<number>
some_other_property: p.Property<string>
}
}
export interface NewActionTool extends NewActionTool.Attrs {}
如果您想更新模型,在大多数情况下,最相关的属性是 Props
。您在那里定义的属性需要与相应 Python 模型的属性和类型匹配。BokehJS 属性在 core.properties
中定义,通常使用 import * as p from "core/properties"
导入。
接下来,定义实际的模型本身。该模型扩展了相应的 BaseModel
基类。如果您的模型包含视图,这也是您链接模型和视图的地方。
export class NewActionTool extends ActionTool {
properties: NewActionTool.Props
// only when a view is required:
__view_type__: NewActionToolView
// do not remove this constructor, or you won't be
// able to use `new NewActionTool({some_property: 1})`
// this constructor
constructor(attrs?: Partial<NewActionTool.Attrs>) {
super(attrs)
}
static {
this.prototype.default_view = NewActionToolView
this.define<NewActionTool.Props>(({Number, String}) => ({
some_property: [ Number, 0 ],
some_other_property: [ String, "Default String" ],
...
// add more property definitions and defaults
// use properties from lib/core/property and primitives from lib/core/kinds
// does have to match Python, both type and default value (and nullability)
}))
}
}
视图#
如果您的模型需要显示相关的逻辑,则需要定义一个视图。视图通常处理模型在浏览器中的显示方式。
视图扩展了相应的 BaseView
基类。
export class NewActionToolView extends ActionToolView {
declare model: NewActionToolView
initialize(): void {
super.initialize()
// perform view initialization (remove if not needed)
}
async lazy_initialize(): Promise<void> {
await super.lazy_initialize()
// perform view lazy initialization (remove if not needed)
}
...
}