Help using the Figure
object in a custom Bokeh extension
#12220
-
I would like to use the I have updated my tsconfig.json file to include the path to the module I would like to use, which does not seem to work. Note, all referenced files are below. The bokeh build command runs without error in the extension directory, but when I load it in a JupyterLab session I get the following error in my browser’s console. Uncaught Error: Cannot find module '@bokehjs/api/main' The JS, Python and Jupyter commands are shown below. Any help regarding how to correctly set up the paths so that I can find the Corresponding discourse link: https://discourse.bokeh.org/t/using-the-figure-object-in-a-custom-bokeh-extension/9358 files
{
"compilerOptions": {
"alwaysStrict": true,
"baseUrl": ".",
"declaration": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"importHelpers": false,
"lib": ["es2020", "dom", "dom.iterable"],
"module": "ES2020",
"moduleResolution": "node",
"noEmitOnError": false,
"noErrorTruncation": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "./dist/lib",
"paths": {
"@bokehjs/*": [
"node_modules/@bokeh/bokehjs/build/js/lib/api/*",
"node_modules/@bokeh/bokehjs/build/js/types/api/*",
"node_modules/@bokeh/bokehjs/build/js/lib/*",
"node_modules/@bokeh/bokehjs/build/js/types/*"
]
},
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strictBindCallApply": false,
"strictFunctionTypes": false,
"strictNullChecks": true,
"strictPropertyInitialization": false,
"target": "ES2020"
},
"include": ["./**/*.ts"]
}
import {HTMLBox, HTMLBoxView} from '@bokehjs/models/layouts/html_box';
import * as p from '@bokehjs/core/properties';
import {ColumnDataSource} from '@bokehjs/models/sources/column_data_source';
import {build_view} from '@bokehjs/core/build_views';
import {div} from '@bokehjs/core/dom';
import * as Bkapi from '@bokehjs/api/main';
const linearRange = (start: number, stop: number, step: number = 1): number[] => {
return Array.from({length: stop - step - start / step + 1}, (_, i) => {
return start + i * step;
});
};
export class Marginal1dView extends HTMLBoxView {
// Declare public writeable properties.
model: Marginal1d;
containerDiv: HTMLDivElement;
plotDiv: HTMLDivElement;
fig: Bkapi.Plotting.Figure;
// Initialize the properties.
initialize(): void {
super.initialize();
this.containerDiv = div({style: 'height: 100%; width: 100%'});
this.plotDiv = div({});
this.containerDiv = div({style: 'height: 100%; width: 100%'}, this.plotDiv);
}
// Render the view.
async render(): Promise<void> {
super.render();
this.el.appendChild(this.containerDiv);
await this.setPlot();
}
private async setPlot() {
// Compute the data for the initial view
const x = linearRange(0, 300, 10);
const y: number[] = [];
for (let i = 0; i < x.length; i += 1) {
y.push(Math.sin(x[i]));
}
// Create the source for the initial view
const source = new ColumnDataSource({data: {x: x, y: y}});
// Create the figures
this.fig = new Bkapi.Plotting.Figure({
background_fill_color: null,
border_fill_color: null,
outline_line_color: null,
min_border: 0,
sizing_mode: 'stretch_both',
toolbar_location: null,
});
// Add the glyphs to the figure
this.fig.line({
x: {field: 'x'},
y: {field: 'y'},
source: source,
});
// Render the figure
const view = await build_view(this.fig);
this.plotDiv.innerHTML = '';
view.renderTo(this.plotDiv);
}
}
export namespace Marginal1d {
export type Attrs = p.AttrsOf<Props>;
export type Props = HTMLBox.Props;
}
export interface Marginal1d extends Marginal1d.Attrs {}
export class Marginal1d extends HTMLBox {
properties: Marginal1d.Props;
__view_type__: Marginal1dView;
static __module__ = 'diagnostics.bokeh_extensions.marginal1d';
constructor(attrs?: Partial<Marginal1d.Attrs>) {
super(attrs);
}
static {
this.prototype.default_view = Marginal1dView;
}
}
from bokeh.models import HTMLBox
class Marginal1d(HTMLBox):
pass
from bokeh.io import output_notebook, show
from diagnostics import Marginal1d
output_notebook() marginal1d = Marginal1d()
output_notebook(hide_banner=True)
show(marginal1d) |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 2 replies
-
|
Beta Was this translation helpful? Give feedback.
-
This from bokeh.resources import Resources
cdn = Resources(mode="cdn", components=[
"bokeh", "bokeh-gl", "bokeh-widgets", "bokeh-tables", "bokeh-mathjax", "bokeh-api",
])
output_notebook(resources=cdn) should work as a workaround, but still fails to include api bundle. |
Beta Was this translation helpful? Give feedback.
-
FYI, tangentially, the PyScript work (I am not super familiar with) that is going on my be useful for things like this some day cc @philippjfr @jbednar |
Beta Was this translation helpful? Give feedback.
-
The solution is to build a plot using the different components found in BokehJS that you can use to create a figure. These include items like axes, grids, glyphs, etc. This means that you cannot use the import {LinearAxis} from '@bokehjs/models/axes/linear_axis';
import {Line} from '@bokehjs/models/glyphs/line';
import {Grid} from '@bokehjs/models/grids/grid';
import {Plot} from '@bokehjs/models/plots/plot';
import {DataRange1d} from '@bokehjs/models/ranges/data_range1d';
import {ColumnDataSource} from '@bokehjs/models/sources/column_data_source';
// Create the figure.
const figure = new Plot({
width: 500,
height: 500,
outline_line_color: 'black',
title: 'TITLE',
x_range: new DataRange1d({start: -1, end: 100}),
y_range: new DataRange1d({start: -1.2, end: 1.2}),
});
// Create and add an x-axis to the figure.
const xaxis = new LinearAxis({
axis_label: 'x-axis',
axis_line_color: 'black',
minor_tick_line_color: 'grey',
});
figure.add_layout(xaxis, 'below');
// Create and add a y-axis to the figure.
const yaxis = new LinearAxis({
axis_label: 'y-axis',
axis_line_color: 'black',
minor_tick_line_color: 'grey',
});
figure.add_layout(yaxis, 'left');
// Create and add a grid along the x-axis to the figure.
const xgrid = new Grid({
ticker: xaxis.ticker,
dimension: 0,
grid_line_alpha: 0.3,
grid_line_color: 'grey',
grid_line_width: 0.3,
});
figure.add_layout(xgrid);
// Create and add a grid along the y-axis to the figure.
const ygrid = new Grid({
ticker: yaxis.ticker,
dimension: 1,
grid_line_alpha: 0.3,
grid_line_color: 'grey',
grid_line_width: 0.3,
});
figure.add_layout(ygrid);
// Create a glyph to add to the figure.
const glyphName = 'glyph'
const glyph = new Line({
x: {field: 'x'},
y: {field: 'y'},
line_color: 'black',
line_width: 2.0,
name: glyphName,
});
// Create data for the glyph.
const len = 100;
const start = 0;
const step = 1;
const x = Array.from({length: len}, (_, i) => {return start + i * step});
const y = x.map((value) => Math.sin(value));
const data = {x: x, y: y};
// Create a ColumnDataSource that contains data for the glyph.
const source = new ColumnDataSource({
data: {left: data.left, top: data.top, right: data.right, bottom: data.bottom}
});
// Add the glyph to the figure.
figure.add_glyph(glyph, source, {name: glyphName}); |
Beta Was this translation helpful? Give feedback.
The solution is to build a plot using the different components found in BokehJS that you can use to create a figure. These include items like axes, grids, glyphs, etc. This means that you cannot use the
Figure
object directly found in theapi
module, but you can build aPlot
using all the components found in theFigure
object. See the example below. This is what I had to do in order to get a figure to render in a custom extension, after creating and adding a glyph.