admin管理员组

文章数量:1429684

I'm Trying to create a custom Save tool in bokeh, and here's what I have:

class NewSaveTool(Tool):
    JS_CODE = """
        import * as p from "core/properties"
        import {ActionTool, ActionToolView} from "models/tools/actions/action_tool"
        export class NewSaveToolView extends ActionToolView
            do: () -> 
                save_name = @model.source
                @plot_view.save(save_name)

        export class NewSaveTool extends ActionTool
            default_view: NewSaveToolView
            type: "SaveTool"
            tool_name: "Save"
            icon: "bk-tool-icon-save"
            @define {
                source: [ p.String ]
            }
    """
    source = String
    __implementation__ = JS_CODE

The tool loads and is in the toolbar, but when I click the button, I get

Uncaught TypeError: this.plot_view.save is not a function

This is the exact function that the save tool in the source uses, so does anyone know why it didn't work in this instance?

I'm Trying to create a custom Save tool in bokeh, and here's what I have:

class NewSaveTool(Tool):
    JS_CODE = """
        import * as p from "core/properties"
        import {ActionTool, ActionToolView} from "models/tools/actions/action_tool"
        export class NewSaveToolView extends ActionToolView
            do: () -> 
                save_name = @model.source
                @plot_view.save(save_name)

        export class NewSaveTool extends ActionTool
            default_view: NewSaveToolView
            type: "SaveTool"
            tool_name: "Save"
            icon: "bk-tool-icon-save"
            @define {
                source: [ p.String ]
            }
    """
    source = String
    __implementation__ = JS_CODE

The tool loads and is in the toolbar, but when I click the button, I get

Uncaught TypeError: this.plot_view.save is not a function

This is the exact function that the save tool in the source uses, so does anyone know why it didn't work in this instance?

Share Improve this question asked Apr 12, 2017 at 0:43 G WG W 311 silver badge3 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

Answering this late on since it took me far too much time to make it work.
The main change here is 'do' to 'doit', though I'm honestly not sure where the specified error is from, one of the few I never got.

A working implementation (on bokeh 12.13 at least) is:

JS_CODE_SAVE = """
import * as p from "core/properties"
import {ActionTool, ActionToolView} from "models/tools/actions/action_tool"

export class NewSaveView extends ActionToolView

  # this is executed when the button is clicked
  doit: () ->
    @plot_view.save(@model.save_name)

export class NewSave extends ActionTool
  default_view: NewSaveView
  type: "NewSave"

  tool_name: "Save"
  icon: "bk-tool-icon-save"

  @define { save_name: [ p.String ] }
"""

class NewSave(Tool):
    """
    Save a plot with a custom name.
    Usage: NewSaveTool(save_name=name)
    """
    __implementation__ = JS_CODE_SAVE
    save_name = String()

Implemented as: tools = [CustomSaveTool(savename='custom name')]

To actually dynamically change the save name, you have to change the savename attribute, so eg in a widget callback function: plot.tools[0].save_name = 'new save name'

The implementation given in the initial response does not work with more recent versions of bokeh.

Here is an updated version based on the code for the original SaveTool. It exposes a save_name property which can be set in Python:

from bokeh.models import ActionTool
from bokeh.util.piler import TypeScript
from bokeh.core.properties import String

CUSTOM_SAVE_TS = """
import {ActionTool, ActionToolView} from "models/tools/actions/action_tool"
import * as p from "core/properties"
import {tool_icon_save} from "styles/icons.css"

export class CustomSaveToolView extends ActionToolView {
  model: CustomSaveTool

  async save(name: string): Promise<void> {
    const blob = await this.plot_view.to_blob()
    const link = document.createElement("a")
    link.href = URL.createObjectURL(blob)
    link.download = name // + ".png" | "svg" (inferred from MIME type)
    link.target = "_blank"
    link.dispatchEvent(new MouseEvent("click"))
  }

  doit(): void {
    this.save(this.model.save_name)
  }
}

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

  export type Props = ActionTool.Props & {
    save_name: p.Property<string>
  } 
}

export interface CustomSaveTool extends CustomSaveTool.Attrs {}

export class CustomSaveTool extends ActionTool {
  properties: CustomSaveTool.Props 
  __view_type__: CustomSaveToolView

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

  static init_CustomSaveTool(): void {
    this.prototype.default_view = CustomSaveToolView

    this.register_alias("save", () => new CustomSaveTool())

    this.define<CustomSaveTool.Props>(({String}) => ({
      save_name: [ String ],
    }))
  }

  tool_name = "Custom Save"
  icon = tool_icon_save

}
"""

class CustomSaveTool(ActionTool):
    """Modified save tool allowing custom file names"""
    __implementation__ = TypeScript(CUSTOM_SAVE_TS)
    save_name = String()

save_tool = CustomSaveTool(save_name='custom_filename')

Note that pared to the original SaveTool code I had to scrap the parts related to the 'copy' function which I do not think is really used anyway.

Great! It works! Just one remark about the usage example above which should be:

tools = [NewSave(savename='custom name')]

Here is a plete working example (using CustomSaveTool as class name):

from bokeh.models import Tool, String
from bokeh.plotting import figure
from bokeh.io import show

JS_CODE_SAVE = """
import * as p from "core/properties"
import {ActionTool, ActionToolView} from "models/tools/actions/action_tool"

export class NewSaveView extends ActionToolView

  # this is executed when the button is clicked
  doit: () ->
    @plot_view.save(@model.save_name)

export class CustomSaveTool extends ActionTool
  default_view: NewSaveView
  type: "CustomSaveTool"

  tool_name: "Save"
  icon: "bk-tool-icon-save"

  @define { save_name: [ p.String ] } """

class CustomSaveTool(Tool):
    """
    Save a plot with a custom name.
    Usage: CustomSaveTool(save_name = name)
    """
    __implementation__ = JS_CODE_SAVE
    save_name = String()

tools = [CustomSaveTool(save_name = 'custom name 1')]
plot = figure(x_range = (0, 10), y_range = (0, 10), tools = tools)
plot.line(x = [1, 2, 3], y = [4, 5, 6])
plot.tools[0].save_name = 'custom name 2'
show(plot)

本文标签: javascriptBokeh Custom Save ToolStack Overflow