Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inlined build for perspective-jupyterlab, improve PerspectiveWidget #1466

Merged
merged 7 commits into from
Jul 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ parameters:
jobs:
- job: 'WebAssembly'
pool:
vmImage: 'ubuntu-16.04'
vmImage: "ubuntu-latest"

strategy:
matrix:
Expand Down Expand Up @@ -274,7 +274,7 @@ jobs:

- job: 'Linux'
pool:
vmImage: 'ubuntu-16.04'
vmImage: "ubuntu-latest"

strategy:
matrix:
Expand Down Expand Up @@ -495,9 +495,6 @@ jobs:
- bash: jupyter labextension install $(System.DefaultWorkingDirectory)/packages/perspective-jupyterlab
displayName: "Install perspective-jupyterlab labextension"

- bash: yarn jlab_link
displayName: "Link local Perspective to Jupyterlab"

- bash: yarn test_js --jupyter --debug
displayName: "Run Jupyterlab tests"
env:
Expand Down
2 changes: 1 addition & 1 deletion binder/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ipywidgets==7.5.1
jupyterlab==3.0.9
jupyterlab==3.0.14
pandas==0.25.3
pyarrow==3.0.0
15 changes: 15 additions & 0 deletions examples/jupyter-notebooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Jupyter Notebook examples for `perspective-python`

This folder contains several notebooks designed as an introduction to the various features of `perspective-python`:

- `table_tutorial.ipynb` shows how to load, update, query, and serialize data using `Table` and `View`, and how to connect multiple `Table` instances together using `on_update`.
- `widget_tutorial.ipynb` demonstrates how to use `PerspectiveWidget` as a powerful interactive visualization component within a Jupyter notebook.
- `pandas_pivots.ipynb` displays Perspective's ability to read pivots from a pivoted DataFrame and automatically apply it as part of a `PerspectiveWidget`.

Each notebook is fully self-contained and should offer a good place to start for those interested in using `perspective-python` whether within a Jupyter environment or in a pure-Python context.

For examples pertaining to `perspective-python` Tornado servers, check out:

- [tornado-python](https://github.com/finos/perspective/tree/master/examples/tornado-python): a simple Tornado server that delivers a static dataset to the user using `perspective-python` and `<perspective-viewer>`.
- [tornado-streaming-python](https://github.com/finos/perspective/tree/master/examples/tornado-streaming-python): a streaming Tornado server that demonstrates `perspective-python`'s high throughput and performance in streaming scenarios.
- [workspace-editing-python](https://github.com/finos/perspective/tree/master/examples/workspace-editing-python): a full-featured example using `<perspective-workspace>` that illustrates a deep and powerful integration between `<perspective-workspace>` and `perspective-python`.
4 changes: 2 additions & 2 deletions examples/jupyter-notebooks/widget_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
"# Create a complex widget\n",
"w = PerspectiveWidget(\n",
" df,\n",
" plugin=\"heatmap\",\n",
" plugin=\"Heatmap\",\n",
" columns=[\"Sales\"],\n",
" row_pivots=[\"State\"],\n",
" sort=[[\"Sales\", \"desc\"]]\n",
Expand Down Expand Up @@ -254,4 +254,4 @@
},
"nbformat": 4,
"nbformat_minor": 4
}
}
2 changes: 1 addition & 1 deletion packages/perspective-jupyterlab/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@finos/perspective-jupyterlab",
"version": "0.9.0",
"description": "Perspective.js",
"description": "A Jupyterlab extension for the Perspective library, designed to be used with perspective-python.",
"files": [
"dist/*.d.ts",
"dist/*.js.map",
Expand Down
6 changes: 4 additions & 2 deletions packages/perspective-jupyterlab/src/config/plugin.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
externals: [/^([a-z0-9]|@(?!finos\/perspective-viewer))/],
externals: [/\@jupyter|\@lumino/],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include jupyter widgets external?

stats: {modules: false, hash: false, version: false, builtAt: false, entrypoints: false},
module: {
rules: [
Expand Down Expand Up @@ -77,7 +77,9 @@ module.exports = {
},
output: {
filename: "[name].js",
libraryTarget: "commonjs2",
library: {
type: "umd"
},
publicPath: "",
path: path.resolve(__dirname, "../../dist")
}
Expand Down
7 changes: 7 additions & 0 deletions packages/perspective-jupyterlab/src/less/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ div.PSPContainer-dark {
flex: 1;
}

// Widget height for Jupyterlab
.jp-NotebookPanel-notebook div.PSPContainer,
.jp-NotebookPanel-notebook div.PSPContainer-dark {
height: 520px;
}

// Widget height for Voila
.jp-OutputArea-output div.PSPContainer,
.jp-OutputArea-output div.PSPContainer-dark {
height: 520px;
}

div.PSPContainer perspective-viewer {
.perspective-viewer-material-dense();
--plugin--border: 1px solid #e0e0e0;
Expand Down
49 changes: 29 additions & 20 deletions packages/perspective-jupyterlab/src/ts/psp_widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export interface PerspectiveWidgetOptions extends PerspectiveViewerOptions {
server?: boolean;
title?: string;
bindto?: HTMLElement;
plugin_config?: PerspectiveViewerOptions;

// these shouldn't exist, PerspectiveViewerOptions should be sufficient e.g.
// ["row-pivots"]
Expand Down Expand Up @@ -66,7 +65,7 @@ export class PerspectiveWidget extends Widget {
const sort: Sort = options.sort || [];
const filters: Filters = options.filters || [];
const expressions: Expressions = options.expressions || options["expressions"] || [];
const plugin_config: PerspectiveViewerOptions = options.plugin_config || {};
const plugin_config: object = options.plugin_config || {};
const dark: boolean = options.dark || false;
const editable: boolean = options.editable || false;
const server: boolean = options.server || false;
Expand Down Expand Up @@ -128,21 +127,27 @@ export class PerspectiveWidget extends Widget {
}
}

save(): PerspectiveViewerOptions {
return this.viewer.save();
async toggleConfig(): Promise<void> {
if (this.isVisible) {
await this.viewer.toggleConfig();
}
}

async save(): Promise<PerspectiveViewerOptions> {
return await this.viewer.save();
}

restore(config: PerspectiveViewerOptions): Promise<void> {
return this.viewer.restore(config);
async restore(config: PerspectiveViewerOptions): Promise<void> {
return await this.viewer.restore(config);
}

/**
* Load a `perspective.table` into the viewer.
*
* @param table A `perspective.table` object.
*/
load(table: Table): void {
this.viewer.load(table);
async load(table: Table): Promise<void> {
await this.viewer.load(table);
}

/**
Expand All @@ -157,8 +162,8 @@ export class PerspectiveWidget extends Widget {
/**
* Removes all rows from the viewer's table. Does not reset viewer state.
*/
clear(): void {
this.viewer.table.clear();
async clear(): Promise<void> {
await this.viewer.table.clear();
}

/**
Expand All @@ -167,8 +172,8 @@ export class PerspectiveWidget extends Widget {
*
* @param data
*/
replace(data: TableData): void {
this.viewer.table.replace(data);
async replace(data: TableData): Promise<void> {
await this.viewer.table.replace(data);
}

/**
Expand Down Expand Up @@ -294,13 +299,20 @@ export class PerspectiveWidget extends Widget {
}
}

get plugin_config(): PerspectiveViewerOptions {
// `plugin_config` cannot be synchronously read from the viewer, as it is
// not part of the attribute API and only emitted from save(). Users can
// pass in a plugin config and have it applied to the viewer, but they
// cannot read the current `plugin_config` of the viewer if it has not
// already been set from Python.
get plugin_config(): object {
return this._plugin_config;
}
set plugin_config(plugin_config: PerspectiveViewerOptions) {
set plugin_config(plugin_config: object) {
this._plugin_config = plugin_config;

// Allow plugin configs passed from Python to take effect on the viewer
if (this._plugin_config) {
this.viewer.restore(this._plugin_config);
this.viewer.restore({plugin_config: this._plugin_config});
}
}

Expand Down Expand Up @@ -371,10 +383,6 @@ export class PerspectiveWidget extends Widget {
}
}

toggleConfig(): void {
this._viewer.toggleConfig();
}

static createNode(node: HTMLDivElement): HTMLPerspectiveViewerElement {
node.classList.add("p-Widget");
node.classList.add(PSP_CONTAINER_CLASS);
Expand Down Expand Up @@ -405,13 +413,14 @@ export class PerspectiveWidget extends Widget {
}
});
resize_observer.observe(node, {attributes: true});
viewer.toggleConfig();
}

return viewer;
}

private _viewer: HTMLPerspectiveViewerElement;
private _plugin_config: PerspectiveViewerOptions;
private _plugin_config: object;
private _client: boolean;
private _server: boolean;
private _dark: boolean;
Expand Down
1 change: 1 addition & 0 deletions packages/perspective-jupyterlab/src/ts/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export class PerspectiveDocumentWidget extends DocumentWidget<PerspectiveWidget>
this.context.model.fromString(resultAsB64);
this.context.save();
} else if (this._type === "json") {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result: any = await view.to_json();
this.context.model.fromJSON(result);
this.context.save();
Expand Down
9 changes: 9 additions & 0 deletions packages/perspective-jupyterlab/src/ts/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,15 @@ export class PerspectiveView extends DOMWidgetView {
}
}

/**
* When the View is removed after the widget terminates, clean up the
* client viewer and Web Worker.
*/
remove(): void {
this.pWidget.delete();
this.client_worker.terminate();
}

/**
* When traitlets are updated in python, update the corresponding value on
* the front-end viewer. `client` and `server` are not included, as they
Expand Down
4 changes: 1 addition & 3 deletions packages/perspective-jupyterlab/test/js/resize.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ utils.with_server({}, () => {
"resize.html",
() => {
test.capture(
"Basic widget functions",
"Config should show by default",
async page => {
await page.waitForSelector("perspective-viewer:not([updating])");
await page.evaluate(async () => await document.querySelector("perspective-viewer").toggleConfig());
await page.waitForSelector("perspective-viewer[settings]");
await page.waitForSelector("perspective-viewer:not([updating])");
},
{}
);
Expand Down
38 changes: 36 additions & 2 deletions packages/perspective-jupyterlab/test/jupyter/widget.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ utils.with_jupyterlab(process.env.__JUPYTERLAB_PORT__, () => {
return tbl.querySelector("thead tr").childElementCount;
});

expect(num_columns).toEqual(14);
expect(num_columns).toBeGreaterThanOrEqual(8);

const num_rows = await viewer.evaluate(async viewer => {
const tbl = viewer.querySelector("regular-table");
Expand Down Expand Up @@ -129,7 +129,7 @@ utils.with_jupyterlab(process.env.__JUPYTERLAB_PORT__, () => {
return tbl.querySelectorAll("tbody tr").length;
});

expect(num_columns).toEqual(13);
expect(num_columns).toEqual(12);
expect(num_rows).toEqual(5);
});

Expand Down Expand Up @@ -178,6 +178,40 @@ utils.with_jupyterlab(process.env.__JUPYTERLAB_PORT__, () => {
expect(plugin).toEqual("Y Line");
}
);

test.jupyterlab(
"Sets layout",
[["table = perspective.Table(arrow_data)\n", "w = perspective.PerspectiveWidget(table)"], ["w"], ["w.columns = ['f64']\n", "w.layout.width = '200px'\nw.layout.height = '100px'"]],
async page => {
await execute_all_cells(page);
await page.waitForTimeout(5000);
const container = await page.waitForSelector(".jp-OutputArea-output .PSPContainer", {visible: true});
await page.waitForTimeout(2500);
const dimensions = await container.evaluate(async container => {
const computed = getComputedStyle(container);
return [computed.width, computed.height];
});
expect(dimensions[0]).toEqual("200px");
expect(dimensions[1]).toEqual("100px");
}
);

test.jupyterlab(
"Sets plugin config",
[["table = perspective.Table(arrow_data)\n", "w = perspective.PerspectiveWidget(table)"], ["w"], ["w.columns = ['f64']\n", "w.plugin_config = {'f64': {'fixed': 10}}"]],
async page => {
const viewer = await default_body(page);
const plugin_config = await viewer.evaluate(async viewer => {
const config = await viewer.save();
return config.plugin_config;
});
expect(plugin_config).toEqual({
f64: {
fixed: 10
}
});
}
);
},
{name: "Simple", root: path.join(__dirname, "..", "..")}
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"resize_Basic_widget_functions": "7bb46cb439e6d4fbfdde6965d8fc2383",
"resize_Resize_the_container_causes_the_widget_to_resize": "91f8fa9f8ec317f0c656c6115807adb4",
"resize_row_pivots_traitlet_works": "686982074776272a55a4b3b63503125f",
"__GIT_COMMIT__": "455eea35adcda4ab55f4c778a9f4b16c1a019f46"
"resize_Resize_the_container_causes_the_widget_to_resize": "4ea2f9616992d3273a579532e1b9f25b",
"resize_row_pivots_traitlet_works": "2bda67e1502d83e55145fab4a4edacf9",
"resize_Config_should_show_by_default": "7bb46cb439e6d4fbfdde6965d8fc2383",
"__GIT_COMMIT__": "704d4ee796275b823d76579150353dfc9731b42a"
}
18 changes: 11 additions & 7 deletions packages/perspective-viewer/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ import {Table, View} from "@finos/perspective";

export interface HTMLPerspectiveViewerElement extends PerspectiveViewerOptions, HTMLElement {
load(data: Table): void;
notifyResize(): void;
delete(): Promise<void>;
flush(): Promise<void>;
getEditPort(): Promise<number>;
toggleConfig(): void;
save(): PerspectiveViewerOptions;
toggleConfig(): Promise<void>;
download(flat: boolean): Promise<any>;
copy(flat: boolean): Promise<void>;
save(): Promise<PerspectiveViewerOptions>;
restore(x: PerspectiveViewerOptions): Promise<void>;
reset(): void;
restore(x: any): Promise<void>;
notifyResize(): void;
restyleElement(): void;

readonly table?: Table;
readonly view?: View;
}
Expand All @@ -33,15 +36,16 @@ export type Pivots = string[];
export type Columns = string[];

export interface PerspectiveViewerOptions {
aggregates?: Aggregates;
editable?: boolean;
plugin?: string;
columns?: Columns;
expressions?: Expressions;
"row-pivots"?: Pivots;
"column-pivots"?: Pivots;
aggregates?: Aggregates;
filters?: Filters;
sort?: Sort;
expressions?: Expressions;
plugin_config?: object;
editable?: boolean;
selectable?: boolean;
}

Expand Down
Loading