使用FuncAnimation

时间:2018-07-28 18:21:27

标签: python animation matplotlib

我一直在尝试注释3d散点图中的各个点,并使它们动态更新。

参考此:Matplotlib: Annotating a 3D scatter plot

但是我使用FuncAnimation来动态更新我的点,上面的链接没有一个解决方案,该解决方案可以让您知道如何在每个funcanimation间隔不断更改文本的位置。

这里的问题是,尽管我可以一开始就画出文字,但随后的间隔不会更新我的文字的位置。

下面是代码

import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation

class Simulator:

    def __init__(self):
        s_1 = ((0.5, 0.5, 0.0), (0.5,0.5,0.2), (0.5,0.5,1.0), (1.9,0.5,2.0))
        s_2 = ((1.9, 0.5, 0.0), (1.9,0.5,0.2), (1.9,0.5,1.0), (1.9,1.9,2.0))
        s_3 = ((1.2, 1.2, 0.0), (1.2,1.2,0.2), (1.2,1.2,1.0), (1.2,1.2,2.5))
        s_4 = ((0.5, 1.9, 0.0), (0.5,1.9,0.2), (0.5,1.9,1.0), (0.5,0.5,2.0))
        s_5 = ((1.9, 1.9, 0.0), (1.9,1.9,0.2), (1.9,1.9,1.0), (0.5,1.9,2.0))

        self.data = {
        's_1': {'raw': s_1},
        's_2': {'raw': s_2},
        's_3': {'raw': s_3},
        's_4': {'raw': s_4},
        's_5': {'raw': s_5}
        }

        ###### Setup ######
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111, projection='3d')
        # Setting the axes properties
        self.ax.set_xlim3d([0.0, 3.0])
        self.ax.set_xlabel('X')

        self.ax.set_ylim3d([0.0, 3.0])
        self.ax.set_ylabel('Y')

        self.ax.set_zlim3d([0.0, 3.0])
        self.ax.set_zlabel('Z')

        for point,dic in self.data.items():
            dic['x'] = []
            dic['y'] = []
            dic['z'] = []
            dic['length'] = len(dic['raw'])
            for coords in dic['raw']:
                dic['x'].append(coords[0])
                dic['y'].append(coords[1])
                dic['z'].append(coords[2])

        # Interval in milliseconds
        self.anim = animation.FuncAnimation(self.fig, self.update, init_func=self.setup, interval=1000)

        plt.show()


    def setup(self):
        plots = []

        for point,dic in self.data.items():
            dic['plot'] = self.ax.scatter3D([], [], [], c='red', picker = True)
            dic['label'] = self.ax.text3D(dic['x'][0], dic['y'][0], dic['z'][0], point, zorder=1, color='k')

    def update(self, i):

        plots = []

        seq_x = []
        seq_y = []
        seq_z = []

        for point,dic in self.data.items():
            if i < dic['length']:

                seq_x = dic['x'][i]
                seq_y = dic['y'][i]
                seq_z = dic['z'][i]
                dic['plot']._offsets3d = [seq_x], [seq_y], [seq_z]

                #### THIS IS NOT WORKING!!!! ####
                dic['label'].set_position((seq_x, seq_y, seq_z))

                #### BUT SOMEHOW THIS IS WORKING AND THE TEXT's COLORS GETS UPDATED??? ####
                dic['label'].set_color('red')

                #### IF SOMEONE IS KIND ENOUGH, I HAVE NO IDEA WHY THIS DOES NOT WORK TOO :( ####
                dic['plot'].set_color('blue')
                plots.append(dic['plot'])
            else:
                self.anim.event_source.stop()
                print('Simulation ended.')
        return plots


Simulator()

enter image description here

从上图可以看到,文本不应该在它们应该位于的位置旁边。我想要的是文字随着内容的增加而紧随其后。

以下部分不是主要问题的一部分,而是我遇到的另一个问题:

有人知道如何更改散点图的颜色吗?我有 尝试过set_color('another color'),但是它不起作用, 积分不会自动更新。

@ImportanceOfBeingErnest

嗯,好像我没有尝试使用那些解决方案一样

我不同意,除一个解决方案答案外,所有答案都显示了针对任何更新的解决方案。这就是为什么。

HYRY接受的答案:

我最初尝试过此答案,但是如果您能向我指出,我只是不知道您将在哪里输入z坐标。这个答案太复杂了。我什至不需要尖锐的箭头,我只需要始终在此处标记该点,并在该点的后面跟随标签。此外,文本仅在鼠标释放时更新,而不动态更新标签。如果您尝试使用我拥有的代码,您将意识到,即使在屏幕上四处拖动,这些点仍会自行移动,标签也应如此。

msch的答案:

我喜欢这个答案,它简单易懂,是问题的核心。这个问题基本上是此答案的基础。我想我的英语水平不如您的英语水平高,所以我再次编辑了该问题,以确保它准确地说明了它的含义。请让我知道是否仍然不清楚。

卢奇科的答案:

这个答案看起来不错,但实际上确实很复杂。我什至不知道从哪里开始。我尝试添加该类,然后使用代码

annotate3D(ax, s=str('hi'), xyz=xyz_, fontsize=10, xytext=(-3,3),textcoords='offset points', ha='right',va='bottom')

我得到了错误:TypeError:annotate3D()为参数's'获得了多个值

DonCristobal,fredcallaway和Rafael J的答案:

使用:fig.canvas.draw(),没有必要考虑我正在使用FuncAnimation。如果我要使用fig.canvas.draw(),那不会破坏使用funcanimation的目的吗?让我知道我是否错

duhaime的答案:

没有文本的更新,它只是与ax.text的一巴掌。

1 个答案:

答案 0 :(得分:1)

最简单的方法可能是创建2D文本。

text = ax.text2D(x, y, text)

然后更新其投影坐标。

x2, y2, _ = proj3d.proj_transform(seq_x, seq_y, seq_z, ax.get_proj())
text.set_position((x2,y2))

完整的工作代码:

import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
from mpl_toolkits.mplot3d import proj3d
import matplotlib.animation as animation

class Simulator:

    def __init__(self):
        s_1 = ((0.5, 0.5, 0.0), (0.5,0.5,0.2), (0.5,0.5,1.0), (1.9,0.5,2.0))
        s_2 = ((1.9, 0.5, 0.0), (1.9,0.5,0.2), (1.9,0.5,1.0), (1.9,1.9,2.0))
        s_3 = ((1.2, 1.2, 0.0), (1.2,1.2,0.2), (1.2,1.2,1.0), (1.2,1.2,2.5))
        s_4 = ((0.5, 1.9, 0.0), (0.5,1.9,0.2), (0.5,1.9,1.0), (0.5,0.5,2.0))
        s_5 = ((1.9, 1.9, 0.0), (1.9,1.9,0.2), (1.9,1.9,1.0), (0.5,1.9,2.0))

        self.data = {
        's_1': {'raw': s_1},
        's_2': {'raw': s_2},
        's_3': {'raw': s_3},
        's_4': {'raw': s_4},
        's_5': {'raw': s_5}
        }

        ###### Setup ######
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(111, projection='3d')
        # Setting the axes properties
        self.ax.set_xlim3d([0.0, 3.0])
        self.ax.set_xlabel('X')

        self.ax.set_ylim3d([0.0, 3.0])
        self.ax.set_ylabel('Y')

        self.ax.set_zlim3d([0.0, 3.0])
        self.ax.set_zlabel('Z')

        for point,dic in self.data.items():
            dic['x'] = []
            dic['y'] = []
            dic['z'] = []
            dic['length'] = len(dic['raw'])
            for coords in dic['raw']:
                dic['x'].append(coords[0])
                dic['y'].append(coords[1])
                dic['z'].append(coords[2])

        # Interval in milliseconds
        self.anim = animation.FuncAnimation(self.fig, self.update, init_func=self.setup, interval=1000)

        plt.show()


    def setup(self):
        plots = []

        for point,dic in self.data.items():
            dic['plot'] = self.ax.scatter3D([], [], [], c='red', picker = True)
            dic['label'] = self.ax.text2D(dic['x'][0], dic['y'][0], point, zorder=1, color='k')

    def update(self, i):

        plots = []

        seq_x = []
        seq_y = []
        seq_z = []

        for point,dic in self.data.items():
            if i < dic['length']:

                seq_x = dic['x'][i]
                seq_y = dic['y'][i]
                seq_z = dic['z'][i]
                dic['plot']._offsets3d = [seq_x], [seq_y], [seq_z]

                #### Set position of text
                x2, y2, _ = proj3d.proj_transform(seq_x, seq_y, seq_z, self.ax.get_proj())
                dic['label'].set_position((x2,y2))

            else:
                self.anim.event_source.stop()
                print('Simulation ended.')
        return plots


Simulator()