迁移自定义小部件库#
这些迁移指南专门针对第三方小部件的开发者。
从版本7.x迁移到8.0#
在本节中,我们讨论如何将自定义小部件从ipywidgets 7迁移到 ipywidgets 8,或使用相同代码库同时支持ipywidgets 7和ipywidgets 8。
有关影响自定义小部件作者的变更摘要列表,请参阅8.0版本更新日志中的“开发者”部分。
请考虑通过从JavaScript widget cookiecutter生成一个新的widget并调整代码到您的widget来更新您的widget,因为cookiecutter已更新以使用Python打包和Jupyter Widget基础设施的最佳实践。
例如迁移,请查看这些PR:
更新 setup.py#
首先在你的 setup.py 或 setup.cfg 中更新依赖以支持 8.x 版本。
例如
install_requires=[
- 'ipywidgets>=7,<8',
+ 'ipywidgets>=7,<9',
],
更新 package.json#
接下来,你应该更新JavaScript依赖项。你需要更新你的@jupyter-widgets/base依赖项和@jupyter-widgets/controls(如果你依赖它的话)。
如果你想继续支持 ipywidgets<8,差异将如下所示:
- "@jupyter-widgets/base": "^2 || ^3 || ^4",
+ "@jupyter-widgets/base": "^2 || ^3 || ^4 || ^5 || ^6",
如果您从现在开始只想支持 ipywidgets==8,也可以应用以下差异:
- "@jupyter-widgets/base": "^2 || ^3 || ^4",
+ "@jupyter-widgets/base": "^6",
注意,“@jupyter-widgets/base”版本5是为JupyterLab 4上的ipywidgets 7支持预留的,“@jupyter-widgets/base”版本6是随ipywidgets 8发布的版本。
ManagerBase 类已被拆分为一个接口类型 IWidgetManager,该接口仍保留在 @jupyter-widgets/base 包中,而其实现在则移至新的 @jupyter-widgets/base-manager 包中。因此,如果你要继承 ManagerBase 类,你需要在你的 package.json 中添加如下新的依赖项,并相应地更新你的导入语句。
+ "@jupyter-widgets/base-manager": "^1",
更新webpack publicPath配置#
我们强烈建议您更新您的widget的webpack配置中的publicPath,它用于生成AMD模块,采用类似于这些更改的变更。这些更改允许您的AMD模块托管在任何地方,而不是硬编码某个特定的CDN,如unpkg.com,并且通过消除为notebook扩展生成的AMD模块和为CDN生成的AMD模块之间的差异来简化事情。
更新浏览器代码#
Phosphor -> Lumino#
Phosphor库已被归档。它已被分叉并重命名为Lumino,维护工作现由JupyterLab治理下进行。
如果您曾经从@jupyter-widgets/base库中导入类,如JupyterPhosphorPanelWidget和JupyterPhosphorWidget,您将需要更新它们:
- import { JupyterPhosphorPanelWidget, JupyterPhosphorWidget } from '@jupyter-widgets/base';
+ import { JupyterLuminoPanelWidget, JupyterLuminoWidget } from '@jupyter-widgets/base';
DOMWidgetView.pWidget 属性已重命名为 DOMWidgetView.luminoWidget(虽然为方便起见,仍提供 pWidget 的别名):
- this.pWidget
+ this.luminoWidget
DOMWidgetView.processPhosphorMessage 方法已重命名为 DOMWidgetView.processLuminoMessage。如果您想同时支持 ipywidgets 7.x 和 8.x,您应该同时实现这两个方法并调用正确的父类方法:
- processPhosphorMessage(msg: Message): void {
- super.processPhosphorMessage(msg);
- switch (msg.type) {
- case 'resize':
- this.resize();
- break;
- }
- }
+ _processLuminoMessage(msg: Message, _super: (msg: Message) => void): void {
+ _super.call(this, msg);
+ switch (msg.type) {
+ case 'resize':
+ this.resize();
+ break;
+ }
+ }
+
+ processPhosphorMessage(msg: Message): void {
+ this._processLuminoMessage(msg, super.processPhosphorMessage);
+ }
+
+ processLuminoMessage(msg: Message): void {
+ this._processLuminoMessage(msg, super.processLuminoMessage);
+ }
如果你放弃对ipywidgets 7.x的支持,可以简单地将processPhosphorMessage方法重命名为processLuminoMessage。
小组件管理器导入#
如前所述,如果你依赖ManagerBase类,你将需要更新导入:
- import { ManagerBase } from '@jupyter-widgets/base';
+ import { ManagerBase } from '@jupyter-widgets/base-manager';
或者,切换到在 base 包中使用新的 IWidgetManager 接口:
- import { ManagerBase } from '@jupyter-widgets/base';
+ import { IWidgetManager } from '@jupyter-widgets/base';
选择哪一个取决于您如何使用它。如果您将其作为您自己实现的小部件管理器的基础类,并希望子类化它以重用该实现中的方法/逻辑,您应该依赖base-manager包。如果您只对小部件管理器的TypeScript类型感兴趣,例如用于反序列化器函数的参数中,您应该使用IWidgetManager接口类型。
Typescript 技巧: 如果您需要同时支持 ipywidgets 7 及更旧版本和新版本 8 的反序列化器函数,可以将反序列化器函数改为以下签名:
- import { ManagerBase } from '@jupyter-widgets/base';
+ import { unpack_models } from '@jupyter-widgets/base';
export async function myDeserializer(
obj: MyObjectType,
- manager?: ManagerBase
+ manager?: Parameters<typeof unpack_models>[1]
): Promise<JSONValue> {
Backbone 扩展#
ipywidgets所依赖的Backbone.js版本已从1.2.3更改为1.4.0。如果您之前使用var CustomWidgetModel = Widget.extend({ ... });扩展基础控件模型,现在需要使用ES6语法更新类定义:
- var CustomWidgetModel = Widget.extend({
- ...
- });
+ class CustomWidgetModel extends Widget {
+ ...
+ }
如果你之前使用.extend(),你还需要更改模型属性默认值的定义方式。模型默认值现在由一个返回默认值并包含父类默认值的函数提供。例如,Output小部件模型looks like this:
export const OUTPUT_WIDGET_VERSION = '1.0.0';
export class OutputModel extends DOMWidgetModel {
defaults() {
return {
...super.defaults(),
_model_name: 'OutputModel',
_view_name: 'OutputView',
_model_module: '@jupyter-widgets/output',
_view_module: '@jupyter-widgets/output',
_model_module_version: OUTPUT_WIDGET_VERSION,
_view_module_version: OUTPUT_WIDGET_VERSION,
};
}
}
自定义标签名称#
如果您通过定义tagName属性来更改小部件的基HTML标签,现在可以在ipywidgets 8的preinitialize方法中完成(例如,请参阅这里查看核心小部件的变更示例):
- get tagName() {
- return 'button';
- }
+ preinitialize() {
+ this.tagName = 'button';
+ }
如果你需要与ipywidgets 7兼容,请继续使用get tagName访问器而非preinitialize。然而,新版本的Typescript会警告你正在用一个函数覆盖属性。如果你想同时保持与ipywidgets 7和ipywidgets 8的兼容性,并且正在使用Typescript,你可以添加一个ts-ignore指令来安抚Typescript,就像在ipydatawidgets中做的那样:
+ // @ts-ignore: 2611
get tagName() {
return 'button';
}
从 6.0 迁移到 7.0#
例如迁移,请查看这些PR:
为了避免将您的开发周期与ipywidgets绑定,我们建议在一个分支上开始迁移,并保持该分支开放,直到ipywidgets 7.0发布。
我们还建议在一个全新的notebook中测试迁移,而不是在包含使用ipywidgets 6.0实例化的小部件的notebook中。
更新 setup.py#
首先在您的setup.py中将依赖项更新至最新版本。要
找到正确的版本号,请前往 Github 上的发布页面并
浏览标签,直到看到最新的 7.0.0 标签。
更新 package.json#
接下来,我们应该更新 JavaScript 依赖项。对于 widget 开发者来说,最重要的变化是 jupyter-widgets 的 JavaScript 包已经被拆分到 @jupyter-widgets/base 和 @jupyter-widgets/controls:
@jupyter-widgets/base包含基础控件类和布局类@jupyter-widgets/controls包含面向用户的小部件类。
在你的package.json中,从依赖项中移除jupyter-js-widgets
并添加@jupyter-widgets/base。要找到正确的版本,请前往
发布页面并
循环查看标签,直到看到名为
@jupyter-widgets/base@<version>的标签。如果你认为你依赖它(例如,如果你
创建继承自VBox或HBox或其他面向用户的小部件的控件),则对
@jupyter-widgets/controls执行相同的操作。
更新Webpack配置#
如果您使用Webpack来构建客户端库,您的Webpack
配置文件(可能位于js/webpack.config.json)声明了
jupyter-js-widgets作为外部依赖项。您需要在notebook的捆绑包和
可嵌入的捆绑包中都更改此项。如果您只需要@jupyter-widgets/base,您的外部依赖项应为:
externals: ['@jupyter-widgets/base']
如果你同时需要 @jupyter-widgets/base 和 @jupyter-widgets/controls,请在数组中包含这两个包。
cookiecutter 模板提供了一个示例配置。
更新客户端代码#
如果你现在构建库的客户端代码,将会收到许多关于缺少 jupyter-js-widgets 依赖项的错误。你需要将每个 jupyter-js-widgets 的导入替换为 @jupyter-widgets/base 的导入(或者可能是 @jupyter-widgets/controls 的导入)。
您的导入语句现在应该类似于以下其中一种(取决于您通常如何导入其他模块):
widgets = require('@jupyter-widgets/base');
require(['@jupyter-widgets/base'], function (widgets) {});
import * as widgets from '@jupyter-widgets/base';
所有的窗口小部件模型也应该声明属性
_view_module_version 和 _model_module_version。一个最小模型现在看起来是这样的:
var HelloModel = widgets.DOMWidgetModel.extend({
defaults: _.extend(widgets.DOMWidgetModel.prototype.defaults(), {
_model_name: 'HelloModel',
_view_name: 'HelloView',
_model_module: 'example_module',
_view_module: 'example_module',
_model_module_version: '~1.0.0',
_view_module_version: '~1.0.0',
}),
});
要使嵌入功能正常工作,模块版本需要是语义版本范围
,与NPM上的发布版本匹配。最常见的方式是通过使用
~{{ version number }}来请求与您当前package.json中版本兼容的版本
,用于_model_module_version和_view_module_version。详情请参阅cookiecutter
模板。
由于您可能希望避免在每个组件中重复模块版本,常见的做法是从package.json导入版本并
添加一个~前缀。请参阅
ipyvolume
作为示例。如果您这样做,请确保您的webpack配置
包含了JSON加载器。请参阅ipyvolume的
Webpack配置作为示例。
更新笔记本扩展#
之前,笔记本扩展(通常是 js/src/extension.js)需要在 requirejs 的配置中定义 jupyter-js-widgets。这
不再需要。请查看 cookiecutter
模板
以获取正确 requirejs 配置的示例。
更新Python代码#
所有小部件需要声明以下六个特征:
class ExampleWidget(widgets.Widget):
_model_name = Unicode('name of model in JS')
_view_name = Unicode('name of view in JS')
_model_module = Unicode('name your JS package')
_view_module = Unicode('name your JS package')
_model_module_version = Unicode('version of your JS bundle')
_view_module_version = Unicode('version of your JS bundle')
您的部件可能已经声明了 _model_name、
_view_name、_model_module 和 _view_module。_model_module 和
_view_module 应该是您在 NPM 上的包名称(即
package.json 中 name 字段的值)。_model_module_version 和
_view_module_version 应该是您的 JavaScript 客户端的版本(即
package.json 中 version 字段的值)。
_model_module_version 和 _view_module_version 用于在嵌入部件时查找您的
JavaScript 包。嵌入管理器将在找到部件时寻找位于
https://cdn.jsdelivr.net/npm/<module-name>@<module-version>/dist/index.js
的包。
更新嵌入式组件#
现在有两种选项可将小部件嵌入到笔记本之外的HTML页面中。
嵌入标准组件#
如果您只是嵌入ipywidgets附带的默认小部件,那么您只需包含以下脚本标签:
<script
src="https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager@*/dist/embed.js"
crossorigin="anonymous"
></script>
如果你想使用特定版本的embedder,你可以将@*替换为一个semver范围,例如@^0.9.0
使用RequireJS嵌入自定义小部件#
为了嵌入第三方小部件,您可以使用基于RequireJS的嵌入方式。首先,确保RequireJS已在页面上加载,例如:
<!-- Load require.js. Delete this if your page already loads require.js -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"
integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA="
crossorigin="anonymous"
></script>
然后包含以下脚本,它定义了嵌入库并运行渲染小部件的函数:
<script
src="https://cdn.jsdelivr.net/npm/@jupyter-widgets/html-manager@*/dist/embed-amd.js"
crossorigin="anonymous"
></script>
如果你想使用特定版本的embedder,你可以将@*替换为一个semver范围,例如@^0.9.0
如果你需要在不使用RequireJS的情况下嵌入自定义部件,你需要编译包含第三方库的自定义嵌入javascript。