参考文献
概览
ImJoy插件文件本质上是包括一系列自定义块的html文件(受.vue
格式启发)。
如下是一个插件文件的典型组成。需要注意的是,这些块的顺序无关紧要,因此可以将块打乱。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<config lang="json">
** 该代码块以Json格式定义插件的属性**
</config>
《script lang="javascript">
** 该代码块以JavaScript 或 Python 格式编写插件功能**
</script>
<window lang="html">
** 该代码块以HTML 格式编写界面**(适用于window类型插件)
</window>
<style lang="css">
** 该代码块以CSS 格式定义界面样式**(适用于window类型插件)
</style>
<docs lang="markdown">
** 该代码块以Markdown 格式编写插件文档 **
</docs>
<attachment name="XXXXX">
** 该代码块用于存储文本数据**
</attachment>
其中,只有<config>
和《script>
这两个代码块是必须的,其他块都是可选的。
config块
使用 JSON 或 YAML 格式的字段来定义插件的通用属性。json
格式的配置为(注意JSON本身不支持注释,所以下面#号以后的内容只是为了这里方便说明,实际要去除;一些需要详细解释的字段会在下面单独说明):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<config lang="json">
{
"name": "Untitled Plugin", # 插件名称。它必须是唯一的,以避免与其他插件冲突
"type": "web-worker", # 插件类型,选项有:`web-worker`、`window`、`native-python` 和`web-python`
"ui": "image processing", # 配置在插件下方显示的内容,详细用法将在下面详述
"url": "", # 当前文件的路径,用户从插件存储库安装插件时需使用该url来下载该插件
"cover": "", # 插件封面图片的url,详细用法将在下面详述
"labels": [], # 定义一个“labels”列表来分类插件以允许基于语义标签进行搜索或过滤。
"authors": [], # 作者姓名列表。
"license": "", # 插件所采用的许可证
"repository": "", # 插件项目存储库的url路径
"website": "", # 插件项目网站的url
"tags": [], # 插件所支持的配置标签,详细用法将在下面详述
"version": "0.1.0", # 指定插件版本
"api_version": "0.1.6", # 指定插件所基于的 ImJoy 的 api 版本。
"description": "A plugin for image processing.", # 插件的简短描述
"flags": ["functional"], # 用来控制插件行为,详细用法将在下面详述
"icon": "extension", # 定义插件菜单的图标,详细用法将在下面详述
"inputs": null, # 定义用来触发插件的输入条件,比如拖进来一个文件,详细用法将在下面详述
"outputs": null, # 定义插件的输出,详细用法将在下面详述
"cmd": "python", # 仅适用于python插件,用于运行插件的命令。默认情况下,它将使用`python`运行。根据安装的不同,它也可能是“python3”或“python27”等。
"env": null, # 仅适用于python插件,虚拟环境或docker镜像命令,用于创建插件运行的环境。具体用法见另一篇博客的“指定依赖”一节
"permissions": [], # 指定权限,详细用法将在下面详述
"requirements": [], # 定义插件依赖,具体用法见另一篇博客的“指定依赖”一节
"dependencies": [], # 指定当前插件依赖的其他ImJoy插件,详细用法将在下面详述
"default": {}, # 仅适用于window插件,定义窗口的默认值,详细用法将在下面详述
"base_frame": null, # 仅适用于window插件,定义在该窗口插件中内嵌的外部html的url路径,详细用法将在下面详述
"runnable": true # 定义插件是否可以通过点击插件菜单来执行,详细用法将在下面详述
}
</config>
而yaml
格式的配置为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<config lang="yaml">
name: Untitled Plugin
type: web-worker
tags: []
ui: image processing
cover: ''
version: 0.1.0
api_version: 0.1.6
description: A plugin for image processing.
icon: extension
inputs:
outputs:
env:
permissions: []
requirements: []
dependencies: []
</config>
cover字段
cover
字段用来指定插件封面图片的url,它将显示在图像安装对话框中,以及插件文档的顶部。
示例:"cover":"https://imjoy.io/static/img/imjoy-card-plain.png"
。
建议封面图片的纵横比为 16:9。 它可以托管在 GitHub 存储库中,此时应该使用图像的绝对路径URL。
可以使用多个图像,将 cover
设置为一个数组即可:"cover": ["url_to_image1", "url_to_image2", "url_to_image3"]
。
tags字段
tags
字段用来定义插件所支持的配置标签。
(注意:这里的tags
不是用于分类或分组目的,分类或分组可以使用labels
字段实现。)
这些定义的标签为插件执行提供了灵活的可配置的模式,比如可以配置插件是在 CPU 或 GPU 上运行。
以Unet Segmentation这个插件为例,如下是安装该插件时的界面及其源代码:
可以看出,该插件在tags字段中定义了CPU
、GPU
、Windows-CPU
、Windows-GPU
四个标签,那么相应地,就可以在后面的环境配置和依赖库解析时,对其进行不同的要求:1
2
3
4
5
6
7
8
9
10
11
12"env":
{"CPU": ["conda config --add channels conda-forge", "conda create -n tf-cpu python=3.6"],
"GPU": ["conda config --add channels conda-forge", "conda create -n tf-gpu python=3.6"],
"Windows-CPU": ["conda config --add channels conda-forge", "conda create -n tf-cpu python=3.6"],
"Windows-GPU": ["conda config --add channels conda-forge", "conda create -n tf-gpu python=3.6"]
},
"requirements":
{"CPU": ["repo:https://github.com/zhixuhao/unet", "pip:h5py scikit-image keras==2.1.4 numpy==1.18.0 tensorflow==1.15.4"],
"GPU": ["repo:https://github.com/zhixuhao/unet", "pip:h5py scikit-image keras==2.1.4 numpy==1.18.0 tensorflow-gpu==1.15.4"],
"Windows-CPU": ["repo:https://github.com/zhixuhao/unet", "pip:h5py scikit-image keras==2.1.4 numpy==1.18.0 tensorflow==1.15.4"],
"Windows-GPU": ["repo:https://github.com/zhixuhao/unet", "pip:h5py scikit-image keras==2.1.4 numpy==1.18.0 tensorflow-gpu==1.15.4"]
},
如果插件定义了tags,它们将出现在代码编辑器的顶部以及安装过程中。如果使用url
分发插件,还可以具体指定插件将使用哪个标签安装。
在<config>
代码块中,以下字段可以被tags所配置:env
、requirements
、dependencies
、icon
、ui
、type
、flags
、cover
。<script>
代码块也可以被tags配置,此时必须把tags
字段作为属性放到<script>
中,同时注意原来的lang
属性还要保留。比如:开发者可能想在插件的稳定版和开发版之间自由切换。那么可以定义这样的标签: "tags": ["stable", "dev"]
,同时定义两个脚本块:<script lang="python" tag="stable">
和 <script lang="python "tag="dev">
。在开发和测试插件时,ImJoy 编辑器会识别插件有多个标签,此时选择其中一个标签,那么加载插件时就会加载此标签下的相应代码。
ui字段
ui
字段是为了配置在插件下方显示的内容。
以HPA Classification插件为例,安装完成后,在ImJoy的左侧插件区,点击插件右侧的箭头,会看到ui
字段定义的内容:
以下是ui
字段的一个详细用法示例:
包含了各种输入框的用法,以及同一个ui的三种写法:option1
、option11
、option12
。
对于每个元素,都定义了一个唯一的id
,然后可以使用 ctx.config.id
来访问插件中此元素的值。
flags字段
flags
字段定义一个标志数组,用来控制插件的行为。
一个重要的flag是 functional
:functional
标志表示插件暴露的所有api函数都是纯函数。这意味着它们的输出将仅取决于输入。同时纯函数保证在调用任何插件 api 函数之后对插件没有副作用。这意味着应该避免修改插件函数中的全局变量(不过一个例外是setup()
函数)。
使一个插件具有functional
标志,可以使调试更容易,重要的是其他插件或工作流调用functional
插件时能严格重现其行为。functional
插件对于 ImJoy 在将来执行并行化和批处理时至关重要。
作者还没有编写真正的测试来验证插件是否是functional
,所以当前需要确定自己的插件仅包含纯函数时才添加 functional
标志。
此外,flags
还支持运行时控制。这些标志允许用户界面和插件引擎如何处理 ImJoy 实例:
single-instance
(仅适用于 python 插件):在此标志下,Python引擎只会运行一个插件进程,即使从不同的浏览器或工作区调用了该插件。在这种情况下,不同的 ImJoy 实例将共享相同的插件进程。当插件需要独占有限的资源(例如 GPU)时,这尤其有用。allow-detach
(仅适用于 python 插件):在此标志下,允许插件进程与用户界面分离。这意味着当用户界面断开或关闭时,插件不会被杀死。然而,为了重新连接到这个进程,需要从同一个浏览器和相同的工作区重新连接,或添加single-instance
标志。比如:如果想制作一个插件,它可以在没有用户界面的情况下在后台运行,那就对该插件赋予"flags": ["single-instance", "allow-detach"]
。那么重新启动时,用户界面将自动重新连接到此进程。需要注意的是,如果多个 ImJoy 实例附加到此插件进程,每个实例都会调用setup()
函数。这可能会导致冲突,因此建议 (1) 将与接口相关的代码保留在setup()
中,例如api.register()
; (2)将只想每个进程运行一次的代码移动到插件类的__init__
函数中。
icon字段
定义插件菜单中使用的图标。可以选择以下格式:
(1)在https://material.io/tools/icons/找到图标,直接使用指定的名称即可;
实测使用该网站上的图标名称时,不能使用带空格的名称,以及全部要用小写。
(2)可以直接复制粘贴emoji符号,例如从这里。
(3)指定 JPEG、PNG 或 GIF 格式图像的 URL,推荐大小:64x64。
(4)如果设置为null
或""
,它会默认使用第一条的material icon的extension
图标。
inputs字段
定义用于触发插件的输入条件,包括文件输入或数据匹配模式。例如,当用户将某个后缀的文件拖放到工作区时,相应的插件被激活。
基本上可以使用标准的 json 模式(http://json-schema.org/)来验证输入数据对象。例如,要定义插件使用 png 文件,可以指定 "inputs": {"type": "object", "properties": {"name": {"type": "string", "maxLength" : 100}}, "required": ["name"]}
。在后台,ImJoy使用 ajv 库来验证对象。为了简化模式的使用,还使用以下关键字扩展了标准的 json 模式:
(1)file
:对于文件对象,使用 mime types 或文件扩展名。可以使用以下关键字之一:
mime
:常见的 mime 类型字符串(或列表)。例如{"file": {"mime": "image/png"}}
。还可以指定一个 mime 类型列表:"mime": ["image/png", "image/jpeg", "image/tiff"]
。ext
:文件扩展名字符串(或列表)。例如{"file": {"ext": "png"}}
,或者可以指定一个扩展名列表:{"file": {"ext": ["png", "jpg", " jpeg"]}}
。
(2)ndarray
:例如{"ndarray": {"shape": [10, 10], "dtype": "uint8"}}
。
shape
:数组的形状。需要为每个维度指定一个数字,或者使用null
表示该维度的任何大小。例如:{"type": "ndarray", "shape": [10, 10]}
,或者如果第一维可以是任意大小:{"type": "ndarray", "shape": [null,10]}
dtype
:数组的数据类型。例如:{"ndarray": {"dtype": "uint8"}}
或者如果同时支持uint8
和float32
:{"ndarray": {"dtype": ["uint8", " float32"]}}
。支持的dtype
是:["int8", "int16", "int32", "uint8", "uint16", "uint32", "float32", "float64", "array"]
。ndim
:维数。例如:{"ndarray": {"ndim": 2}}
或者如果同时支持 2D 和 3D:{"ndarray": {"ndim": [2, 3]}}
(3)对于通过 http 或其他协议暴露的远程 url,{"type": "url", "extension": "zarr"}
。
另外需注意,如果在模式中使用正则表达式来验证字符串,可能需要设置 maxLength
,否则它会非常慢,甚至在验证长字符串时可能会崩溃。例如,如果想匹配一个包含以.tiff
结尾的file_name
的对象,那么可以这样设置:{"properties": {"file_name": {"type": "string","pattern": ".*\\.tiff$", "maxLength": 1024}}}
。
outputs字段
使用 json-schema 语法 (http://json-schema.org/) 定义输出。
格式与inputs
相同。
permissions字段
对于 window
插件,可以声明以下权限:
- 摄像头camera
- 音乐数字接口midi
- 地理位置geolocation
- 麦克风microphone
- 加密媒体encryted-media
- 全屏full-screen
- 支付请求payment-request
例如,如果某个window
插件需要网络摄像头的访问权限,需要添加以下权限:1
“permissions”:[“camera”],
注意,摄像头和麦克风等设备只有在 ImJoy 为 https
时才能工作,这意味着如果是从https://imjoy.io 运行这个插件,那么是可以工作的,但如果使用的是自己托管的以http
协议访问的ImJoy服务器,那么它就不会运行。解决方法是使用隧道服务,例如使用 Telebit 或 ngrok 将 http
url 转换为 https
。
dependencies字段
该字段指定当前插件依赖的其他ImJoy插件。这些所依赖的插件将在安装过程中自动安装。
要定义依赖项,请使用以下格式:
1) 对于没有tag的依赖,使用 REPOSITORY:PLUGIN_NAME
或 PLUGIN_URL
,例如:imjoy-team/imjoy-plugins:Image Window
;
2) 对于带有指定tag的依赖,使用REPOSITORY:PLUGIN_NAME@TAG
或PLUGIN_URL@TAG
,例如:imjoy-team/imjoy-plugins:Unet Segmentation@GPU
。在这种情况下,标签“GPU”用于指定托管在 GitHub 存储库imjoy-team/imjoy-plugins
(https://github.com/imjoy-team/)上的名为`Unet Segmentation的插件。如果插件没有托管在GitHub上或者该GitHub仓库不是ImJoy插件仓库的标准格式(即在仓库的根目录中没有定义
manifest.imjoy.json文件),则可以直接使用 url,例如:
https://github.com/imjoy-team/imjoy-demo-plugins/blob/master/repository/3dDemos.imjoy.html`(标签可以用`@TAG`添加)。
default字段
仅适用于window
插件,用于定义一个对象的默认值。
例如,可以通过设置 "defaults": {"w": 10, "h": 7}
来指定默认窗口大小。
或者,可以使用 "defaults": {"fullscreen": true}
默认使窗口处于全屏模式。
要使窗口默认处于独立模式(全屏并与工作区分离),可以设置 "defaults": {"standalone": true}
。
如果要将窗口显示为对话框,设置 "defaults": {"as_dialog": true}
。
base_frame字段
仅适用于window插件,定义在该窗口插件中内嵌的外部html的url路径。
虽然可以在base_frame
字段中使用任何其他网站的url,但是为了Imjoy内核可以与该html进行通信,它需要满足以下条件:
(1)该网站需要允许嵌入,不过这并不总是能够有效,因为它们可能有严格的 内容安全策略的限制,通常是通过X-Content-Security-Policy
的header,或页面中的 <meta>
元素来实现该限制。要解决这个问题,如果你可以控制该站点,需要将 *.imjoy.io
添加到header中。
(2)在base_frame
里面,需要开启imjoy-rpc
协议。这可以按照 imjoy-core 仓库中的说明轻松搞定。参考“ImJoy RPC library to your website”这一部分进行操作,基本上就是需要导入imjoy-loader
,并加载imjoy RPC库,然后导出想公开给其他ImJoy插件的api。
完成上述操作后,就可以将第三方网站集成为一个ImJoy插件。
runnable
定义插件是否可以通过点击插件菜单来执行(默认情况下,所有插件都是runnable
)。
对于不单独运行的辅助插件,(例如,native-python
插件可以被window
插件调用,不一定由用户直接执行),设置"runnable": false
会向下移动插件到插件菜单的底部,并使其不可点击。
docs块
在该块中,使用 Markdown 语言来编写插件文档。
Markdown语言的介绍见这里。
注意,如果在文档中提供的链接将在另一个选项卡中打开,则 ImJoy 实例将继续运行。
window块
在该块中,使用HTML 代码来编写窗口的显示内容。
ImJoy 使用 vue.js 来解析插件文件,它强制要求仅有根元素存在于模板中。这意味着在<window>
块中必须使用一个div
来包装所有节点:1
2
3
4
5
6<window>
<div>
<p> line 1</p>
<p> line 2</p>
</div>
</window>
如下则不可以:1
2
3
4<window>
<p> line 1</p>
<p> line 2</p>
</window>
style块
在该块中,使用CSS 代码来编写窗口显示内容的样式。
以MNIST CNN插件为例,给出以上<window>
块和style
块的一个说明:
script块
该块中包含实际的插件代码。
插件可以用 JavaScript 或 Python 编写,一个最小的插件需要实现两个函数:setup()
和 run()
。有一个例外是那种辅助插件(用 "runnable": false
指定),它不需要 run()
函数。
(1)setup()
函数:在插件第一次加载和初始化时执行它。
(2)run()
函数:每次执行插件时都会调用。执行时,一个带有上下文(名为“ctx”)的对象object(Javascript插件)或字典dictionary(Python插件)将被传递到函数中。返回的结果将显示为一个新窗口或传递给工作流中的下一个 op
。更多内容请参见另一篇博客的 运行时插件 部分。
(3)可选:resume()
函数:仅适用于带有 allow-detach
标志的可分离 native-python
插件。当ImJoy 重新连接到正在运行的插件进程时,resume()
将被调用(而不是 setup()
) 。
(4)可选:update()
函数:将在操作的任何设置更改时调用。
(5)可选:exit()
函数:当插件被杀死时,函数exit
将被调用。
<script>
块的 lang
属性用于指定使用的编程语言:
(1)对于 Javascript,使用 <script lang="javascript"> ... </script>
(2)对于 Python,使用 <script lang="python"> ... </script>
对于 Javascript 插件,还支持 ES 模块,要启用它,将 type="module"
添加到<script>
标签中。例如:<script type="module" lang="javascript">...</script>
。<script>
也支持 tags
,有关信息参考上面的tags
字段的解析。