Bokeh Custom JS如何注册自定义工具(从< 0.12.1迁移到0.12.4)

时间:2017-01-16 11:19:01

标签: javascript python bokeh

我有一个与bokeh 0.12.1一起使用的应用程序。它包含一些添加到点按工具的自定义Javascript和回调。

迁移到散景0.12.4我收到了  错误消息

 Bokeh Error
Model `SimpleTapTool' does not exist. This could be due to a widget or a custom model not being registered before first usage.

SimpleTapTool的代码:     来自bokeh.models导入工具,实例,回调,CustomJS,Span

class SimpleTapTool(Tool):
__implementation__ = """
Bokeh.require.modules["custom/main"] = [function (require, module, exports) { 
module.exports = {SimpleTapTool: require("custom/simple_tap_tool")};
}, {}];

Bokeh.require.modules["custom/simple_tap_tool"] = [
function (require, module, exports) {
    var SelectTool, SimpleTapTool, SimpleTapToolView, _, p,
        extend = function (child, parent) {
            for (var key in parent) {
                if (hasProp.call(parent, key)) child[key] = parent[key];
            }
            function ctor() {
                this.constructor = child;
            }

            ctor.prototype = parent.prototype;
            child.prototype = new ctor();
            child.__super__ = parent.prototype;
            return child;
        },
        hasProp = {}.hasOwnProperty;

    _ = require("underscore");

    p = require("core/properties");

    SelectTool = require("models/tools/gestures/select_tool");

    SimpleTapToolView = (function (superClass) {
        extend(SimpleTapToolView, superClass);

        function SimpleTapToolView() {
            return SimpleTapToolView.__super__.constructor.apply(this, arguments);
        }

        SimpleTapToolView.prototype._tap = function (e) {
            var callback, canvas, cb_data, ds, geometry, i, len, r, ref, tool_events, vx, vy, xm, ym;
            canvas = this.plot_view.canvas;
            xm = this.plot_view.frame.get('x_mappers')['default'];
            ym = this.plot_view.frame.get('y_mappers')['default'];
            vx = canvas.sx_to_vx(e.bokeh.sx);
            vy = canvas.sy_to_vy(e.bokeh.sy);
            geometry = {
                type: 'point',
                vx: vx,
                vy: vy,
                x: xm.map_from_target(vx),
                y: ym.map_from_target(vy)
            };
            tool_events = this.plot_model.plot.tool_events;
            tool_events.set("geometries", [geometry]);
            callback = this.mget("callback");
            cb_data = {
                geometries: this.plot_model.plot.tool_events.get('geometries')
            };
            ref = this.mget('computed_renderers');
            var time_at_cursor = xm.map_from_target(vx);
            // Note that the following code only works when a name was passed to the renderer
            // i.e. python line.(x,y,name="example")
            // another trick is done by using the model.document.. since all plots in one case view belong to the
            // same document.
            // there is an implicit linking between the legend where the #ids are equal to the renderer names...
            var all_renderers = this.model.document._all_models_by_name._dict;

            _.forEach(all_renderers, function (r) {
                ds = r.data_source;
                var time_index = _.findIndex(ds.data.x, function (t) {
                    return t >= time_at_cursor;
                });

                if (r.name.search('S__bit_name') !== -1 && r.name.search('-zero_line') === -1) {
                    var value_at_cursor = ds.data.y[time_index];
                    var zero_line_renderer = _.find(all_renderers, function (renderer) {
                        return renderer.name === (r.name + '-zero_line');
                    });
                    var value_at_cursor_zero_line = zero_line_renderer.data_source.data.y[0];
                    $("#" + r.name).text(value_at_cursor - value_at_cursor_zero_line);
                }
                else {

                    var y_avg = ds.data.y[time_index-1] + (ds.data.y[time_index] - ds.data.y[time_index-1])/(ds.data.x[time_index] - ds.data.x[time_index-1])*(time_at_cursor-ds.data.x[time_index-1]);
                    $("#" + r.name).text(y_avg.toPrecision(6));
                }
            });
            $('.subgroup-legend').show(1000);
            $('.value-header-time-placeholder').text('@ ' + time_at_cursor.toPrecision(6) + ' s');


            for (i = 0, len = ref.length; i < len; i++) {
                r = ref[i];
                ds = r.get('data_source');

                if (callback != null) {
                    if (_.isFunction(callback)) {
                        callback(ds, cb_data);
                    } else {
                        callback.execute(ds, cb_data);
                    }
                }
            }
            return null;
        };

        return SimpleTapToolView;

    })(SelectTool.View);

    SimpleTapTool = (function (superClass) {
        extend(SimpleTapTool, superClass);

        function SimpleTapTool() {
            return SimpleTapTool.__super__.constructor.apply(this, arguments);
        }

        SimpleTapTool.prototype.default_view = SimpleTapToolView;
        SimpleTapTool.prototype.type = "SimpleTapTool";
        SimpleTapTool.prototype.tool_name = "Vertical Line";
        SimpleTapTool.prototype.icon = "bk-tool-icon-tap-select";
        SimpleTapTool.prototype.event_type = "tap";
        SimpleTapTool.prototype.default_order = 100;
        SimpleTapTool.define({
            callback: [p.Any]
        });
        return SimpleTapTool;

    })(SelectTool.Model);

    module.exports = {
        Model: SimpleTapTool,
        View: SimpleTapToolView
    };
}, {}];

Bokeh.Models.register_locations(Bokeh.require("custom/main"));
"""

callback = Instance(Callback, help="""
        A client-side action specification, like opening a URL, showing
        a dialog box, etc. See :class:`~bokeh.models.actions.Action` for        details.
        """)


def add_custom_tapping_tool_to_plots(plots, initial_span_location=None):
    spans = dict()

for key, plot in enumerate(plots):
    span = create_span(initial_location=initial_span_location)
    if 'events' in plot:
        spans['eventsspan'] = span
    else:
        spans['span_1_%s' % plot] = span
    plots[plot].add_layout(span)

simple_tap_tool_callback_js = CustomJS(args=spans, code="""
    var arguments_array = _.toArray(arguments);
    var spans_array = _.slice(arguments_array, 0, arguments_array.length - 2);
    var data_source = arguments_array[arguments_array.length - 3]
    var x = cb_data['geometries'][0].x;

    window.bokehCaseSpanLocation = x;
    spans_array.forEach(function(span) {
        span.location = x;
        span.visible = true;

    })
""")

for sp in spans:
    print sp

for plot in plots:
    simple_tap_tool = SimpleTapTool()
    simple_tap_tool.callback = simple_tap_tool_callback_js
    plots[plot].add_tools(simple_tap_tool)
return plots


def create_span(initial_location=None):
    if initial_location is None:
        initial_location = -1000

return Span(
    location=initial_location, visible=False,
    dimension='height',
    line_color='#389AEA',
    line_dash='dashed',
    line_width=3
)

这是测试它的python函数。

    plot = figure()
    plot.line(range(10), range(10))
    plots ={'plot': plot}
    plots = add_custom_tapping_tool_to_plots(plots)
    show(plots['plot'])

我在0.12.3的发行说明中看到,提到散景编译器改变了......但我无法在我的情况下找到问题。

如何正确注册SimpleTapTool模型?

0 个答案:

没有答案