matplotlib imshow、ArtistAnimation和类属性

1 投票
1 回答
920 浏览
提问于 2025-04-17 23:28

我正在用Python编写康威的生命游戏,并想展示它的演变过程。但是我在显示输出时遇到了问题。下面是我全部的代码。

我用这个例子作为基础:来自matplotlib的文档。我的动画是静态的,但如果我使用

ims.append([plt.imshow(world.state+0, cmap=plt.cm.binary, interpolation='nearest')])

它就能正确动画化。我尝试写一个函数wolrd.get_state(),以为这是某种评估问题,但没有成功。

这让我很烦恼,我到底漏掉了什么呢?

谢谢

    import numpy as np
    from scipy.signal import convolve2d

    import matplotlib.pyplot as plt
    import matplotlib.animation as animation


    class World():
       """world information"""

       def __init__(self, grid):
           # noinspection PyNoneFunctionAssignment
           self.state = np.empty(grid.shape)
           self.__x_size = len(grid[0, :])
           self.__y_size = len(grid[:, 0])
           self.__x = range(self.__x_size)
           self.__y = range(self.__y_size)
           for i in self.__x:
            for j in self.__y:
                self.state[i, j] = grid[i, j]

    def evolve(self):
        alive_neighbours = convolve2d(self.state, np.ones((3, 3)), mode='same', boundary='wrap') - self.state
        for i in self.__y:
            for j in self.__x:
                if alive_neighbours[i, j] < 2:
                    self.state[i, j] = 0
                elif alive_neighbours[i, j] == 3:
                    self.state[i, j] = 1
                elif alive_neighbours[i, j] > 3:
                    self.state[i, j] = 0
        # self.state = np.random.randint(2, size=(self.__x_size, self.__y_size ))


if __name__ == "__main__":
    nbx = 5
    nby = 5
    nb_gen = 5

    initial_seed = np.random.randint(2, size=(nbx, nby))
    world = World(initial_seed)

    ims = []
    fig = plt.figure()

    for i in range(nb_gen):
        ims.append([plt.imshow(world.state, cmap=plt.cm.binary, interpolation='nearest')])
        world.evolve()

    ani = animation.ArtistAnimation(fig, ims, interval=500, blit=True, repeat_delay=1000)

    plt.show()

注意:我已经成功使用了:

def animate(i):
    world.evolve()
    return (plt.imshow(world.state, cmap=plt.cm.binary, interpolation='nearest'),)

ani = animation.FuncAnimation(fig, animate, frames=nb_gen, interval=500, blit=True, repeat_delay=1000)

但这不是我想要的。

1 个回答

4

动画的每一帧都显示的是相同的世界状态。

因为世界类只是在修改状态,所以每一帧都传入了同一个对象的引用给imshow()。也就是说,动画的每一帧都在引用同一个数组。

Matplotlib在调用plt.show()之前不会把动画画到屏幕上,所以你只能看到传给imshow()的数组的最终版本,也就是World.state的最终状态。

这就是为什么

a = [1, 2, 3]
b = a
a.append(4)
print(b)

输出的是[1, 2, 3, 4]。b指向a,所以当a改变时,b也会跟着改变。

把要显示的图像复制一份可以解决这个问题。

ims.append([plt.imshow(world.state.copy(), cmap=plt.cm.binary, interpolation='nearest')])

这也是为什么world.state + 0和np.random.randint(2, size=(self.__x_size, self.__y_size ))能正常工作的原因:它们都创建了新的数组,而不是修改已经传给imshow()的数组。

撰写回答