class Custom(LayoutDOM): __implementation__ = TypeScript(CODE) text = String(default="Custom text") slider = Instance(Slider)
class ngl(LayoutDOM): # The special class attribute ``__implementation__`` should contain a string # of JavaScript code that implements the browser side of the extension model. # Below are all the "properties" for this model. Bokeh properties are # class attributes that define the fields (and their types) that can be # communicated automatically between Python and the browser. Properties # also support type validation. More information about properties in # can be found here:__implementation__ = TypeScript(TS_CODE) # # https://docs.bokeh.org/en/latest/docs/reference/core/properties.html#bokeh-core-properties __implementation__ = TypeScript(TS_CODE) # This is a Bokeh ColumnDataSource that can be updated in the Bokeh # server by Python code # The vis.js library that we are wrapping expects data for x, y, and z. # The data will actually be stored in the ColumnDataSource, but these # properties let us specify the *name* of the column that should be # used for each field. spin = Bool representation = String rscb = String no_coverage = String color_list = List(List(String)) pdb_string = String
class AudioButton(Button): ''' A button that implements client-side audio playback. ''' # TODO: Figure out why installation fails when the TypeScript code # is included as a separate .ts file. It isn't copied along with # the .py file. #__implementation__ = 'audio_button.ts' __implementation__ = TypeScript(TS_CODE) channels = List(String, help=""" The list of column names in `source` that identify the audio channels. """) end = Float(np.Inf, help=""" The time in seconds of the last sample in `source` for playback. """) fs = Float(help=""" The sample rate of the audio signal in `source`. """) source = Instance(ColumnDataSource, help=""" A dict of audio signal data, including at least one column of sample data and a column of sample times (named 'seconds'). """) start = Float(0.0, help=""" The time in seconds of the first sample in `source` for playback. """)
class JSMol(LayoutDOM): # The special class attribute ``__implementation__`` should contain a string # of JavaScript (or CoffeeScript) code that implements the JavaScript side # of the custom extension model. __implementation__ = TypeScript(TS_CODE) # Below are all the "properties" for this model. Bokeh properties are # class attributes that define the fields (and their types) that can be # communicated automatically between Python and the browser. Properties # also support type validation. More information about properties in # can be found here: # # https://bokeh.pydata.org/en/latest/docs/reference/core.html#bokeh-core-properties # This is a Bokeh Dict providing the JSMol "Info variable" # See http://wiki.jmol.org/index.php/Jmol_JavaScript_Object/Info info = Dict(String, String) # Currently (mis)using this to pass a script to be executed since there # doesn't seem to be a better solution (?) # https://groups.google.com/a/continuum.io/forum/#!topic/bokeh/0m17mNMnTys script_source = Instance(ColumnDataSource) # Path to JSMol javascript file (can be full or relative URL), e.g. # e.g. https://chemapps.stolaf.edu/jmol/jsmol/JSmol.min.js # or jsmol/JSmol.min.js js_url = String
class Surface3d(LayoutDOM): # The special class attribute ``__implementation__`` should contain a string # of JavaScript (or CoffeeScript) code that implements the JavaScript side # of the custom extension model. __implementation__ = TypeScript(TS_CODE) # Below are all the "properties" for this model. Bokeh properties are # class attributes that define the fields (and their types) that can be # communicated automatically between Python and the browser. Properties # also support type validation. More information about properties in # can be found here: # # https://bokeh.pydata.org/en/latest/docs/reference/core.html#bokeh-core-properties # This is a Bokeh ColumnDataSource that can be updated in the Bokeh # server by Python code data_source = Instance(ColumnDataSource) # The vis.js library that we are wrapping expects data for x, y, and z. # The data will actually be stored in the ColumnDataSource, but these # properties let us specify the *name* of the column that should be # used for each field. x = String y = String z = String
def impl(cls, name: str, fields: Dict[str, Tuple[str, ...]], extra: Union[None, str, Path] = None) -> TypeScript: "returns the typescript implementation" path = Path(__file__).with_suffix('.ts') if not path.exists(): return "" code = ''.join(open(path)) line = '\n' + ' ' * 3 internal = {i: j for i, j in fields.items() if i[0] == '_'} cumpy = {i: j for i, j in fields.items() if i[0] != '_'} for title, fcn, itms in [ ('.Props & {', lambda i, j: f'{i}: p.Property<{j[1]}>', fields), ('this.define<NAME.Props>({', lambda i, j: f'{i}: {j[0]},', cumpy) ]: repl = line + line.join(fcn(i, j) for i, j in itms.items()) code = code.replace(title, title + repl) if internal: repl = '{' + line + line.join( fcn(i, j) for i, j in internal.items())[:-1] + '}' code = code.replace('this.define', f'this.internal({repl})\n this.define') code = code.replace('NAME', name) if extra: code = cls.__impl_extra(code, extra) return TypeScript(code)
class LatexLabel(Label): """A subclass of `Label` with all of the same class attributes except canvas mode isn't supported and DOM manipulation happens in the TypeScript superclass implementation that requires setting `render_mode='css'`). Only the render method of LabelView is overwritten to perform the text -> latex (via katex) conversion """ __javascript__ = ["https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.js"] __css__ = ["https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.css"] __implementation__ = TypeScript(""" import {Label, LabelView} from "models/annotations/label" declare namespace katex { function render(expression: string, element: HTMLElement, options: {displayMode?: boolean}): void } export class LatexLabelView extends LabelView { model: LatexLabel render(): void { // Here because AngleSpec does units tranform and label doesn't support specs let angle: number switch (this.model.angle_units) { case "rad": { angle = -1 * this.model.angle break } case "deg": { angle = -1 * this.model.angle * Math.PI/180.0 break } default: throw new Error("unreachable") } const panel = this.panel || this.plot_view.frame const xscale = this.plot_view.frame.xscales[this.model.x_range_name] const yscale = this.plot_view.frame.yscales[this.model.y_range_name] const {x, y} = this.model let sx = this.model.x_units == "data" ? xscale.compute(x) : panel.xview.compute(x) let sy = this.model.y_units == "data" ? yscale.compute(y) : panel.yview.compute(y) sx += this.model.x_offset sy -= this.model.y_offset this._css_text(this.layer.ctx, "", sx, sy, angle) katex.render(this.model.text, this.el, {displayMode: true}) } } export class LatexLabel extends Label { static init_LatexLabel(): void { this.prototype.default_view = LatexLabelView } } """)
class Custom(HTMLBox): __implementation__ = TypeScript(CODE) text = String(default="Custom text") slider = Instance(Slider) margin = Override(default=5)
class Surface3d(LayoutDOM): __implementation__ = TypeScript(TS_CODE) data_source = Instance(ColumnDataSource) x = String y = String z = String
class PolygonDeckGL(LayoutDOM): # The special class attribute ``__implementation__`` should # contain a string of JavaScript (or CoffeeScript) code # that implements the JavaScript side of the custom extension model. __implementation__ = TypeScript(TS_CODE) layer_spec = Dict(String, Any) deck_spec = Dict(String, Any) data_source = Instance(ColumnDataSource) color_mapper = Instance(LinearColorMapper) tooltip = Bool
class LatexLabel(Label): """A subclass of the Bokeh built-in `Label` that supports rendering LaTex using the KaTex typesetting library. Only the render method of LabelView is overloaded to perform the text -> latex (via katex) conversion. Note: ``render_mode="canvas`` isn't supported and certain DOM manipulation happens in the Label superclass implementation that requires explicitly setting `render_mode='css'`). """ __javascript__ = ["https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"] __css__ = ["https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"] __implementation__ = TypeScript(TS_CODE)
class LatexLabel(HTMLLabel): """A subclass of the Bokeh built-in `HTMLLabel` that supports rendering LaTex using the KaTex typesetting library. Only the render method of HTMLLabelView is overloaded to perform the text -> latex (via katex) conversion. """ __javascript__ = [ "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js" ] __css__ = [ "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css" ] __implementation__ = TypeScript(TS_CODE)
class VolumeFormatter(TickFormatter): __implementation__ = TypeScript(""" import {TickFormatter} from "models/formatters/tick_formatter" export class VolumeFormatter extends TickFormatter { doFormat(ticks: string[] | string[]) { const formatted = [`${parseInt(ticks[0]).toLocaleString('en')}`] for (let i = 1, len = ticks.length; i < len; i++) { formatted.push(`${parseInt(ticks[i]).toLocaleString('en')}`) } return formatted } } """)
class PriceFormatter(TickFormatter): __implementation__ = TypeScript(""" import {TickFormatter} from "models/formatters/tick_formatter" export class PriceFormatter extends TickFormatter { doFormat(ticks: string[] | string[]) { console.log(ticks) const formatted = [`$${parseFloat(ticks[0]).toFixed(2)}`] for (let i = 1, len = ticks.length; i < len; i++) { formatted.push(`$${parseFloat(ticks[i]).toFixed(2)}`) } return formatted } } """)
class Surface3d(LayoutDOM): # The special class attribute ``__implementation__`` should contain a string # of JavaScript code that implements the browser side of the extension model. __implementation__ = TypeScript(TS_CODE) # Below are all the "properties" for this model. Bokeh properties are # class attributes that define the fields (and their types) that can be # communicated automatically between Python and the browser. Properties # also support type validation. More information about properties in # can be found here: # # https://docs.bokeh.org/en/latest/docs/reference/core/properties.html#bokeh-core-properties # This is a Bokeh ColumnDataSource that can be updated in the Bokeh # server by Python code data_source = Instance(ColumnDataSource) # The vis.js library that we are wrapping expects data for x, y, and z. # The data will actually be stored in the ColumnDataSource, but these # properties let us specify the *name* of the column that should be # used for each field. x = String y = String z = String colorValue = String myWidth = String(default="100px") legendLabel = String(default="XXX") xxx = Int options3D = Dict(String, Any, default={ "width": 300, "height": 300, "legendLabel": "XXX" }) print("x", __implementation__)
class MyPlot(Plot): __implementation__ = TypeScript(""" import {Plot, PlotView} from "models/plots/plot" import * as p from "core/properties" import "./custom.less" export class MyPlotView extends PlotView { model: MyPlot render(): void { super.render() this.el.classList.add("bk-my-plot") const angle = `${this.model.gradient_angle}deg` let offset = 0 const colors = [] const step = this.model.gradient_step for (const color of this.model.gradient_colors) { colors.push(`${color} ${offset}px`) offset += step colors.push(`${color} ${offset}px`) } this.el.style.backgroundImage = `repeating-linear-gradient(${angle}, ${colors.join(', ')})` } } export namespace MyPlot { export type Attrs = p.AttrsOf<Props> export type Props = Plot.Props & { gradient_angle: p.Property<number> gradient_step: p.Property<number> gradient_colors: p.Property<string[]> } } export interface MyPlot extends MyPlot.Attrs { width: number | null height: number | null } export class MyPlot extends Plot { properties: MyPlot.Props __view_type__: MyPlotView static init_MyPlot(): void { this.prototype.default_view = MyPlotView this.define<MyPlot.Props>({ gradient_angle: [ p.Number, 0 ], gradient_step: [ p.Number, 20 ], gradient_colors: [ p.Array, ["white", "lightgray"] ], }) this.override({ background_fill_alpha: 0.0, border_fill_alpha: 0.0, }) } } """) gradient_angle = Float(default=0) gradient_step = Float(default=20) gradient_colors = List(Color, default=["white", "gray"]) background_fill_alpha = Override(default=0.0) border_fill_alpha = Override(default=0.0)
class InteractiveColorBar(ColorBar): __implementation__ = TypeScript(TS_CODE)
def test_PolygonDeckGL(self): assert ( PolygonDeckGL.__implementation__.code == TypeScript(TS_CODE).code)
class ColorBarSideTitle(ColorBar): __implementation__ = TypeScript(ts_code)
class MyFormatter(TickFormatter): __implementation__ = TypeScript(JS_CODE)
class TestFormatter2(TickFormatter): __implementation__ = TypeScript("^") # invalid syntax on purpose
class AutocompleteInputCustom(TextInput): __implementation__ = TypeScript(""" import {TextInput, TextInputView} from "models/widgets/text_input" import {empty, display, undisplay, div, Keys} from "core/dom" import * as p from "core/properties" import {clamp} from "core/util/math" export class AutocompleteInputViewCustom extends TextInputView { model: AutocompleteInputCustom protected _open: boolean = false protected _last_value: string = "" protected _hover_index: number = 0 protected menu: HTMLElement render(): void { super.render() this.input_el.classList.add("bk-autocomplete-input") this.input_el.addEventListener("keydown", (event) => this._keydown(event)) this.input_el.addEventListener("keyup", (event) => this._keyup(event)) this.menu = div({class: ["bk-menu", "bk-below"]}) this.menu.addEventListener("click", (event) => this._menu_click(event)) this.menu.addEventListener("mouseover", (event) => this._menu_hover(event)) this.el.appendChild(this.menu) undisplay(this.menu) } change_input(): void { if (this._open && this.menu.children.length > 0) { this.model.value = this.menu.children[this._hover_index].textContent! this.input_el.focus() this._hide_menu() } else { this.model.value = this.input_el.value this.input_el.focus() this._hide_menu() } } protected _update_completions(completions: string[]): void { empty(this.menu) for (const text of completions) { const item = div({}, text) this.menu.appendChild(item) } if (completions.length > 0) this.menu.children[0].classList.add('bk-active') } protected _show_menu(): void { if (!this._open) { this._open = true this._hover_index = 0 this._last_value = this.model.value display(this.menu) const listener = (event: MouseEvent) => { const {target} = event if (target instanceof HTMLElement && !this.el.contains(target)) { document.removeEventListener("click", listener) this._hide_menu() } } document.addEventListener("click", listener) } } protected _hide_menu(): void { if (this._open) { this._open = false undisplay(this.menu) } } protected _menu_click(event: MouseEvent): void { if (event.target != event.currentTarget && event.target instanceof Element) { this.model.value = event.target.textContent! this.input_el.focus() this._hide_menu() } } protected _menu_hover(event: MouseEvent): void { if (event.target != event.currentTarget && event.target instanceof Element) { let i = 0 for (i = 0; i<this.menu.children.length; i++) { if (this.menu.children[i].textContent! == event.target.textContent!) break } this._bump_hover(i) } } protected _bump_hover(new_index: number): void { const n_children = this.menu.children.length if (this._open && n_children > 0) { this.menu.children[this._hover_index].classList.remove('bk-active') this._hover_index = clamp(new_index, 0, n_children-1) this.menu.children[this._hover_index].classList.add('bk-active') } } _keydown(_event: KeyboardEvent): void {} _keyup(event: KeyboardEvent): void { switch (event.keyCode) { case Keys.Enter: { this.change_input() break } case Keys.Esc: { this._hide_menu() break } case Keys.Up: { this._bump_hover(this._hover_index-1) break } case Keys.Down: { this._bump_hover(this._hover_index+1) break } default: { const value = this.input_el.value if (value.length <= 1) { this._hide_menu() return } const completions: string[] = [] for (const text of this.model.completions) { if (text.indexOf(value) != -1) completions.push(text) } this._update_completions(completions) if (completions.length == 0) this._hide_menu() else this._show_menu() } } } } export namespace AutocompleteInputCustom { export type Attrs = p.AttrsOf<Props> export type Props = TextInput.Props & { completions: p.Property<string[]> } } export interface AutocompleteInputCustom extends AutocompleteInputCustom.Attrs {} export class AutocompleteInputCustom extends TextInput { properties: AutocompleteInputCustom.Props constructor(attrs?: Partial<AutocompleteInputCustom.Attrs>) { super(attrs) } static initClass(): void { this.prototype.type = "AutocompleteInputCustom" this.prototype.default_view = AutocompleteInputViewCustom this.define<AutocompleteInputCustom.Props>({ completions: [ p.Array, [] ], }) } } AutocompleteInputCustom.initClass() """) completions = List(String, help="")
class LatexLabel(Label): """A subclass of `Label` with additional class attributes 'width' and 'height', canvas mode isn't supported and DOM manipulation happens in the TypeScript superclass implementation that requires setting `render_mode='css'`). Only the render method of LabelView is overwritten to perform the text -> latex (via MathJax) conversion """ __javascript__ = ["https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"] __implementation__ = TypeScript(""" import {Label, LabelView} from "models/annotations/label" import * as p from "core/properties" import {ImageLoader} from "core/util/image" declare namespace MathJax { function tex2svg(input: string): HTMLElement } export class LatexLabelView extends LabelView { model: LatexLabel image: HTMLImageElement | undefined width: number height: number initialize(): void { super.initialize() const mathjax_element = MathJax.tex2svg(this.model.text) mathjax_element.setAttribute('style', 'visibility: hidden;') document.body.appendChild(mathjax_element) const svg_element = mathjax_element.children[0] as SVGElement const outer_HTML = svg_element.outerHTML const blob = new Blob([outer_HTML],{type:'image/svg+xml;charset=utf-8'}) const url = URL.createObjectURL(blob) this.height = parseFloat(getComputedStyle(svg_element, null).getPropertyValue('height')) this.width = parseFloat(getComputedStyle(svg_element, null).getPropertyValue('width')) document.getElementsByClassName('MathJax')[0].remove() new ImageLoader(url, { loaded: (image) => { this.image = image this.request_render() URL.revokeObjectURL(url) }, }) } protected _render(): void { if(this.image) { const panel = this.layout != null ? this.layout : this.plot_view.frame const xscale = this.coordinates.x_scale const yscale = this.coordinates.y_scale let sx = this.model.x_units == "data" ? xscale.compute(this.model.x) : panel.bbox.xview.compute(this.model.x) let sy = this.model.y_units == "data" ? yscale.compute(this.model.y) : panel.bbox.yview.compute(this.model.y) sx += this.model.x_offset sy -= this.model.y_offset this.layer.ctx.drawImage(this.image, sx, sy, this.width, this.height) this.notify_finished() } } } export namespace LatexLabel { export type Attrs = p.AttrsOf<Props> export type Props = Label.Props } export interface LatexLabel extends LatexLabel.Attrs {} export class LatexLabel extends Label { properties: LatexLabel.Props __view_type__: LatexLabelView static init_LatexLabel(): void { this.prototype.default_view = LatexLabelView } } """)
class DrawTool(Tool): __implementation__ = TypeScript(JS_CODE) source = Instance(ColumnDataSource)
class KeyboardResponder(LayoutDOM): __implementation__ = TypeScript(KEYBOARDRESPONDERCODE_TS) keycode = Int(default=0)
class Upload(serverutils.HTTPModel, bokeh.models.Widget): upload_id = bokeh.core.properties.String() upload_url = bokeh.core.properties.String() value = bokeh.core.properties.String(default="", help=""" The file content hash """) mime_type = bokeh.core.properties.String(default="", help=""" The mime type of the selected file. """) filename = bokeh.core.properties.String(default="", help=""" The filename of the selected file. The file path is not included as browsers do not allow access to it. """) accept = bokeh.core.properties.String(default="", help=""" Comma-separated list of standard HTML file input filters that restrict what files the user can pick from. Values can be: `<file extension>`: Specific file extension(s) (e.g: .gif, .jpg, .png, .doc) are pickable `audio/*`: all sound files are pickable `video/*`: all video files are pickable `image/*`: all image files are pickable `<media type>`: A valid `IANA Media Type`_, with no parameters. .. _IANA Media Type: https://www.iana.org/assignments/media-types/media-types.xhtml """) multiple = bokeh.core.properties.Bool(default=False, help=""" set multiple=False (default) for single file selection, set multiple=True if selection of more than one file at a time should be possible. """) __implementation__ = TypeScript(TS_CODE) __javascript__ = ["https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"] def __init__(self, **kw): bokeh.models.Widget.__init__(self, **kw) self._file = None def http_init(self): uploadify(self.bokeh_tornado) self.upload_url = self.base_url + "/bokeh-garden/upload/" self.upload_id = str(uuid.uuid4()) uploads[self.upload_id] = self def upload(self, request_handler, file, filename, mime_type): if self._file is not None: self._file.close() self._file = file self._filename = filename self._mime_type = mime_type self.document.add_next_tick_callback(self.handle_upload) request_handler.write(b"Upload succeeded") @tornado.gen.coroutine def handle_upload(self): self.filename = self._filename self.mime_type = self._mime_type self.value = self._filename print("Post handled") @property def file(self): self._file.seek(0) return self._file @property def value_bytes(self): # Only do this for small file return self.file.read() # Override on_change so that a handler can be set for "value" even # though that's not a real property... def on_change(self, attr, *callbacks): bokeh.util.callback_manager.PropertyCallbackManager.on_change(self, attr, *callbacks) def __del__(self): if self._file is not None: self._file.close()
class TestFormatter(TickFormatter): __implementation__ = TypeScript(TS_CODE)
class IonRangeSlider(AbstractSlider): # The special class attribute ``__implementation__`` should contain a string # of JavaScript (or CoffeeScript) code that implements the JavaScript side # of the custom extension model or a string name of a JavaScript (or # CoffeeScript) file with the implementation. with open( os.path.join(os.path.dirname(__file__), '../bokeh-ion-rangesliderjs/src/ion_range_slider.ts') ) as file_: implementation = file_.readlines() __implementation__ = TypeScript(''.join(implementation)) __javascript__ = [ "https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js", "https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/js/ion.rangeSlider.js" ] __css__ = [ "https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.css", "https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/css/ion.rangeSlider.css", "https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/css/ion.rangeSlider.skinFlat.min.css", "https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/img/sprite-skin-flat.png" ] # Below are all the "properties" for this model. Bokeh properties are # class attributes that define the fields (and their types) that can be # communicated automatically between Python and the browser. Properties # also support type validation. More information about properties in # can be found here: # # http://bokeh.pydata.org/en/latest/docs/reference/core.html#bokeh-core-properties start = Float(default=0, help=""" The minimum allowable value. """) end = Float(default=1, help=""" The maximum allowable value. """) value = Tuple(Float, Float, default=[0, 1], help=""" Initial or selected range. """) step = Float(default=0.1, help=""" The step between consecutive values. """) #format = String(default="0[.]00") Ignored right now #bar_color = Color(default="#e6e6e6", help=""" """) Overwritten default slider_type = Enum(enumeration('single', 'double'), default='single', help=""" Choose slider type, could be single - for one handle, or double for two handles. """) values = List(Any, help=""" Set up your own array of possible slider values. They could be numbers or strings. The following attributes are ignored if you supply values: min, max and step. """) grid = Bool(default=True, help=""" Show the grid beneath the slider. """) prettify_enabled = Bool(default=True, help=""" Improve readability of long numbers. 10000000 -> 10 000 000 """) prettify = Instance(Callback, help=""" Set up your own prettify function. Can be anything. For example, you can set up unix time as slider values and than transform them to cool looking dates. """) force_edges = Bool(default=False, help=""" Slider will be always inside it's container. """) prefix = String(default="", help=""" Set prefix for values. Will be set up right before the number: $100 """)
class CustomInspectTool(Tool): __implementation__ = TypeScript(TS_CODE) _active = Nullable(Bool) icon = Image() tool_name = String()
class TimeStamp(LayoutDOM): __implementation__ = TypeScript(JS_CODE) text = String(default="Testing")