title: Python interactive plot in browser: drag and drop arbitrary elements
body: <p>I have my backend in Python and I'm trying to create an interactive plot in the web browser, where the user can drag and drop elements across the plot. The plan is then to hook onto the 'drop' event with some JS that updates values in a form and submits the form, which would then cause the backend to update the plot.</p>
  <p>So far I have been using mpld3, but I am not invested in it. I would be open to a solution using some other package like Plotly or Bokeh.</p>
  <p>What I am looking for is quite similar to <a href="http://mpld3.github.io/examples/drag_points.html" rel="nofollow noreferrer">this mpld3 example</a>, except that I want to allow the user to drag objects other than the points. Specifically, I want to create some vertical and horizontal lines and allow the user to drag these horizontally and vertically, as illustrated in this picture:</p>
  <p><a href="https://i.stack.imgur.com/LWMoj.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/LWMoj.png" alt="enter image description here" /></a></p>
  <p>I have gotten so far as to adapt the mpld3 plugin to actually work with the latest version of d3.js, and I am able to drag the points (see <a href="https://gist.github.com/tadamcz/d02c57f64a37e9aee45a3654cc83d245" rel="nofollow noreferrer">gist</a>). However, I am not able to adapt the plugin to make the vertical lines draggable like the points in the example.</p>
  <p>My first idea was something like</p>
  <pre><code>lines = ax.vlines(xdata,min(ydata),ydata)
  plugins.connect(fig, DragPlugin(lines))
  </code></pre>
  <p><code>vlines()</code> returns a <code>matplotlib.collections.LineCollection</code> item, and I don't know how to access individual lines inside that. I tried simply modifying the type check inside the <code>__init__</code> (<code>if isinstance(object, mpl.lines.Line2D)</code> to be more permissive, but that didn't work.</p>
  <p>My next idea was to just draw the lines using <code>ax.plot()</code>:</p>
  <pre><code>lines = []
  for i in range(len(xdata)):
      from_xy = (xdata[i],min(ydata))
      to_xy = (xdata[i],ydata[i])
      xs = from_xy[0],to_xy[0]
      ys = from_xy[1],to_xy[1]
      lines.append(ax.plot(xs,ys, 'k-')[0])
  </code></pre>
  <p>In this case the list <code>lines</code> contains <code>matplotlib.lines.Line2D</code> objects, which I thought would be easier to manipulate individually in mpld3 than a <code>LineCollection</code>. However, the Plugin seems to be written to deal with a the entire set of red points as a  <code>Line2D</code> object (like a polygonal chain with invisible edges, if you see what I mean). This is what I conclude from the fact that the string <code>'pts'</code> is used to find the mpld3 id of the points. Just passing a Line2D object to <code>DragPlugin</code>, however, does not work:</p>
  <pre><code># don't use the suffix:
  def __init__(self, object):
          self.dict_ = {&quot;type&quot;: &quot;drag&quot;,&quot;id&quot;: utils.get_id(object)}
  
  # after defining `lines` as above:
  plugins.connect(fig, DragPlugin(lines[0]))
  </code></pre>
  <p>I get the following in the browser console:</p>
  <pre><code>Uncaught TypeError: obj.elements(...).data(...).style is not a function
  </code></pre>
  <p>Which gets raised at the following part of the JS:</p>
  <pre><code>obj.elements()
             .data(obj.offsets)
             .style(&quot;cursor&quot;, &quot;default&quot;)
             .call(drag);
  </code></pre>
  <p>But I have no idea what that JS is doing and it's not documented in the plugin.</p>
  <p>Another thing I will need is to restrict the vertical lines to be only draggable horizontally, and conversely for the horizontal lines. But first I would like to get dragging the lines to work at all.</p>