Bokeh erweitern

Bokeh verfügt über eine Vielzahl von integrierten Typen, mit denen interaktive Visualisierungen und Datenanwendungen im Browser erstellt werden können. Es gibt jedoch spezifische Funktionen, die nicht in die Kernbibliothek aufgenommen wurden. Es gibt jedoch die Möglichkeit, Bokeh zu erweitern

  • um das Verhalten vorhandener Bokeh-Modelle zu verändern

  • um neue Modelle hinzuzufügen, die JavaScript-Bibliotheken von Drittanbietern mit Python verbinden

  • um spezialisierte Modelle für bestimmte Fachdomänen zu erstellen

Solche benutzerdefinierten Erweiterungen können mit Standard-Releases erstellt und verwendet werden. Dabei ist es nicht erforderlich, eine Entwicklungsumgebung einzurichten oder etwas aus den Sourcen zu erstellen.

Struktur von Bokeh-Modellen

Python-Modelle

JavaScript-Modelle und Ansichten

Während die Python-Seite meistens deklarativ ist, ohne viel oder echten Code, erfordert die JavaScript-Seite Code um das Modell zu implementieren. Gegebenenfalls muss auch Code für eine entsprechende Ansicht bereitgestellt werden.

Nachfolgend findet ihr eine kommentierte TypeScript-Implementierung für Custom und CustomView. Bei integrierten Modellen ist dieser Code direkt in den endgültigen BokehJS-Skripts enthalten. Im nächsten Abschnitt wird gezeigt, wie ihr diesen Code mit benutzerdefinierten Erweiterungen verknüpfen könnt.

Hinweis: BokehJS wurde ursprünglich in CoffeeScript geschrieben, wird jedoch nach TypeScript portiert. Dementsprechend ist die Anleitung hier in TypeScript. Benutzerdefinierte Erweiterungen können jedoch auch in CoffeeScript oder in reinem JavaScript geschrieben werden.

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 <div> elements by default, accessible as
    // ``this.el``. Many Bokeh views ignore this default <div>, and instead
    // do things like draw to the HTML canvas. In this case though, we change
    // the contents of the <div>, based on the current slider value.
    empty(this.el)
    this.el.appendChild(div({
      style: {
        'padding': '2px',
        'color': '#b88d8e',
        'background-color': '#2a3153',
      },
    }, `${this.model.text}: ${this.model.slider.value}`))
  }
}

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({
  text:   [ p.String ],
  slider: [ p.Any    ],
})

Zusammenfügen

Bei integrierten Bokeh-Modellen wird die Implementierung in BokehJS vom Build-Prozess automatisch mit dem entsprechenden Python-Modell abgeglichen. Um JavaScript-Implementierungen mit Python-Modellen zu verbinden, ist ein zusätzlicher Schritt erforderlich. Die Python-Klasse sollte über ein Klassenattribut __implementation__ verfügen, dessen Name der TypeScript-Code (oder JavaScript- oder CoffeeScript-Code) ist, der das clientseitige Modell sowie optionale Ansichten definiert.

Vorausgesetzt, der obige TypeScript-Code wurde in einer Datei custom.ts gespeichert, könnte die vollständige Python-Klasse folgendermaßen aussehen:

[1]:
from bokeh.core.properties import String, Instance
from bokeh.models import LayoutDOM, Slider

class Custom(LayoutDOM):

    __implementation__ = "custom.ts"

    text = String(default="Custom text")

    slider = Instance(Slider)

Wenn diese Klasse dann in einem Python-Modul custom.py definiert ist, kann die benutzerdefinierte Erweiterung jetzt genau wie jedes integrierte Bokeh-Modell verwendet werden:

[2]:
from bokeh.io import show, output_file
from bokeh.layouts import column
from bokeh.models import Slider

slider = Slider(start=0, end=10, step=0.1, value=0, title="value")

custom = Custom(text="Special Slider Display", slider=slider)

layout = column(slider, custom)

show(layout)

Integration in Bokeh Server

Es sind keine besonderen Arbeiten oder Modifikationen erforderlich, um benutzerdefinierte Erweiterungen in den Bokeh-Server zu integrieren. Wie bei Standalone-Dokumenten ist die JavaScript-Implementierung automatisch in der gerenderten Anwendung enthalten. Zusätzlich erfolgt die Standardsynchronisation der Bokeh-Modelleigenschaften, die für alle integrierten Modelle gilt, auch transparent für benutzerdefinierte Erweiterungen.

Beispiele

In der Bokeh-Dokumentation stehen einige vollständige Beispiele zur Verfügung, die als Referenz dienen sollen. In vielen Fällen ist es erforderlich, den Quellcode der Basisklassen in bokehjs/src/lib/models zu studieren.