贡献到 BokehJS#

BokehJS 是 Bokeh 用户最终交互的浏览器端客户端运行时库。这个库主要用 TypeScript 编写,是 Bokeh 绘图系统独一无二的方面之一。

所有 Bokeh 可视化的核心构建块都是基于 Bokeh 的 模型 的对象。这些模型是 绘图 元素的表示,例如轴、图形小部件

在 Python 方面,Bokeh 将每个绘图元素对象的属性序列化为 JSON 数据。在浏览器端,BokehJS 反序列化此 JSON 数据并根据这些信息创建 JavaScript 对象。然后,BokehJS 使用这些 JavaScript 对象来渲染可视化。

Flowchart describing the flow of data from Python objects through JSON to the browser-side. There, the JSON data is converted into JavaScript objects which then get rendered as output. Output can be HTML Canvas, WebGL, or SVG.

这种 Python 和 JavaScript 的结合,使你可以在 Python 中定义可视化,同时利用在浏览器中运行的 JavaScript 提供的所有交互性。此外,这种结合使你几乎可以使用 Python 进行所有数据处理,并使用大型和流式服务器端数据进行交互式 JavaScript 可视化。

除了使用 BokehJS 创建基于 JSON 数据的可视化之外,你还可以将 BokehJS 作为独立的 JavaScript 库使用。有关使用 BokehJS 直接创建可视化的更多信息,请参阅 BokehJS

源代码位置#

Bokeh 存储库包含 Bokeh 的 Python 代码以及 BokehJS 的 JavaScript 代码。BokehJS 源代码位于此单存储库的 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 lintnode make lint --fix。此外,请查看周围的代码并尝试与现有代码保持一致。

在处理 BokehJS 时,请记住一些指南和提示

  • 不要使用 for-in 循环,尤其是没有被 hasOwnProperty() 保护的循环。而是使用 for-of 循环,并结合 keys()values() 和/或 entries() 来自 core/util/object 模块。

  • 默认情况下,对字符串使用双引号("string")。在需要避免转义引号的情况下使用单引号(case '"': return """)。

  • 对于多行、标记和插值字符串,请使用 模板字面量(模板字符串)`Bokeh ${Bokeh.version}`)。

始终使用 ESLint 对 BokehJS 代码进行 lint:从 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 进行 lint。运行 node make lint --fix 以让 ESLint 自动修复一些问题。

node make 每当 package.json 发生更改时,就会自动运行 npm install

测试#

Bokeh 存储库包含几个 测试套件。这些测试有助于确保 BokehJS 作为自己的库以及与 Bokeh 的所有其他组件结合使用时,能够始终如一地运行。

要了解有关在本地运行 BokehJS 测试的更多信息,请参阅 运行 JavaScript 测试

要了解有关添加和更新 BokehJS 测试的更多信息,请参阅 编写 JavaScript 测试(BokehJS)

BokehJS 中的模型和视图#

BokehJS 中可视化的基本构建块是模型和视图

模型

模型是一种数据结构,它可能具有或不具有视觉表示。BokehJS 的模型及其属性与 Bokeh Python 代码中的模型及其属性 相匹配。Bokeh 使用 默认测试 来确保模型在 Python 和 BokehJS 之间保持兼容。

视图

视图定义模型的视觉表示。任何影响浏览器中外观的模型都需要相应的视图。

对于每个模型,模型定义和相应的视图应该位于 bokehjs/models 目录中的同一文件中。

提示

在更新或添加新的模型和视图时,请查看当前如何实现类似的模型和视图。

模型和视图的基类#

BokehJS 模型通常扩展基类。例如:Axis 模型扩展了 GuideRendererCircle 模型扩展了 XYGlyph

模型对应的视图扩展了相应的基视图类。例如:AxisView 扩展了 GuideRendererViewCircleView 扩展了 XYGlyphView

假设您要定义一种新的 Bokeh 工具栏的操作按钮工具 类型,称为 NewActionTool。您的新按钮的模型将继承自 ActionTool,其对应的视图将继承自 ActionToolView

import {ActionTool, ActionToolView} from "./action_tool"

模型#

BokehJS 模型需要一个 namespaceinterface。至少,这包括 AttrsProps。您还可以使用更多属性,例如 MixinsVisuals

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)
  }

  ...
}