我做了一个森林小动画。我的代码在问题的结尾。

代码forestfire.py包含将火焰传播到所有forest的功能。然后forestfire_test.pyforestfire.py导入为ff,然后可以通过单击matplotlib显示的数组,用鼠标将forest置于火上。

在问我问题之前,这里有一些信息:


无树:forest[i,j] = 0
一棵树:forest[i,j] = 1
着火的树:forest[i,j] = 2
哈希值:forest[i,j] = 3


基本上发生的事情是forest是n乘m的2维数组,由1到0之间的数字组成。函数onclick触发forest,而forest仍然有树触发该功能spreadfire扑灭了大火。

使用功能onclick,我可以设置森林着火(如果单击数组,树木将变成红色),而功能start,由于按钮Start,我可以执行代码。

现在的问题是,我第一次执行代码时不知道ani是什么(NameError: global name 'ani' is not defined)–这很正常,因为我甚至在调用函数start之前就调用了动画ani(试图保存它)。 。但是,如果我尝试将ani保存在函数start中,则会得到一个空白图–这也是正常的。

总而言之,我需要在调用函数start之后保存动画,但是不能在函数start的末尾保存动画,否则会得到空白图。有人可以告诉我该怎么办?

PS:我使用Spyder和IPython控制台,如果我的解释不够清楚,请告诉我。

forestfire_test.py

import forestfire as ff
import numpy as np

import matplotlib.pylab as plt
import matplotlib.colors as mcolors
from matplotlib import cm

from matplotlib.widgets import Button, Cursor


global forest
forest = np.random.rand(100,100)


# Colormap
greens = cm.Greens(np.linspace(0,1, num=50))
greensfill = cm.Greens(np.ones(25))
red = [(1,0,0,1)]*len(greens)
gray = [(.5,.5,.5,1)]*len(greens)

colors = np.vstack((greens, greensfill, red, gray))
mycmap = mcolors.LinearSegmentedColormap.from_list('my_colormap', colors)

# Figure
fig, ax = plt.subplots(figsize=(10,5))
fig.subplots_adjust(right=1.3)
im = ax.imshow(forest, animated=True, cmap = mycmap, interpolation="none", origin='lower', vmin=0, vmax=3.5)


ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.tick_params(direction='out')


cursor = Cursor(ax, useblit=True, color='red', linewidth=1)
plt.show()


# Coordinates
def onclick(event):
    x, y = int(event.xdata), int(event.ydata)
    forest[y,x] = 2.
    im.set_data(forest)
    fig.canvas.draw_idle()


fig.canvas.mpl_connect('button_press_event', onclick)


# Start button
def start(event):
    global ani
    ani = ff.forestfire(forest)

button_ax = plt.axes([0.15, 0.45, 0.2, 0.1])
button = Button(button_ax, 'Start', color='lightgrey', hovercolor='grey')
button.on_clicked(start)


# Animation
ani.save("forestfire_test.mp4", writer = 'ffmpeg', fps=5, dpi=500)


forestfire.py

from random import random

import numpy as np
from numpy.random import choice

import matplotlib.pylab as plt
import matplotlib.colors as mcolors
from matplotlib import cm

import matplotlib.animation as animation



def hazard(p):
    r=random()
    assert p>=0 and p<=1
    return r <= p


def chg(case):
    if case > 1.:
        return 1.
    else:
        return case


def spreadfire(forest):

    n,m=forest.shape
    c = np.copy(forest)

    L = xrange(3)

    for i in xrange(n):
        for j in xrange(m):

            if c[i,j] == 2.:

                sautX, sautY = choice([0,1,40],p=[0.4999,0.4999,0.0002]), choice([0,1,40],p=[0.4999,0.4999,0.0002])
                Y, X = xrange(max(0,i-1-sautY),min(n,i+2+sautY),sautY+1), xrange(max(0,j-1-sautX),min(m,j+2+sautX),sautX+1)

                for y1,y2 in zip(Y,L):
                    for x1,x2 in zip(X,L):

                        if hazard(chg(c[y1,x1])):
                            forest[y1,x1] = 2.

    return forest


def forestfire(forest):

    fig, ax = plt.subplots()

    movie, hashes = [], []

    # Colormap
    greens = cm.Greens(np.linspace(0,1, num=50))
    greensfill = cm.Greens(np.ones(25))
    red = [(1,0,0,1)]*len(greens)
    gray = [(.5,.5,.5,1)]*len(greens)

    colors = np.vstack((greens, greensfill, red, gray))
    mycmap = mcolors.LinearSegmentedColormap.from_list('my_colormap', colors)

    # Initialization
    k = 0
    firefront = 5


    forest = spreadfire(forest)

    c = np.copy(forest)
    c[np.where(c==2.)] = 3.
    hashes.append(c)

    im = plt.imshow(forest, animated=True, cmap = mycmap, interpolation="none", origin='lower', vmin=0, vmax=3.5)
    movie.append([im])

    # Fire propagation
    while np.count_nonzero(forest == 2.) != 0:
        k += 1
        print k

        if k < firefront:

            forest = spreadfire(forest)

            c = np.copy(forest)
            c[np.where(c==2.)] = 3.
            hashes.append(c)

            im = plt.imshow(forest, animated=True, cmap = mycmap, interpolation="none", origin='lower', vmin=0, vmax=3.5)
            movie.append([im])

        else:
            forest = spreadfire(forest)

            c = np.copy(forest)
            c[np.where(c==2.)] = 3.
            hashes.append(c)

            forest[np.where(hashes[0]==3.)] = 3.
            im = plt.imshow(forest, animated=True, cmap = mycmap, interpolation="none", origin='lower', vmin=0, vmax=3.5)
            movie.append([im])
            hashes.remove(hashes[0])

    return animation.ArtistAnimation(fig, movie, blit=True, repeat_delay=100)

最佳答案

如评论中所述,这里有两个问题:


您需要在启动功能内移动ani.save。这是因为ani仅在按下开始按钮后才能定义。
然后,应仅在脚本末尾调用plt.show


然后,这对我来说工作正常,在脚本上运行(在python 2.7.10,matplotlib 2.0.2上),在某种意义上,单击开始按钮后,动画将作为mp4文件保存到当前目录。

除此之外,我还需要按照previous question中的注释将帧速率设置为6或更高。

关于python - Matplotlib:单击启动动画的按钮后如何保存动画?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45270207/

10-11 14:42