Skip to content

Commit

Permalink
Basic support for widgets in lab code consoles
Browse files Browse the repository at this point in the history
  • Loading branch information
jtpio committed Mar 5, 2021
1 parent 98e6c86 commit 9d2e0b2
Show file tree
Hide file tree
Showing 4 changed files with 411 additions and 13 deletions.
1 change: 1 addition & 0 deletions jupyterlab_widgets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@jupyter-widgets/controls": "^4.0.0-alpha.2",
"@jupyter-widgets/output": "^5.0.0-alpha.2",
"@jupyterlab/application": "^3.0.0",
"@jupyterlab/console": "^3.0.0",
"@jupyterlab/docregistry": "^3.0.0",
"@jupyterlab/logconsole": "^3.0.0",
"@jupyterlab/mainmenu": "^3.0.0",
Expand Down
112 changes: 103 additions & 9 deletions jupyterlab_widgets/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// Distributed under the terms of the Modified BSD License.

import { ISettingRegistry } from '@jupyterlab/settingregistry';

import * as nbformat from '@jupyterlab/nbformat';

import { IConsoleTracker, CodeConsole } from '@jupyterlab/console';

import { DocumentRegistry } from '@jupyterlab/docregistry';

import {
Expand Down Expand Up @@ -34,7 +37,11 @@ import { AttachedProperty } from '@lumino/properties';

import { WidgetRenderer } from './renderer';

import { WidgetManager, WIDGET_VIEW_MIMETYPE } from './manager';
import {
WidgetManager,
WIDGET_VIEW_MIMETYPE,
KernelWidgetManager
} from './manager';

import { OutputModel, OutputView, OUTPUT_WIDGET_VERSION } from './output';

Expand All @@ -58,7 +65,7 @@ const SETTINGS: WidgetManager.Settings = { saveState: false };
/**
* Iterate through all widget renderers in a notebook.
*/
function* widgetRenderers(
function* notebookWidgetRenderers(
nb: Notebook
): Generator<WidgetRenderer, void, unknown> {
for (const cell of nb.widgets) {
Expand All @@ -74,6 +81,25 @@ function* widgetRenderers(
}
}

/**
* Iterate through all widget renderers in a console.
*/
function* consoleWidgetRenderers(
console: CodeConsole
): Generator<WidgetRenderer, void, unknown> {
for (const cell of toArray(console.cells)) {
if (cell.model.type === 'code') {
for (const codecell of (cell as CodeCell).outputArea.widgets) {
for (const output of toArray(codecell.children())) {
if (output instanceof WidgetRenderer) {
yield output;
}
}
}
}
}
}

/**
* Iterate through all matching linked output views
*/
Expand Down Expand Up @@ -140,13 +166,58 @@ export function registerWidgetManager(
});
}

export function registerConsoleWidgetManager(
console: CodeConsole,
rendermime: IRenderMimeRegistry,
renderers: IterableIterator<WidgetRenderer>
): DisposableDelegate {
let wManager = Private.widgetManagerProperty.get(console);
if (!wManager) {
wManager = new KernelWidgetManager(
console.sessionContext.session?.kernel!,
rendermime
);
WIDGET_REGISTRY.forEach(data => wManager!.register(data));
Private.widgetManagerProperty.set(console, wManager);
}

for (const r of renderers) {
r.manager = wManager;
}

// Replace the placeholder widget renderer with one bound to this widget
// manager.
rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE);
rendermime.addFactory(
{
safe: false,
mimeTypes: [WIDGET_VIEW_MIMETYPE],
createRenderer: options => new WidgetRenderer(options, wManager)
},
0
);

return new DisposableDelegate(() => {
if (rendermime) {
rendermime.removeMimeType(WIDGET_VIEW_MIMETYPE);
}
wManager!.dispose();
});
}

/**
* The widget manager provider.
*/
const plugin: JupyterFrontEndPlugin<base.IJupyterWidgetRegistry> = {
id: '@jupyter-widgets/jupyterlab-manager:plugin',
requires: [IRenderMimeRegistry],
optional: [INotebookTracker, ISettingRegistry, IMainMenu, ILoggerRegistry],
optional: [
INotebookTracker,
IConsoleTracker,
ISettingRegistry,
IMainMenu,
ILoggerRegistry
],
provides: base.IJupyterWidgetRegistry,
activate: activateWidgetExtension,
autoStart: true
Expand All @@ -165,6 +236,7 @@ function activateWidgetExtension(
app: JupyterFrontEnd,
rendermime: IRenderMimeRegistry,
tracker: INotebookTracker | null,
consoleTracker: IConsoleTracker | null,
settingRegistry: ISettingRegistry | null,
menu: IMainMenu | null,
loggerRegistry: ILoggerRegistry | null
Expand All @@ -179,7 +251,10 @@ function activateWidgetExtension(
const wManager = Private.widgetManagerProperty.get(nb.context);
if (wManager) {
wManager.onUnhandledIOPubMessage.connect(
(sender: WidgetManager, msg: KernelMessage.IIOPubMessage) => {
(
sender: WidgetManager | KernelWidgetManager,
msg: KernelMessage.IIOPubMessage
) => {
const logger = loggerRegistry.getLogger(nb.context.path);
let level: LogLevel = 'warning';
if (
Expand Down Expand Up @@ -226,7 +301,7 @@ function activateWidgetExtension(
panel.context,
panel.content.rendermime,
chain(
widgetRenderers(panel.content),
notebookWidgetRenderers(panel.content),
outputViews(app, panel.context.path)
)
);
Expand All @@ -238,7 +313,7 @@ function activateWidgetExtension(
panel.context,
panel.content.rendermime,
chain(
widgetRenderers(panel.content),
notebookWidgetRenderers(panel.content),
outputViews(app, panel.context.path)
)
);
Expand All @@ -247,6 +322,24 @@ function activateWidgetExtension(
});
}

if (consoleTracker !== null) {
consoleTracker.forEach(async panel => {
await panel.sessionContext.ready;
registerConsoleWidgetManager(
panel.console,
panel.console.rendermime,
chain(consoleWidgetRenderers(panel.console))
);
});
consoleTracker.widgetAdded.connect(async (sender, panel) => {
await panel.sessionContext.ready;
registerConsoleWidgetManager(
panel.console,
panel.console.rendermime,
chain(consoleWidgetRenderers(panel.console))
);
});
}
if (settingRegistry !== null) {
// Add a command for automatically saving (jupyter-)widget state.
commands.addCommand('@jupyter-widgets/jupyterlab-manager:saveWidgetState', {
Expand Down Expand Up @@ -321,10 +414,11 @@ namespace Private {
* A private attached property for a widget manager.
*/
export const widgetManagerProperty = new AttachedProperty<
DocumentRegistry.Context,
WidgetManager | undefined
DocumentRegistry.Context | CodeConsole,
WidgetManager | KernelWidgetManager | undefined
>({
name: 'widgetManager',
create: (owner: DocumentRegistry.Context): undefined => undefined
create: (owner: DocumentRegistry.Context | CodeConsole): undefined =>
undefined
});
}
11 changes: 7 additions & 4 deletions jupyterlab_widgets/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import { Panel, Widget as LuminoWidget } from '@lumino/widgets';

import { IRenderMime } from '@jupyterlab/rendermime-interfaces';

import { WidgetManager } from './manager';
import { LabWidgetManager } from './manager';
import { DOMWidgetModel } from '@jupyter-widgets/base';

/**
* A renderer for widgets.
*/
export class WidgetRenderer extends Panel
implements IRenderMime.IRenderer, IDisposable {
constructor(options: IRenderMime.IRendererOptions, manager?: WidgetManager) {
constructor(
options: IRenderMime.IRendererOptions,
manager?: LabWidgetManager
) {
super();
this.mimeType = options.mimeType;
if (manager) {
Expand All @@ -28,7 +31,7 @@ export class WidgetRenderer extends Panel
/**
* The widget manager.
*/
set manager(value: WidgetManager) {
set manager(value: LabWidgetManager) {
value.restored.connect(this._rerender, this);
this._manager.resolve(value);
}
Expand Down Expand Up @@ -125,6 +128,6 @@ export class WidgetRenderer extends Panel
* The mimetype being rendered.
*/
readonly mimeType: string;
private _manager = new PromiseDelegate<WidgetManager>();
private _manager = new PromiseDelegate<LabWidgetManager>();
private _rerenderMimeModel: IRenderMime.IMimeModel | null = null;
}
Loading

0 comments on commit 9d2e0b2

Please sign in to comment.