Extend Bokeh to render text with custm CSS to the main plot in real time (with ColumnDataSource)

Hi,

I am currently working on a Bokeh server app that needs to render text with custom CSS inside a plot in real time. I tried to do this with a Glyph Text object, however the styling attributes currently available are too limited. So I tried to make a Bokeh extension for it according to issue 6977, but I couldn’t gather exactly how to do that after reading the documentation.

I want to make an extension with custom CSS that displays text in the main plot through a CustomDataSource object, but I don’t know exactly how to add it to the plot. The custom extension class currently inherits from LayoutDOM, and contains an Instance(ColumnDataSource) object as an attribute. What I want to know is, what method do I need to call to add this custom class to the main plot? I’ve tried add_layout(), add_tools() and add_glyph() so far, but those didn’t work.

Here is my code for the extension:

class Custom(LayoutDOM, Renderer):

“”“Custom text class so CSS can be applied to it.”""

implementation = “custom.ts”

source = Instance(ColumnDataSource)

``

Here is my TypeScript for the extension (taken mostly from the example in the Extending Bokeh documentation):

import {div, empty} from “core/dom”

import * as p from “core/properties”

import {LayoutDOM, LayoutDOMView} from “models/layouts/layout_dom”

export class CustomView extends LayoutDOMView {

initialize(options) {

super.initialize(options)

this.render()

// Set BokehJS listener so that when the Bokeh slider has a change

// event, we can process the new data

// this.connect(this.model.slider.change, () => this.render())

}

render() {

// BokehjS Views create

elements by default, accessible as

// this.el. Many Bokeh views ignore this default

, and instead

// do things like draw to the HTML canvas. In this case though, we change

// the contents of the

, based on the current slider value.

empty(this.el)

for (var _i = 0; i < this.model.x.length; _i++ ) {

this.el.appendChild(div({

style: {

‘padding’: ‘1px’,

‘color’: ‘#000000’,

‘text-shadow’: ‘-1px 0 white, 0 1px white, 1px 0 white, 0 -1px white’,

},

}, ${this.model.source.text}))

}

// this.el.appendChild(div({

// style: {

// ‘padding’: ‘2px’,

// ‘color’: ‘#000000’,

// ‘text-shadow’: ‘-1px 0 white, 0 1px white, 1px 0 white, 0 -1px white’;

// },

// }, ${this.model.text}: ${this.model.text}))

}

}

export class Custom extends LayoutDOM {

// If there is an associated view, this is typically boilerplate.

default_view = CustomView

// The type class attribute should generally match exactly the name

// of the corresponding Python class.

type = “Custom”

}

// The @define block adds corresponding “properties” to the JS model. These

// should normally line up 1-1 with the Python model class. Most property

// types have counterparts, e.g. bokeh.core.properties.String will be

// p.String in the JS implementation. Any time the JS type system is not

// yet as complete, you can use p.Any as a “wildcard” property type.

Custom.define({

source: [ p.Any ],

})

``

Would really appreciate some help on how to make this work. Thank you!

Regards,

Shreya Gupta