Notebook / Extension / Misc === > #ipython, jupyter notebook, python notebook, jupyterlab, elyra ###### tags: `Jupyter` ###### tags: `Jupyter`, `JupyterLab`, `Notebook`, `Elyra`, `Extension` :::success :bulb: **官方範例**:[[github] jupyterlab / extension-examples](https://github.com/jupyterlab/extension-examples) - [hello-world](https://github.com/jupyterlab/extension-examples/tree/master/hello-world) ::: <br> [TOC] <br> <hr> <br> # 官方範例 > [[github] jupyterlab / extension-examples](https://github.com/jupyterlab/extension-examples) > > - [hello-world](https://github.com/jupyterlab/extension-examples/tree/master/hello-world) > - [Commands](https://github.com/jupyterlab/extension-examples/blob/master/commands) > - [Command Palette](https://github.com/jupyterlab/extension-examples/blob/master/command-palette/README.md) > - [Main Menu](https://github.com/jupyterlab/extension-examples/blob/master/main-menu/README.md) > - [Settings](https://github.com/jupyterlab/extension-examples/blob/master/settings) > - [Launcher](https://github.com/jupyterlab/extension-examples/blob/master/launcher/README.md) > - [Widgets](https://github.com/jupyterlab/extension-examples/blob/master/widgets) > - [Main Widget / Content Header](https://github.com/jupyterlab/extension-examples/tree/master/contentheader) > - [Datagrid](https://github.com/jupyterlab/extension-examples/blob/master/datagrid/README.md) > - [React components](https://github.com/jupyterlab/extension-examples/blob/master/react-widget/README.md) > - [Kernel Messaging](https://github.com/jupyterlab/extension-examples/blob/master/kernel-messaging/README.md) > - [Kernel Output](https://github.com/jupyterlab/extension-examples/tree/master/kernel-output) > - [Toolbar Button](https://github.com/jupyterlab/extension-examples/blob/master/toolbar-button/README.md) > - [signals example](https://github.com/jupyterlab/extension-examples/blob/master/signals) > > - More > - [Context Menu](https://github.com/jupyterlab/extension-examples/blob/master/context-menu) > - [Custom Log Console](https://github.com/jupyterlab/extension-examples/blob/master/custom-log-console) > - [Log Messages](https://github.com/jupyterlab/extension-examples/blob/master/log-messages) > - [Documents](https://github.com/jupyterlab/extension-examples/blob/master/documents) > - [State](https://github.com/jupyterlab/extension-examples/blob/master/state) > - [Completer](https://github.com/jupyterlab/extension-examples/blob/master/completer) > > - Backend > - [Server Hello World](https://github.com/jupyterlab/extension-examples/blob/master/server-extension) ## [hello-world](https://github.com/jupyterlab/extension-examples/tree/master/hello-world), 2022/02/22 done - 介紹 cookiecutter - python 名稱:不能有 `-` (dash) - 專案架構說明 - 編譯 - `pip install -e .` 等同包含: - `jlpm install` - `jlpm build` (`src/*.ts` -> `lib/*.js` -> final code) - 安裝 - `jupyter labextension develop . --overwrite` 單純建立 symbolic link - 持續編譯 - `jlpm watch` 包含: 1. `jupyter labextension develop . --overwrite` 2. `jlpm build` - 開啟 jupyterlab - `jupyter lab --watch` `--watch` 感覺可有可無 <br> ## [Commands](https://github.com/jupyterlab/extension-examples/blob/master/commands), 2022/02/22 done - 帶參數 - 呼叫 ```typescript commands.execute(command, { origin: 'init' }) ``` - 接收 ```typescript execute: (args: any) => { ... }, ``` <br> ## [Command Palette](https://github.com/jupyterlab/extension-examples/blob/master/command-palette/README.md), 2022/02/22 done | Object | API | | ------ | ----| | ICommandPalette | [addItem({command, arg, category, rank})](https://jupyterlab.github.io/jupyterlab/interfaces/_apputils_src_index_.ipaletteitem.html) | <br> <hr> <br> # CSS - 引用範例 - [jupyterlab/packages/apputils/style/base.css](https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/style/base.css) ``` ... @import './dialog.css'; ... ``` <br> <hr> <br> # CommandPalette ## 如何顯示左側欄的 指令面板(Command Palette)? ![](https://i.imgur.com/AiXB0hr.jpg) - 啟用左側欄選項: 1. 進入 Menu: Settings > Command Palette 2. 將 model 設為 ==**false**== - 說明: JupyterLab 3.0 預設不顯示左側欄; 可透過快捷鍵 Ctrl + Shift + C 來顯示小視窗 ## 範例 ![](https://i.imgur.com/HRyZGTf.png) log: ![](https://i.imgur.com/q6FojZd.png) ```typescript= import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; import { ICommandPalette } from '@jupyterlab/apputils'; const plugin: JupyterFrontEndPlugin<void> = { id: 'my_test1:plugin', autoStart: true, requires: [ICommandPalette], activate: (app: JupyterFrontEnd, palette: ICommandPalette) => { console.log('my_test1 is activated!'); app.commands.addCommand('say hello', { label: 'Say Hello', execute: () => { console.log('Hello'); } }); palette.addItem({ command: 'say hello', category: 'My Category' }); } }; export default plugin; ``` - command ID 為字串,可以包含空白字元 - command ID 如果不存在,palette UI 就不會出現該 command 選項 - 取得頁面焦點 (focus) 是透過: ``` // 設成啟用中的頁面 app.shell.activateById(widget.id) ``` <br> <hr> <br> # Launcher ## 官方範例 - ### [jupyterlab / extension-examples / launcher](https://github.com/jupyterlab/extension-examples/tree/master/launcher) - [schema/plugin.json](https://github.com/jupyterlab/extension-examples/blob/master/launcher/schema/plugin.json) - [style/Python-logo-notext.svg](https://github.com/jupyterlab/extension-examples/tree/master/launcher/style) - [src](https://github.com/jupyterlab/extension-examples/tree/master/launcher/src) ## 簡化版 ![](https://i.imgur.com/do2yhoK.png) ![](https://i.imgur.com/hCv4t0M.png) 1. 準備 icon,並放到 [style](https://github.com/jupyterlab/extension-examples/tree/master/launcher/style) ![](https://i.imgur.com/eqdaOFG.png =20%x) - [線上轉檔](https://www.aconvert.com/tw/image/png-to-svg/):png -> svg (png2svg, png to svg) 2. [`src/svg.d.ts`](https://github.com/jupyterlab/extension-examples/blob/master/launcher/src/svg.d.ts) (沒有變更) ```typescript= declare module '*.svg' { const value: string; export default value; } ``` 3. `src/index.ts` ```typescript= import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; import { ILauncher } from '@jupyterlab/launcher'; // also: // $ jlpm add @jupyterlab/launcher import { LabIcon } from '@jupyterlab/ui-components'; import iconStr from '../style/parabricks-logo.svg'; const PLUGIN_ID = 'launcher:plugin'; const COMMAND_ID = 'launcher:launch-parabricks'; /** * Initialization data for the launcher extension. */ const plugin: JupyterFrontEndPlugin<void> = { id: PLUGIN_ID, autoStart: true, requires: [ILauncher], activate: (app: JupyterFrontEnd, launcher: ILauncher) => { console.log('launcher is activated!'); // Parabicks icon const icon = new LabIcon({ name: 'launcher:parabricks-icon', svgstr: iconStr, }); // 向系統註冊指令 app.commands.addCommand(COMMAND_ID, { label: 'Parabricks', caption: 'Launch Parabricks', icon: icon, execute: async (args) => { alert('Parabricks was launched.') }, }); // 在 launcher 頁面長出選項,點了 icon 後執行 command // 限定變數只能是 `command` ? const command = COMMAND_ID; if (launcher) { launcher.add({ command, category: 'Nvidia', rank: 1, }); } } }; export default plugin; ``` <br> <hr> <br> # Widget > [[doc]](https://jupyterlab.github.io/lumino/widgets/classes/widget.html) > - [IOptions](https://jupyterlab.github.io/lumino/widgets/interfaces/widget.ioptions.html) :::warning :warning: **注意事項** - 建立 widget ```javascript if (!widget || widget.isDisposed) { // create a new widget } ``` - 更新 widget [update()](https://jupyterlab.github.io/lumino/widgets/classes/widget.html#update) -> [onUpdateRequest()](https://jupyterlab.github.io/lumino/widgets/classes/widget.html#onupdaterequest) - 首次建立 widget, `onUpdateRequest()` 自動會被呼叫 3 次 (不清楚原因) ::: ## 官方範例 - ### [jupyterlab / extension-examples / widgets](https://github.com/jupyterlab/extension-examples/tree/master/widgets) ![](https://i.imgur.com/VALK6JU.png) ## iconClass ### 內建 > 定義在:`packages/ui-components/style/deprecated.css` - `jp-JupyterIcon` ![](https://i.imgur.com/wHgHKb4.png) - packages/ui-components/style/deprecated.css: `.jp-JupyterIcon {` - packages/apputils-extension/src/workspacesplugin.ts: `const ICON_NAME = 'jp-JupyterIcon';` - `jp-LinkIcon` ![](https://i.imgur.com/G7o6huG.png) - `jp-AddIcon` ![](https://i.imgur.com/STN5btc.png) - `jp-CodeIcon` ![](https://i.imgur.com/LqzUYWH.png) ### 自行實作 - `icon-ipynb.png` ![](https://i.imgur.com/L9TPolz.png =25%x) - `base.css` ``` .jp-ipynb-icon { background-image: url(icon-ipynb.png); } ``` - index.ts ``` widget.title.iconClass = 'jp-ipynb-icon'; ``` ## localization - [Internationalization and Localization](https://jupyterlab.readthedocs.io/en/stable/extension/internationalization.html) <br> <hr> <br> # Dialog ## 官方原始碼 ### apputils/src/dialog.tsx - [Dialog](https://github.com/jupyterlab/jupyterlab/blob/59c1ee11b0443c407c155b8a2cee72728221e12c/packages/apputils/src/dialog.tsx#L79) - [showDialog](https://github.com/jupyterlab/jupyterlab/blob/59c1ee11b0443c407c155b8a2cee72728221e12c/packages/apputils/src/dialog.tsx#L25) ### apputils/src/inputdialog.ts - [InputPasswordDialog](https://github.com/jupyterlab/jupyterlab/blob/59c1ee11b0443c407c155b8a2cee72728221e12c/packages/apputils/src/inputdialog.ts#L333) ## [User Interface Helpers](https://jupyterlab.readthedocs.io/en/stable/extension/ui_helpers.html) > 官方提供的簡易 dialog API ### getText > request a short text. ![](https://i.imgur.com/v7TRPfO.png) ```typescript= import { InputDialog } from '@jupyterlab/apputils' ... activate: (app: JupyterFrontEnd) => { console.log('JupyterLab extension myextension3 is activated!'); // Request a text InputDialog.getText({ title: 'Provide your email' }) .then(value => { console.log('text ' + value.value); }); ... ``` ### show hello ![](https://i.imgur.com/VkT2HZA.png) ```typescript= import { showDialog, Dialog, } from '@jupyterlab/apputils' ... activate: (app: JupyterFrontEnd) => { const title = 'tj_tsai'; const body = 'hello, my name is TJ'; const buttons = [Dialog.okButton({ label: 'Dismiss' })]; showDialog({ title: title, body: body, buttons: buttons }); } ... ``` ### show error ``` import { showErrorMessage } from '@jupyterlab/apputils'; ... showErrorMessage('Error', reason); ``` - [doc](https://jupyterlab.readthedocs.io/en/stable/api/modules/apputils.html#showerrormessage) ### custom layout > 需求來源:[[PrimeHub] Submit a Notebook as a Job](https://docs.primehub.io/docs/zh-tw/notebook-as-job-cht) > - 操作範例 > ![](https://i.imgur.com/HJrTilm.png) > - [InfuseAI //primehub-job](https://github.com/InfuseAI/primehub-job/tree/master/jupyterlab_primehub) ![](https://i.imgur.com/SFZUhO6.png) ```typescript= import { JupyterFrontEnd, JupyterFrontEndPlugin } from '@jupyterlab/application'; import { showDialog, Dialog, } from '@jupyterlab/apputils' import { Widget } from '@lumino/widgets'; class FormDialogBase extends Widget implements Dialog.IBodyWidget<string> { constructor() { super(); const form = document.createElement('div'); form.innerHTML = ` <label>Instance type</label>: <br><input type='text' value='vCPUx1'> <br> <br><label>Image</label>: <br><input type='text' value='ml-sklearn'> <br> <br><label>Job name</label>: <br><input type='text' value='penguin-job'> `; this.node.appendChild(form); } } /** * Initialization data for the myextension3 extension. */ const plugin: JupyterFrontEndPlugin<void> = { id: 'myextension3:plugin', autoStart: true, activate: (app: JupyterFrontEnd) => { console.log('JupyterLab extension myextension3 is activated!'); const title = 'Submit Notebook as Job'; const body = new FormDialogBase(); const buttons = [Dialog.okButton({ label: 'Submit' })]; showDialog({ title: title, body: body, buttons: buttons }); } }; export default plugin; ``` <br> <hr> <br> # Event - ### [@lumino/signaling](https://jupyterlab.github.io/lumino/signaling/classes/signal.html) > emit, connect - ### [How to use events in jupyterlab extensions?](https://stackoverflow.com/questions/69657393/) - ### [jupyterlab-latex / src / index.ts](https://github.com/jupyterlab/jupyterlab-latex/blob/master/src/index.ts) ```typescript= // Hook up an event listener for when the '.tex' file is saved. const onFileChanged = () => { }; texContext.fileChanged.connect(onFileChanged); ``` <br> <hr> <br> # Toolbar (未消化) ## 官方範例 - ### [jupyterlab / extension-examples / toolbar-button](https://github.com/jupyterlab/extension-examples/tree/master/toolbar-button) <br> <hr> <br> # Log console (未消化) ## 官方範例 - ### [jupyterlab / extension-examples / custom-log-console](https://github.com/jupyterlab/extension-examples/tree/master/custom-log-console) ## 其他資料 - ### [How do I create a Jupyter Lab extension, that adds a custom button to the toolbar of a Jupyter Lab notebook?](https://stackoverflow.com/questions/63065310/) <br> <hr> <br> # Status Bar (未消化) ## 官方範例 - ### [jupyterlab / jupyterlab-statusbar](https://github.com/jupyterlab/jupyterlab-statusbar) <br> <hr> <br> # Token (未消化) ## [State Database](https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html#state-database) ```typescript= const id = 'foo-extension:IFoo'; const IFoo = new Token<IFoo>(id); interface IFoo {} class Foo implements IFoo {} const plugin: JupyterFrontEndPlugin<IFoo> = { id, autoStart: true, requires: [IStateDB], provides: IFoo, activate: (app: JupyterFrontEnd, state: IStateDB): IFoo => { const foo = new Foo(); const key = `${id}:some-attribute`; // Load the saved plugin state and apply it once the app // has finished restoring its former layout. Promise.all([state.fetch(key), app.restored]) .then(([saved]) => { /* Update `foo` with `saved`. */ }); // Fulfill the plugin contract by returning an `IFoo`. return foo; } }; ``` <br> <hr> <br> # Native editor ## [Letex](https://github.com/jupyterlab/jupyterlab-latex) > 有很多概念值得研究 :+1: :100: ![](https://i.imgur.com/ttSguvu.png) - `pip install -U jupyterlab_latex` - `jupyter serverextension enable --sys-prefix jupyterlab_latex` ### [simple.tex](http://www.maths.liv.ac.uk/~rakow/LatexGuide/very_simple.html) <br> <hr> <br> # Input/Output file ## [martinRenou / jupyterlab-fileopen](https://github.com/martinRenou/jupyterlab-fileopen) ## 參考資料 - ### [How To Write Text In Jupyter Notebook And IPython To File](https://www.dev2qa.com/how-to-write-text-in-jupyter-notebook-and-ipython-to-file/) - ### [How to read a local text file?](https://stackoverflow.com/questions/14446447) <br> <hr> <br> # right sidebar (未消化) ## [jupyterlab-contrib / jupyter-videochat](https://github.com/jupyterlab-contrib/jupyter-videochat/) ![](https://i.imgur.com/KSpEmPq.png) ![](https://i.imgur.com/UFmcyrE.png) <br> <hr> <br> # 與 OS 互動 - ### [martinRenou / jupyterlab-fileopen](https://github.com/martinRenou/jupyterlab-fileopen) <br> <hr> <br> # webpack.config.js 範例 - [jupyter-widgets / widget-ts-cookiecutter](https://github.com/jupyter-widgets/widget-ts-cookiecutter/blob/master/%7B%7Bcookiecutter.github_project_name%7D%7D/webpack.config.js) <br> <hr> <br> ## 代碼示例 - 純淨天空 (待研究) - [TypeScript apputils.ToolbarButton類代碼示例](https://vimsky.com/zh-tw/examples/detail/typescript-ex-%40jupyterlab.apputils-ToolbarButton---class.html) - event 處理方式 ``` node.addEventListener('contextmenu', (event: MouseEvent) => { event.preventDefault(); const model = widget.modelForClick(event); const menu = createContextMenu(model, commands, registry); menu.open(event.clientX, event.clientY); }); ```