tl;博士-Here's a link to the gist showing update-on-drag.
要做到这一点,您需要知道:
- 如何通过 Jupyter 的 Javascript 与 IPython 内核进行交互
前端。现在是通过
Jupyter.Kernel.execute (current
source code)。
-
d3.js 足够舒适。 (就像用屏幕来绘制坐标转换一样。)
- 您选择的 d3-via-Python 库。 mpld3 这个例子。
mpld3 有它的own plugin for draggable points 和custom mpld3 plugin 的能力。但是现在有no feature to redraw the plot on update of data;维护人员说,现在最好的方法是在更新时删除并重绘整个情节,或者真正深入 javascript。
正如您所说(据我所知),Ipywidgets 是一种在使用 IPython 内核时将 HTML input 元素链接到 Jupyter 笔记本图的方法,因此不是您想要的。但比我建议的要容易一千倍。 ipywidgets github repo 的README 链接到他们示例套件中的correct IPython notebook to start with。
关于 Jupyter notebook 与 IPython 内核直接交互的最佳博文来自 2013 年的 Jake Vanderplas。它是针对 IPythonIPython 2 和IPython 3 但代码不适用于我的 Jupyter 4 笔记本。
问题似乎在于javascript API for the Jupyter kernel 不断变化。
我更新了mpld3 dragging example 和 Jake Vanderplas 在要点中的示例(链接在此回复的顶部)以提供尽可能短的示例,因为这已经很长了,但是下面的 sn-ps 尝试进行交流这个想法更简洁。
Python
Python 回调可以有任意多的参数,甚至可以是原始代码。内核将通过eval 语句运行它并发回最后的返回值。输出,无论是什么类型,都将作为字符串 (text/plain) 传递给 javascript 回调。
def python_callback(arg):
"""The entire expression is evaluated like eval(string)."""
return arg + 42
Javascript
Javascript 回调应该有一个参数,它是一个 Javascript
Object 遵循结构 documented here。
javascriptCallback = function(out) {
// Error checking omitted for brevity.
output = out.content.user_expressions.out1;
res = output.data["text/plain"];
newValue = JSON.parse(res); // If necessary
//
// Use newValue to do something now.
//
}
使用函数Jupyter.notebook.kernel.execute 从 Jupyter 调用 IPython 内核。发送到的内容
内核是documented here。
var kernel = Jupyter.notebook.kernel;
var callbacks = {shell: {reply: javascriptCallback }};
kernel.execute(
"print('only the success/fail status of this code is reported')",
callbacks,
{user_expressions:
{out1: "python_callback(" + 10 + ")"} // function call as a string
}
);
mpld3 插件中的 JavaScript
修改 mpld3 库的插件,为
要更新的 HTML 元素,以便我们可以在
未来。
import matplotlib as mpl
import mpld3
class DragPlugin(mpld3.plugins.PluginBase):
JAVASCRIPT = r"""
// Beginning content unchanged, and removed for brevity.
DragPlugin.prototype.draw = function(){
var obj = mpld3.get_element(this.props.id);
var drag = d3.behavior.drag()
.origin(function(d) { return {x:obj.ax.x(d[0]),
y:obj.ax.y(d[1])}; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
// Additional content unchanged, and removed for brevity
obj.elements()
.data(obj.offsets)
.style("cursor", "default")
.attr("name", "redrawable") // DIFFERENT
.call(drag);
// Also modify the 'dragstarted' function to store
// the starting position, and the 'dragended' function
// to initiate the exchange with the IPython kernel
// that will update the plot.
};
"""
def __init__(self, points):
if isinstance(points, mpl.lines.Line2D):
suffix = "pts"
else:
suffix = None
self.dict_ = {"type": "drag",
"id": mpld3.utils.get_id(points, suffix)}