为mpld3创建一个新插件

时间:2014-08-07 16:08:00

标签: python plugins matplotlib mpld3

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

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))

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

我添加了额外的代码,以表明链接多个插件可能会有问题。但这可能完全是由于我的一些愚蠢的错误,或者有一种解决方法。以下基于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

所以有待解决的问题......

1 个答案:

答案 0 :(得分:1)

我的猜测是您将文件命名为DragPlugin.py并在脚本顶部使用import DragPlugin。尝试改为from DragPlugin import DragPlugin