我正在开发一个带有散景(0.12.2)的交互式应用程序,可根据特定的交互更新绘图。
现在我使用滑块来改变绘图中字形的位置,但实际上我想在特定的绘图中访问鼠标的位置。
数据集是一个多维矩阵(张量),密集数据,每个图表在特定位置显示一个维度。如果我在一个图上更改标记符号的位置,则需要更新其他图,这意味着我必须根据更新的位置切割我的数据集。
这是一个简单的例子,我尝试使用悬停工具在我的散景服务器更新功能中获取鼠标数据:
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models import CustomJS, HoverTool
from bokeh.io import curdoc
s = ColumnDataSource(data=dict(x=[0, 1], y=[0, 1]))
callback = CustomJS(args=dict(s=s), code="""
var geometry = cb_data['geometry'];
var mouse_x = geometry.x;
var mouse_y = geometry.y;
var x = s.get('data')['x'];
var y = s.get('data')['y'];
x[0] = mouse_x;
y[0] = mouse_y;
s.trigger('change');
""")
hover_tool = HoverTool(callback=callback)
p = figure(x_range=(0, 1), y_range=(0, 1), tools=[hover_tool])
p.circle(x='x', y='y', source=s)
def update():
print s.data
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 1000)
不幸的是,服务器只输出:
{'y':[0,1],'x':[0,1}}
{'y':[0,1],'x':[0,1}}
{'y':[0,1],'x':[0,1}}
{'y':[0,1],'x':[0,1}}
有没有办法访问鼠标位置(在python代码中)?即使访问一个字形的位置也足够了(因为我可以用一些javascript代码改变字形的位置)。
编辑:所以我最近发现有这个 tool_events.on_change()可以用于此目的。不幸的是,它只适用于 TapTool , LassoSelectTool 和 BoxSelectTool ,而不适用于 HoverTool :
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.models.tools import BoxSelectTool, TapTool, HoverTool, LassoSelectTool
from bokeh.models.ranges import Range1d
TOOLS = [TapTool(), LassoSelectTool(), BoxSelectTool(), HoverTool()]
p = figure(tools=TOOLS,
x_range=Range1d(start=0.0, end=10.0),
y_range=Range1d(start=0.0, end=10.0))
def tool_events_callback(attr, old, new):
print attr, 'callback', new
p.tool_events.on_change('geometries', tool_events_callback)
curdoc().add_root(p)
基于我在此处找到的答案:How can I get data from a ColumnDataSource object which is synchronized with local variables of Bokeh's CustomJS function?。此解决方案的问题是我无法使用pan并触发 tool_events 回调。我只能点击( TapTool )或平移并触发一次回调( Lasso / BoxSelectTool )。我实际上希望在每次鼠标移动时触发这样的回调..
答案 0 :(得分:5)
所以我最近发现你可以为此目的使用自定义模型。这意味着,扩展现有工具,例如GestureTool但实现/覆盖您自己的功能。您需要运行散景服务器(显然)。
$ bokeh serve dir_with_mainfile/
我现在使用的是以下内容: 创建一个文件MouseMoveTool.py:
from bokeh.models import Tool
class MouseMoveTool(Tool):
# assuming your models are saved in subdirectory models/
with open('models/MouseMoveTool.coffee', 'r') as f:
controls = f.read()
__implementation__ = controls
然后创建MouseMoveTool.coffee:
p = require "core/properties"
GestureTool = require "models/tools/gestures/gesture_tool"
class MouseMoveToolView extends GestureTool.View
### Override the _pan function ###
_pan: (e) ->
frame = @plot_model.frame
canvas = @plot_view.canvas
vx = canvas.sx_to_vx(e.bokeh.sx)
vy = canvas.sy_to_vy(e.bokeh.sy)
if not frame.contains(vx, vy)
return null
# x and y are your mouse coordinates relative to the axes values
x = frame.x_mappers.default.map_from_target(vx)
y = frame.y_mappers.default.map_from_target(vy)
# update the model's geometry attribute. this will trigger
# the tool_events.on_change('geometries', ..) callback
# in your python code.
@plot_model.plot.tool_events.geometries = [{x:x, y:y}]
class MouseMoveTool extends GestureTool.Model
default_view: MouseMoveToolView
type: "MouseMoveTool"
tool_name: "Mouse Move Tool"
icon: "bk-tool-icon-pan"
event_type: "pan"
default_order: 13
module.exports =
Model: MouseMoveTool
View: MouseMoveToolView
之后,您可以在main.py程序中使用您的工具:
from models.MouseMoveTool import MouseMoveTool
p = figure(plot_width=300, plot_height=300, tools=[MouseMoveTool()])
p.tool_events.on_change('geometries', on_mouse_move)
def on_mouse_move(attr, old, new):
print new[0] # will print {x:.., y:..} coordinates