具有自定义图标的Bokeh自定义操作

时间:2019-06-06 21:52:34

标签: javascript python css font-awesome bokeh

我正在尝试为工具栏内的散景图构建一些自定义动作,对于这些自定义动作,我还需要自定义图标。

我从这个example开始,它创建了一个绘图工具。一切正常,此时一切都很好。

对于自定义图标,我从example开始,它在fontawesome 4.6.3上方创建了bokeh包装。像魅力一样工作。

现在,我想将两者结合起来,因此首先确保已导入fontawesome_icon.py包装器。然后必须将用于绘图工具的图标设置为已包装的字体图标,因此DrawTool的图标应变为 icon = "bk-u-fa bk-u-fa-pencil",因为这是指定字体图标的方式。

但是此设置会在客户端的浏览器中生成以下错误:

Error rendering Bokeh items: DOMException: Failed to execute 'add' on 'DOMTokenList': The token provided ('bk-u-fa bk-u-fa-pencil') contains HTML space characters, which are not valid in tokens.
    at e.t.render (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:460843)
    at e.render (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:517274)
    at e.t.initialize (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:460541)
    at e.t [as constructor] (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:93458)
    at e.t [as constructor] (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:16968)
    at e.t [as constructor] (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:460274)
    at new e (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:517173)
    at Object.i.build_views (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:13738)
    at t._build_tool_button_views (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:524696)
    at t.initialize (http://localhost:5006/static/js/bokeh.min.js?v=afab3eba5b3a72c05610143940e03c8e:31:523943)

因此,显然不喜欢我在与图标相同的字符串中提供了两个类。 我尝试了几种方法来指定两个类:["bk-u-fa", "bk-u-fa-power-off"]"bk-u-fa"; "bk-u-fa-power-off""bk-u-fa" "bk-u-fa-power-off""bk-u-fa", "bk-u-fa-power-off",但是它们都不起作用。

所以问题是,是否还有另一种方法可以实现带有自定义图标的自定义操作? 或者,如何为这样的自定义操作图标指定多个CSS类?还是可以将第一类指定为图标字符串,而另一部分指定为某些自定义CSS类?

在下面,您可以找到我的主应用程序mygestures.py,您只需要将此文件复制到字体example中,并以bokeh serve --show mygestures.py开头。它具有一个按钮,该按钮在底部附加一些文本和一个图标,以确保图标可以正常工作。

from bokeh.core.properties import Instance
from bokeh.models import ColumnDataSource, Tool
from bokeh.util.compiler import TypeScript
from bokeh.layouts import column, row
from bokeh.plotting import figure, curdoc
from bokeh.models import Button
from bokeh.models import CustomJS

from fontawesome_icon import FontAwesomeIcon


TS_CODE = """
import {GestureTool, GestureToolView} from "models/tools/gestures/gesture_tool"
import {ColumnDataSource} from "models/sources/column_data_source"
import {GestureEvent} from "core/ui_events"
import * as p from "core/properties"

export class DrawToolView extends GestureToolView {
  model: DrawTool

  //this is executed when the pan/drag event starts
  _pan_start(_ev: GestureEvent): void {
    this.model.source.data = {x: [], y: []}
  }

  //this is executed on subsequent mouse/touch moves
  _pan(ev: GestureEvent): void {
    const {frame} = this.plot_view

    const {sx, sy} = ev
    if (!frame.bbox.contains(sx, sy))
      return

    const x = frame.xscales.default.invert(sx)
    const y = frame.yscales.default.invert(sy)

    const {source} = this.model
    source.get_array("x").push(x)
    source.get_array("y").push(y)
    source.change.emit()
  }

  // this is executed then the pan/drag ends
  _pan_end(_ev: GestureEvent): void {}
}

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

  export type Props = GestureTool.Props & {
    source: p.Property<ColumnDataSource>
  }
}

export interface DrawTool extends DrawTool.Attrs {}

export class DrawTool extends GestureTool {
  properties: DrawTool.Props

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

  tool_name = "Drag Span"
  icon = "bk-u-fa bk-u-fa-pencil"
  event_type = "pan" as "pan"
  default_order = 12

  static initClass(): void {
    this.prototype.type = "DrawTool"
    this.prototype.default_view = DrawToolView

    this.define<DrawTool.Props>({
      source: [ p.Instance ],
    })
  }
}
DrawTool.initClass()
"""


class DrawTool(Tool):
    __implementation__ = TypeScript(TS_CODE)
    source = Instance(ColumnDataSource)


source = ColumnDataSource(data=dict(x=[], y=[]))

plot = figure(x_range=(0, 10), y_range=(0, 10), tools=[DrawTool(source=source)])
plot.title.text = "Drag to draw on the plot"
plot.line('x', 'y', source=source)

# show(plot)

button = Button(label="Press Me", callback=CustomJS(
    code="""
    console.log("button callback"); 

    btn = document.createElement("div"); 
    btn.innerHTML = "<h1><i class=\\"bk-u-fa bk-u-fa-pencil\\"></i> Icon and some text</h1>";
    document.getElementsByClassName("bk-root")[0].appendChild(btn);

    """
))

plot = figure(x_range=(0, 10), y_range=(0, 10), tools=[DrawTool(source=source)])
plot.title.text = "Drag to draw on the plot"
plot.line('x', 'y', source=source)

curdoc().add_root(column(row(button), row(plot)))

感谢您的帮助, Gabor

1 个答案:

答案 0 :(得分:0)

  

或者如何为这样的自定义操作图标指定多个CSS类?

目前不可能。内置的ButtonToolButtonView code执行此操作:

render(): void {
  empty(this.el)
  const icon = this.model.computed_icon
  if (isString(icon)) {
    if (startsWith(icon, "data:image"))
      this.el.style.backgroundImage = "url('" + icon + "')"
    else
      this.el.classList.add(icon)
  }
  this.el.title = this.model.tooltip
}

第二个代码路径实际上仅是在考虑了内置工具栏图标的情况下开发的,并且仅添加了一个CSS类。我们添加了第一个代码路径来处理编码为数据URL的图像,从而为自定义图标提供了一些可能性。所以这就是我的建议:将要显示的图标编码为base64数据URL,并使用该字符串设置icon

或者,您正在查看:

  • 制作一个自定义扩展,将ButtonTool子类化,以接受多个CSS类
  • 在GH上发出功能请求,以将其添加为功能

第一个是更多的工作,第二个将花费更多的时间。