Help with trying to subclass bokeh.models.Div

(this is distinct from an earlier post )

what i am trying to do:

subclass bokeh.models.Div so that I can make a few widgets.

what I did:

I know nothing about typescript, and I think I lack understanding of how subclassing from bokeh models on some basic level, so I tried to make a new Div by copying code from bokeh.models.Div:

Note that I copied the TypeScript code from here

import bokeh.layouts
from bokeh.io import curdoc
from bokeh.models import Div
from bokeh.util.compiler import TypeScript

TS_CODE = """
import {Markup, MarkupView} from "./markup"
import * as p from "core/properties"

export class TestDivView extends MarkupView {
  override model: TestDiv

  override render(): void {
    super.render()
    if (this.model.render_as_text)
      this.markup_el.textContent = this.model.text
    else
      this.markup_el.innerHTML = this.has_math_disabled() ? this.model.text : this.process_tex(this.model.text)
  }
}

export namespace TestDiv {
  export type Attrs = p.AttrsOf<Props>

  export type Props = Markup.Props & {
    render_as_text: p.Property<boolean>
  }
}

export interface TestDiv extends TestDiv.Attrs {}

export class TestDiv extends Markup {
  override properties: TestDiv.Props
  override __view_type__: TestDivView

  constructor(attrs?: Partial<TestDiv.Attrs>) {
    super(attrs)
  }

  static {
    this.prototype.default_view = TestDivView

    this.define<TestDiv.Props>(({Boolean}) => ({
      render_as_text: [ Boolean, false ],
    }))
  }
}
"""


class TestDiv(Div):
    def __init__(self, text):
        super(Div, self).__init__()
        __implementation__ = TypeScript(TS_CODE)


layout = bokeh.layouts.column(
    Div(text="this is a normal div"),
    Div(text="this is another normal div"),
    TestDiv(text="this is another normal div"),
)

curdoc().add_root(layout)

when I run this using:

bokeh serve --show test.py

I get a blank page. This is printed to the console:

Failed to repull session Error: Model 'bokeh_app_db2259ff22ff466a8789443be1eb674c.TestDiv' does not exist. This could be due to a widget or a custom model not being registered before first usage."
error
(anonymous function) — bokeh.min.js:39251
asyncFunctionResume
(anonymous function)
promiseReactionJobWithoutPromise
promiseReactionJob
bokeh.min.js:39124

any help appreciated. i’ve pored over the docs, but what I’m trying to do is much simpler – perhaps a extremely naive and minimal example would help?

Fors starters at least, __implementation__ must be a class variable, not a local variable inside _init__.

perhaps a extremely naive and minimal example would help?

There is nothing simpler than the inline example in the Extending Bokeh chapter. Creating a custom extension is inherently a very advanced topic.

thank you so much @Bryan! you gave me the missing piece.

working code:

import bokeh.layouts
from bokeh.io import curdoc
from bokeh.models import Div
from bokeh.util.compiler import TypeScript

TS_CODE = """
import {Markup, MarkupView} from "models/widgets/markup"
import * as p from "core/properties"

export class TestDivView extends MarkupView {
  override model: TestDiv

  override render(): void {
    super.render()
    if (this.model.render_as_text)
      this.markup_el.textContent = this.model.text
    else
      this.markup_el.innerHTML = this.model.text
  }
}

export namespace TestDiv {
  export type Attrs = p.AttrsOf<Props>

  export type Props = Markup.Props & {
    render_as_text: p.Property<boolean>
  }
}

export interface TestDiv extends TestDiv.Attrs {}

export class TestDiv extends Markup {
  override properties: TestDiv.Props
  override __view_type__: TestDivView

  constructor(attrs?: Partial<TestDiv.Attrs>) {
    super(attrs)
  }

  static {
    this.prototype.default_view = TestDivView

    this.define<TestDiv.Props>(({Boolean}) => ({
      render_as_text: [ Boolean, false ],
    }))
  }
}
"""


class TestDiv(Div):

    __implementation__ = TypeScript(TS_CODE)

    def __init__(self, text="testdiv"):
        super(Div, self).__init__()
        self.text = text


layout = bokeh.layouts.column(
    Div(text="this is a normal div"),
    Div(text="this is another normal div"),
    TestDiv(text="wow so testdiv"),
)

curdoc().add_root(layout)


1 Like