Draggable lines select one another in Matplotlib

半腔热情 提交于 2021-02-07 17:57:33

问题


I'm trying to create a class of draggable lines using matplotlib handling and picking. The aim is to set different thresholds and intervals on a graph. Here is the code:

import matplotlib.pyplot as plt
import matplotlib.lines as lines
import numpy as np

class draggable_lines:

    def __init__(self, ax, kind, XorY):

        self.ax = ax
        self.c = ax.get_figure().canvas
        self.o = kind
        self.XorY = XorY

        if kind == "h":
            x = [-1, 1]
            y = [XorY, XorY]

        elif kind == "v":
            x = [XorY, XorY]
            y = [-1, 1]

        else:
            print("choose h or v line")

        self.line = lines.Line2D(x, y, picker=5)
        self.ax.add_line(self.line)
        self.c.draw()
        sid = self.c.mpl_connect('pick_event', self.clickonline)

    # pick line when I select it 
    def clickonline(self, event):

        self.active_line = event.artist
        print("line selected ", event.artist)
        self.follower = self.c.mpl_connect("motion_notify_event", self.followmouse)
        self.releaser = self.c.mpl_connect("button_press_event", self.releaseonclick)

    # The selected line must follow the mouse
    def followmouse(self, event):

        if self.o == "h":
            self.line.set_ydata([event.ydata, event.ydata])
        else:
            self.line.set_xdata([event.xdata, event.xdata])

        self.c.draw()

    # release line on click
    def releaseonclick(self, event):

        if self.o == "h":
            self.XorY = self.line.get_ydata()[0]
        else:
            self.XorY = self.line.get_xdata()[0]

        print (self.XorY)

        self.c.mpl_disconnect(self.releaser)
        self.c.mpl_disconnect(self.follower)


plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
Vline = draggable_lines(ax, "h", 0.5)
Tline = draggable_lines(ax, "v", 0.5)
Tline2 = draggable_lines(ax, "v", 0.1)

The behavior is what I expected when using only 1 line (even if it notify the selection also when I release the line).

When I'm using more than one line it selects all of them at the same time!

I think I'm misunderstanding the event manager functionality, but I cannot understand why different objects, well distinguished (as I can see in the print("line selected ", event.artist)) should select themselves and another!


回答1:


One could ask differently: How would matplotlib know which line to drag if you click on any of them? Answer: it wouldn't, because it has three callbacks, one for each line and will execute them all.

The solution is hence to first check if the line clicked is actually the line to be moved inside the 'pick_event' callback:

if event.artist == self.line:
    # register other callbacks

(On a different note: You would benefit from not calling canvas.draw() so often, but instead canvas.draw_idle())

import matplotlib.pyplot as plt
import matplotlib.lines as lines

class draggable_lines:
    def __init__(self, ax, kind, XorY):
        self.ax = ax
        self.c = ax.get_figure().canvas
        self.o = kind
        self.XorY = XorY

        if kind == "h":
            x = [-1, 1]
            y = [XorY, XorY]

        elif kind == "v":
            x = [XorY, XorY]
            y = [-1, 1]
        self.line = lines.Line2D(x, y, picker=5)
        self.ax.add_line(self.line)
        self.c.draw_idle()
        self.sid = self.c.mpl_connect('pick_event', self.clickonline)

    def clickonline(self, event):
        if event.artist == self.line:
            print("line selected ", event.artist)
            self.follower = self.c.mpl_connect("motion_notify_event", self.followmouse)
            self.releaser = self.c.mpl_connect("button_press_event", self.releaseonclick)

    def followmouse(self, event):
        if self.o == "h":
            self.line.set_ydata([event.ydata, event.ydata])
        else:
            self.line.set_xdata([event.xdata, event.xdata])
        self.c.draw_idle()

    def releaseonclick(self, event):
        if self.o == "h":
            self.XorY = self.line.get_ydata()[0]
        else:
            self.XorY = self.line.get_xdata()[0]

        print (self.XorY)

        self.c.mpl_disconnect(self.releaser)
        self.c.mpl_disconnect(self.follower)

fig = plt.figure()
ax = fig.add_subplot(111)
Vline = draggable_lines(ax, "h", 0.5)
Tline = draggable_lines(ax, "v", 0.5)
Tline2 = draggable_lines(ax, "v", 0.1)
plt.show()



来源:https://stackoverflow.com/questions/47439355/draggable-lines-select-one-another-in-matplotlib

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!