如何使用鼠标在python中移动堆栈图中的图形

时间:2017-07-28 07:28:52

标签: python matplotlib mouseevent mousemove

问题

我想用鼠标在堆栈图中移动单个图形并立即更新图形。这些值不仅应该在图表中更改(图形),还应该更新带有值的列表,以便我可以将它们打印在文件中。

问题

是否可以使用matplotlib在python中实现它,或者你会推荐别的东西吗?实现需要在python中。 Matplotlib被推荐给我,但是如果这个包不可能或者有更好的包,请发表评论。如果您有代码示例,请随时分享。

实施例

x轴表示时间(从今天到未来),y轴表示资源的数量。当一个值被削减时,不可能将图形移动到过去(左)!= 0.你可以将任何图形移动到未来(右边),没有任何东西被削减。 例如,我不能向左移动“O”,但我可以向左移动“R”和“Y”(只有一次)。

在示例中,我使用的列表只有6个条目,但您可以想象它们总是很长。

值:

x-ax = [0, 1, 2, 3, 4, 5]
y-ax = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

   R = [0, 1, 2, 1, 1, 1]
   Y = [0, 2, 1, 2, 1, 0]
   O = [5, 2, 1, 1, 1, 1]

after moving Y one right
   R = [0, 1, 2, 1, 1, 1]
   Y = [0, 0, 2, 1, 2, 1]
   O = [5, 2, 1, 1, 1, 1]

Example plot - move one graph

我拥有什么

我定义了一个图并添加了一个子图,现在我使用stackplot函数来绘制我的数据。

fig = plt.figure()
ax1 = fig.add_subplot(111)
stackPlots = ax1.stackplot(x-ax, R, Y, O)

感谢“ImportanceOfBeingErnest”!我添加了3个事件并将它们连接起来

cid_press = fig.canvas.mpl_connect('button_press_event', on_press)
cid_release = fig.canvas.mpl_connect('button_release_event', on_release)
cid_move = fig.canvas.mpl_connect('motion_notify_event', motion_notify)

def on_press(event):
if(event.button == 1):
    for stack_p in stackPlots:
        contains, attrd = stack_p.contains(event)
        if contains:
            selected_stack = stack_p
            break
    if not contains:
        return

    # I can change the color, but how can I move it?
    selected_stack .set_facecolors('black')
    plt.draw()

    print("clicked on", attrd, selected_stack)


    #print('you pressed', event.button, event.xdata, event.ydata)

def on_release(event):
    if (event.button == 1):
        print('you released', event.button, event.xdata, event.ydata)

def motion_notify(event):
    if (event.button == 1):
        return
        print('you moved', event.button, event.xdata, event.ydata)

当我现在点击图表时,它会将其颜色更改为黑色。所以我知道我有正确的,但我想移动它。 “stackplot”方法返回“PolyCollection”列表。我想我应该重做整个情节,但我需要找出哪个“PolyCollection”对应哪个数组(R,Y,O)......

感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

我认为这个问题需要分为两个主要部分。由于移动堆栈图的一个堆栈不仅是图形移动,而且需要绘制具有更改数据的新堆栈图,因此(a)首先找到计算新数据的方法和( b)然后提供一种方法,用该新数据重绘该图。

(a)沿数据

中的列移动行

我们需要一种方法将数组内的一行向左或向右移动,这样输出数组可能在执行移动的一侧有一列。这可以通过以下方式完成:

import numpy as np

class Moveable():
    def __init__(self, x,y):
        self.x = x
        self.y = y

    def move(self, row, by):
        if by >0:
            for i in range(by):
                self.moveright(row)
                self.sanitize()
        else:
            for i in range(-int(by)):
                self.moveleft(row)
                self.sanitize()

    def moveright(self,row):
        x = np.zeros(len(self.x)+1)
        x[:len(self.x)] = self.x[:]
        x[-1] = self.x[-1]+1
        y = np.zeros((self.y.shape[0], len(self.x)+1))
        y[:, :len(self.x)] = self.y[:,:]
        y[row,0] = 0
        y[row,1:] = self.y[row,:]
        self.x=x
        self.y=y

    def moveleft(self,row):
        x = np.zeros(len(self.x)+1)
        x[1:len(self.x)+1] = self.x[:]
        x[0] = self.x[0]-1
        y = np.zeros((self.y.shape[0], len(self.x)+1))
        y[:, 1:len(self.x)+1] = self.y[:,:]
        y[row,-1] = 0
        y[row,:-1] = self.y[row,:]
        self.x=x
        self.y=y

    def sanitize(self):
        if (self.y[:,0] == 0.).all():
            self.x = self.x[1:]
            self.y = self.y[:,1:]
        if (self.y[:,-1] == 0.).all():
            self.x = self.x[:-1]
            self.y = self.y[:,:-1]

用法例如是

x = [0, 1, 2, 3, 4, 5]

R = [0, 1, 2, 1, 1, 1]
Y = [0, 2, 1, 2, 1, 0]
O = [5, 2, 1, 1, 1, 1]

m= Moveable(np.array(x), np.array([R, Y, O]))
m.move(row=2, by=1)
print(m.x)  # prints [ 1.  2.  3.  4.  5.  6.]
print(m.y)  # [[ 1.  2.  1.  1.  1.  0.]
            #  [ 2.  1.  2.  1.  0.  0.]
            #  [ 5.  2.  1.  1.  1.  1.]]
# Note that 0 is not part of the x array any more, 
# but that we have 6 as new column on the right side of matrix

(b)拖动鼠标时的新堆栈图

现在我们可以使用上面的内容,一旦鼠标选择了一个堆栈并向左或向右移动一些数量,就用新的堆栈图更新轴。

import matplotlib.pyplot as plt

class StackMover():
    def __init__(self, ax, x,y, **kw):
        self.m = Moveable(np.array(x), np.array(y))
        self.xp = None
        self.ax = ax
        self.kw = kw
        self.stackplot = self.ax.stackplot(self.m.x, self.m.y, **self.kw)
        self.c1 = self.ax.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.c2 = self.ax.figure.canvas.mpl_connect('button_release_event', self.on_release)

    def on_press(self,event):
        self.xp = None
        if(event.button != 1): return
        self.row = 0
        for stack_p in self.stackplot:
            contains, attrd = stack_p.contains(event)
            if contains:
                break
            self.row += 1
        if not contains:
            return
        self.xp = event.xdata


    def on_release(self,event):
        if(event.button != 1): return
        if self.xp != None:
            by = int(event.xdata - self.xp)
            self.m.move(self.row, by)
            self.ax.clear()
            self.stackplot = self.ax.stackplot(self.m.x, self.m.y, **self.kw)
        self.ax.figure.canvas.draw_idle()
        self.xp = None


x = [0, 1, 2, 3, 4, 5]

R = [0, 1, 2, 1, 1, 1]
Y = [0, 2, 1, 2, 1, 0]
O = [5, 2, 1, 1, 1, 1]

fig, ax = plt.subplots()
stackPlots = StackMover(ax, x, [R, Y, O])

plt.show()

结果可能如下所示

enter image description here

我在这里忽略了motion_notify_event,因为我们实际上只需要鼠标拖动的起点和终点来获取要更新的堆栈。