Running a Function That Updates Array While Matplotlib Plots it
<p>I have a Python program (<em>main.py</em>) that has a constant stream of data that is handled by a class RTStreamer which basically gets the data -- via a live stream -- and appends it to a numpy array. However I would like to actually visualize the data as it is coming in which is where tkinter and matplotlib come in. In another python file (<em>gui.py</em>) I have a live plot using matplotlib animations and a button to trigger my first file (<em>main.py</em>) to start streaming the data. However when I click the button to start streaming the data I can see on my console that it is getting the data and appending it to the array (because I am printing the array), however the chart does not update at all. </p>

<p>Here is a simplified version of what my <em>main.py</em> looks like:</p>

<pre><code>closeBidArray = np.array([])

class RTStreamer(stream.Streamer):
    def __init__(self, *args, **kwargs):
        super(RTStreamer, self).__init__(*args, **kwargs)
        print(datetime.now(), "initialized")

    def on_success(self, data):
        # get data and append it to closeBidArray

    def on_error(self, data):
        # disconnect 

def run():
    stream = RTStreamer()
    stream.rates()
</code></pre>

<p>An here is what my <em>gui.py</em> looks like:</p>

<pre><code>import main # in order to get closeBidArray

figure = Figure(figsize=(5,5), dpi=100)
subplot1 = figure.add_subplot(111)


class App(tk.Tk):
    # Mostly to do with formatting the window and frames in it

def animate():
    subplot1.clear
    subplot1.plot(main.closeBidArray)

class MainPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        # THIS IS THE BUTTON THAT TRIGGERS TO FUNCTION THAT STREAMS THE DATA:
        button1 = tk.Button(text="Hi", command=main.run)
        button1.pack()

        canvas = FigureCanvasTkAgg(figure, self)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)

app = App()
ani = animation.FuncAnimation(figure, animate, interval=1000)   
app.mainloop()  
</code></pre>

<p>I've noticed if I hit <kbd>CTRL</kbd>+<kbd>C</kbd> to break the program, it strops streaming data and plots the array, and if I hit <kbd>CTRL</kbd>+<kbd>C</kbd> again it closes the matplotlib window altogether. However I would like to stream and append to the array while also plotting it, any ideas on how I can accomplish this? Thanks. </p>

<p>To make your code work, you need to draw the artist on each frame, not just show the canvas. However, that will be effing slow. What you really want to do, is to only update the data, and keep as much of the canvas constant. For that you use blit. Minimum working example below.</p>

<pre><code>import numpy as np
import matplotlib.pyplot as plt

def animate(movie):
    """
    Animate frames in array using blit.

    Arguments:
    ----------
        movie: (time, height, width) ndarray

    """

    plt.ion()
    fig, ax = plt.subplots(1,1)

    # initialize blit for movie axis
    img = ax.imshow(np.zeros((movie.shape[1],movie.shape[2])),
                    interpolation = 'nearest', origin = 'lower', vmin = 0, vmax = 1, cmap = 'gray')

    # cache the background
    bkg = fig.canvas.copy_from_bbox(ax.bbox)

    # loop over frames
    raw_input('Press any key to start the animation...')
    for t in range(movie.shape[0]):
        # draw movie
        img.set_data(movie[t])
        fig.canvas.restore_region(bkg)
        ax.draw_artist(img)
        fig.canvas.blit(ax.bbox)

    return

if __name__ == "__main__":
    movie = np.random.rand(1000, 10, 10)
    animate(movie)
    pass
</code></pre>

python python-3.x matplotlib tkinter