【问题标题】:Bokeh custom action with custom icon带有自定义图标的散景自定义操作
【发布时间】:2019-10-22 10:37:42
【问题描述】:

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

我从创建绘图工具的example 开始。它起作用了,此时一切都很好。

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

现在我想将这两者结合起来,所以首先我确保导入了 fontawesome_icon.py 包装器。那么绘图工具要使用的图标必须设置为包装好的字体图标,这样绘图工具的图标就变成了 icon = "bk-u-fa bk-u-fa-pencil",因为这是如何指定 fontawesome 图标的。

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

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,您只需将此文件复制到 fontawesome 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)))

感谢您的帮助, 嘉柏

【问题讨论】:

    标签: javascript python css font-awesome bokeh


    【解决方案1】:

    或者如何为这样的自定义操作图标指定多个 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 上提出功能请求,将其添加为功能

    第一个需要更多的工作,第二个需要更多的时间。

    【讨论】:

    • 感谢您的回答。我想如果我使用一个只为其图标定义一个类的图标包,它会起作用,对吧?如果我以目录格式部署服务器并且静态导入 css 是否可行:bokeh.pydata.org/en/latest/docs/user_guide/…。因此,我可以直接使用图标的名称,而不是像在 fontawesome 的情况下那样在图标包上制作散景包装器,因为相应的 css 已加载。
    • 这听起来确实有道理,但我从未亲自尝试过。
    • 谢谢,它确实有效,我会发布代码作为答案,所以其他人可能会有它:) 另一个问题是你是否知道是否有可能触发一个动作来自自定义工具的散景 python 后端。我知道预定义的工具正在触发一些事件(如平移、缩放等),这些事件可以在后台进行回调,而且我也知道这些是硬编码的,并且无法从自定义工具生成此类事件,但是有没有办法从自定义工具的 js 回调中触发后端的某些操作?
    • 只是为了信息组织和可访问性,请在此处或在项目 Discourse discourse.bokeh.org/c/community-support 中作为单独的问题提出单独的问题
    猜你喜欢
    • 1970-01-01
    • 2011-12-31
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 2023-04-02
    • 2012-05-03
    • 1970-01-01
    • 2012-05-08
    相关资源
    最近更新 更多