我有一个与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模型?