为mpld3创建一个新的插件



为了学习如何创建新的mpld3插件,我采用了一个现有的示例LinkedDataPlugin (http://mpld3.github.io/examples/heart_path.html),并通过删除对lines object的引用对其进行了稍微修改。也就是说,我创建了以下内容:

class DragPlugin(plugins.PluginBase):
    JAVASCRIPT = r"""
    mpld3.register_plugin("drag", DragPlugin);
    DragPlugin.prototype = Object.create(mpld3.Plugin.prototype);
    DragPlugin.prototype.constructor = DragPlugin;
    DragPlugin.prototype.requiredProps = ["idpts", "idpatch"];
    DragPlugin.prototype.defaultProps = {}
    function DragPlugin(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };
    DragPlugin.prototype.draw = function(){
        var patchobj = mpld3.get_element(this.props.idpatch, this.fig);
        var ptsobj = mpld3.get_element(this.props.idpts, this.fig);
        var drag = d3.behavior.drag()
            .origin(function(d) { return {x:ptsobj.ax.x(d[0]),
                                          y:ptsobj.ax.y(d[1])}; })
            .on("dragstart", dragstarted)
            .on("drag", dragged)
            .on("dragend", dragended);
        patchobj.path.attr("d", patchobj.datafunc(ptsobj.offsets,
                                                  patchobj.pathcodes));
        patchobj.data = ptsobj.offsets;
        ptsobj.elements()
           .data(ptsobj.offsets)
           .style("cursor", "default")
           .call(drag);
        function dragstarted(d) {
          d3.event.sourceEvent.stopPropagation();
          d3.select(this).classed("dragging", true);
        }
        function dragged(d, i) {
          d[0] = ptsobj.ax.x.invert(d3.event.x);
          d[1] = ptsobj.ax.y.invert(d3.event.y);
          d3.select(this)
            .attr("transform", "translate(" + [d3.event.x,d3.event.y] + ")");
          patchobj.path.attr("d", patchobj.datafunc(ptsobj.offsets,
                                                    patchobj.pathcodes));
        }
        function dragended(d, i) {
          d3.select(this).classed("dragging", false);
        }
    }
    mpld3.register_plugin("drag", DragPlugin);
    """
    def __init__(self, points, patch):
        print "Points ID : ", utils.get_id(points)
        self.dict_ = {"type": "drag",
                      "idpts": utils.get_id(points),
                      "idpatch": utils.get_id(patch)}

然而,当我试图将插件链接到一个图形时,如

plugins.connect(fig, DragPlugin(points[0], patch))

我得到一个错误,'module'是不可调用的,指向这一行。这意味着什么,为什么它不起作用?谢谢。

我添加了额外的代码来显示链接多个插件可能会有问题。但这可能完全是由于我的一些愚蠢的错误,或者有一个绕过它的方法。以下基于LinkedViewPlugin的代码生成了三个面板,其中顶部和底部面板应该是相同的。在中间面板中的鼠标悬停被期望控制顶部和底部面板的显示,但是更新只发生在底部面板中。如果能够弄清楚如何在多个面板中反映变化就太好了。谢谢。

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import mpld3
from mpld3 import plugins, utils

class LinkedView(plugins.PluginBase):
    """A simple plugin showing how multiple axes can be linked"""
    JAVASCRIPT = """
    mpld3.register_plugin("linkedview", LinkedViewPlugin);
    LinkedViewPlugin.prototype = Object.create(mpld3.Plugin.prototype);
    LinkedViewPlugin.prototype.constructor = LinkedViewPlugin;
    LinkedViewPlugin.prototype.requiredProps = ["idpts", "idline", "data"];
    LinkedViewPlugin.prototype.defaultProps = {}
    function LinkedViewPlugin(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };
    LinkedViewPlugin.prototype.draw = function(){
      var pts = mpld3.get_element(this.props.idpts);
      var line = mpld3.get_element(this.props.idline);
      var data = this.props.data;
      function mouseover(d, i){
        line.data = data[i];
        line.elements().transition()
            .attr("d", line.datafunc(line.data))
            .style("stroke", this.style.fill);
      }
      pts.elements().on("mouseover", mouseover);
    };
    """
    def __init__(self, points, line, linedata):
        if isinstance(points, matplotlib.lines.Line2D):
            suffix = "pts"
        else:
            suffix = None
        self.dict_ = {"type": "linkedview",
                      "idpts": utils.get_id(points, suffix),
                      "idline": utils.get_id(line),
                      "data": linedata}
class LinkedView2(plugins.PluginBase):
    """A simple plugin showing how multiple axes can be linked"""
    JAVASCRIPT = """
    mpld3.register_plugin("linkedview", LinkedViewPlugin2);
    LinkedViewPlugin2.prototype = Object.create(mpld3.Plugin.prototype);
    LinkedViewPlugin2.prototype.constructor = LinkedViewPlugin2;
    LinkedViewPlugin2.prototype.requiredProps = ["idpts", "idline", "data"];
    LinkedViewPlugin2.prototype.defaultProps = {}
    function LinkedViewPlugin2(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };
    LinkedViewPlugin2.prototype.draw = function(){
      var pts = mpld3.get_element(this.props.idpts);
      var line = mpld3.get_element(this.props.idline);
      var data = this.props.data;
      function mouseover(d, i){
        line.data = data[i];
        line.elements().transition()
            .attr("d", line.datafunc(line.data))
            .style("stroke", this.style.fill);
      }
      pts.elements().on("mouseover", mouseover);
    };
    """
    def __init__(self, points, line, linedata):
        if isinstance(points, matplotlib.lines.Line2D):
            suffix = "pts"
        else:
            suffix = None
        self.dict_ = {"type": "linkedview",
                      "idpts": utils.get_id(points, suffix),
                      "idline": utils.get_id(line),
                      "data": linedata}
fig, ax = plt.subplots(3)
# scatter periods and amplitudes
np.random.seed(0)
P = 0.2 + np.random.random(size=20)
A = np.random.random(size=20)
x = np.linspace(0, 10, 100)
data = np.array([[x, Ai * np.sin(x / Pi)]
                 for (Ai, Pi) in zip(A, P)])
points = ax[1].scatter(P, A, c=P + A,
                       s=200, alpha=0.5)
ax[1].set_xlabel('Period')
ax[1].set_ylabel('Amplitude')
# create the line object
lines = ax[0].plot(x, 0 * x, '-w', lw=3, alpha=0.5)
ax[0].set_ylim(-1, 1)
ax[0].set_title("Hover over points to see lines")
linedata = data.transpose(0, 2, 1).tolist()
plugins.connect(fig, LinkedView(points, lines[0], linedata))
# second set of lines exactly the same but in a different panel
lines2 = ax[2].plot(x, 0 * x, '-w', lw=3, alpha=0.5)
ax[2].set_ylim(-1, 1)
ax[2].set_title("Hover over points to see lines #2")
plugins.connect(fig, LinkedView2(points, lines2[0], linedata))
mpld3.show()

2014年8月22日编辑

我正在进一步编辑此代码,以解决创建插件以控制两个轴上的行为的问题。下面是代码:

class LinkedDragPlugin(plugins.PluginBase):
    JAVASCRIPT = r"""
    mpld3.register_plugin("drag", LinkedDragPlugin);
    LinkedDragPlugin.prototype = Object.create(mpld3.Plugin.prototype);
    LinkedDragPlugin.prototype.constructor = LinkedDragPlugin;
    LinkedDragPlugin.prototype.requiredProps = ["idpts", "idline", "idpatch", 
                                                "idpts2", "idline2", "idpatch2"];
    LinkedDragPlugin.prototype.defaultProps = {}
    function LinkedDragPlugin(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };
    LinkedDragPlugin.prototype.draw = function(){
        var ptsobj = mpld3.get_element(this.props.idpts, this.fig);
        var ptsobj2 = mpld3.get_element(this.props.idpts2, this.fig);
        console.log(ptsobj)
        console.log(ptsobj2)
        var lineobj = mpld3.get_element(this.props.idline, this.fig);
        var lineobj2 = mpld3.get_element(this.props.idline2, this.fig);
        console.log(lineobj)
        console.log(lineobj2)
        var patchobj = mpld3.get_element(this.props.idpatch, this.fig);
        var patchobj2 = mpld3.get_element(this.props.idpatch2, this.fig);
        console.log(patchobj)
        console.log(patchobj2)        
    mpld3.register_plugin("drag", LinkedDragPlugin);
    """
    def __init__(self, points, line, patch):
        if isinstance(points[0], mpl.lines.Line2D):
            suffix = "pts"
        else:
            suffix = None
        self.dict_ = {"type": "drag",
                      "idpts": utils.get_id(points[0], suffix),
                      "idline": utils.get_id(line[0]),
                      "idpatch": utils.get_id(patch[0]),
                      "idpts2": utils.get_id(points[1], suffix),
                      "idline2": utils.get_id(line[1]),
                      "idpatch2": utils.get_id(patch[1])}
        print "ids :", self.dict_
bmap=brewer2mpl.get_map('Greys','Sequential',5)
fig, ax = plt.subplots(1, 2)
fig.set_size_inches(6, 4)
w = 500
h = 300
pt1 = ax[0].plot([w*0.1, w*0.9], [h*0.1, h*0.9], 'bo', ms=10, alpha=0.3)
line1 = ax[0].plot([w*0.1, w*0.9], [h*0.1, h*0.9], 'k', ms=10, lw=2, alpha=0.0)
v = [(0, h*0.1), (w, h*0.1), (0, h*0.9), (w, h*0.9),
     (w*0.1, 0), (w*0.1, h), (w*0.9, 0), (w*0.9, h)]
c = [1, 2, 1, 2, 1, 2, 1, 2]
p = path.Path(v, c)
patch = patches.PathPatch(p, fill=None, alpha=0.5)
patch1 = ax[0].add_patch(patch)
ax[0].set_xlim([0, 500])
ax[0].set_ylim([0, 300])
w = 400
h = 400
pt2 = ax[1].plot([w*0.1, w*0.9], [h*0.1, h*0.9], 'bo', ms=10, alpha=0.3)
#line2 = ax[1].plot([w*0.1, w*0.9], [h*0.1, h*0.9], 'bo', ms=10, alpha=0.3)
line2 = ax[1].plot([w*0.1, w*0.9], [h*0.1, h*0.9], 'k', ms=10, lw=2, alpha=0.0)
v = [(0, h*0.1), (w, h*0.1), (0, h*0.9), (w, h*0.9),
     (w*0.1, 0), (w*0.1, h), (w*0.9, 0), (w*0.9, h)]
c = [1, 2, 1, 2, 1, 2, 1, 2]
p = path.Path(v, c)
patch = patches.PathPatch(p, fill=None, alpha=0.5)
patch2 = ax[1].add_patch(patch)
ax[1].set_xlim([0, 400])
ax[1].set_ylim([0, 400])
plugins.connect(fig, LinkedDragPlugin([pt1[0], pt2[0]], [line1[0], line2[0]], [patch1, patch2]))
mpld3.show()

当我检查ptsobj、ptsobj2、lineobj、lineobj2、patchobj和patchobj2时,我看到

ptsobj = Marker
ptsobj2 = Marker
lineobj = Line2D
lineobj2 = null
patchobj = Patch
patchobj2 = Patch

所以有些问题需要解决…

我猜您将文件命名为DragPlugin.py,并在脚本的顶部使用import DragPlugin。试着用from DragPlugin import DragPlugin代替

最新更新