22
votes

I am just trying to reproduce this simple example of an animation in Matplotlib but using PyPlot in Julia. I am having difficulties with the definition of the iterator simData() that is passed to the function funcAnimation , because it seems that PyPlot doesn't recognize the iterator that I defined in Julia (via a Task) as such.

Here is my approach to define the same function simData():

function simData()

    t_max = 10.0
    dt = 0.05
    x = 0.0
    t = 0.0

    function it()
        while t < t_max
            x = sin(pi*t)
            t = t+dt
            produce(x,t)
        end
    end
    Task(it)
end

As you can check, this kind of iterator yields in theory the same values than the python simData() generator of the example (try for example collect(simData()). However, I got this error when I try to do the animation

LoadError: PyError (:PyObject_Call) <type 'exceptions.TypeError'>
TypeError('PyCall.jlwrap object is not an iterator',)
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/animation.py", line 1067, in __init__
    TimedAnimation.__init__(self, fig, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/animation.py", line 913, in __init__
    *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/animation.py", line 591, in __init__
    self._init_draw()
  File "/usr/local/lib/python2.7/dist-packages/matplotlib/animation.py", line 1092, in _init_draw
    self._draw_frame(next(self.new_frame_seq()))

while loading In[5], in expression starting on line 42

 in pyerr_check at /home/diegotap/.julia/v0.4/PyCall/src/exception.jl:56
 [inlined code] from /home/diegotap/.julia/v0.4/PyCall/src/exception.jl:81
 in pycall at /home/diegotap/.julia/v0.4/PyCall/src/PyCall.jl:402
 in call at /home/diegotap/.julia/v0.4/PyCall/src/PyCall.jl:429

As I mentioned, I think the problem is that the Julia iterator is not recognized as such by Python. Do you have any idea about how to fix that?

PS: Here is a Jupyter notebook with the full code that I used to do the animation.

1

1 Answers

8
votes

In your code, you invoke FuncAnimation() this way:

ani = anim.FuncAnimation(fig, simPoints, simData, blit = false, interval=10, repeat= true)

In the original code, simData() was a generator, but in your code it isn't, it returns a generator, so I'd expect your code to invoke it this way:

ani = anim.FuncAnimation(fig, simPoints, simData(), blit = false, interval=10, repeat= true)

Let's finish the problem -- since we can't get Python to recognize the return value of simData() as an iterator, we'll ignore that feature and have simPoints() call simData() to launch the task and then return a function for Python to animate:

using PyCall
using PyPlot
pygui(true)

@pyimport matplotlib.animation as animation

function simData()
    t_max = 10.0
    dt = 0.05
    x = 0.0
    t = -dt

    function it()
        while t < t_max
            x = sin(pi * t)
            t = t + dt
            produce(x, t)
        end
    end

    Task(it)
end

function simPoints()
    task = simData()

    function points(frame_number)
        x, t = consume(task)
        line[:set_data](t, x)
        return(line, "")
    end

    points
end

figure = plt[:figure]()
axis = figure[:add_subplot](111)
line = axis[:plot]([], [], "bo", ms = 10)[1]
axis[:set_ylim](-1, 1)
axis[:set_xlim](0, 10)

ani = animation.FuncAnimation(figure, simPoints(), blit=false, interval=10, frames=200, repeat=false)

plt[:show]()

This works for one pass of the bouncing ball across the graph and stops when it hits the right edge (unlike the original Python which repeats).