2

I'm writing an application do display data that changes dynamically (the data being read from a socket).

As a dummy case, I try to draw a sine with an amplitude multiplied by 1.1 each second:

import numpy as np
import matplotlib.pyplot as plt
import time

x = np.arange(0, 10, 0.1);
y = np.sin(x)


for i in xrange(100):
    plt.plot(x, y)
    time.sleep(1)
    y=y*1.1

This obviously not the way do it, but it shows my intentions.

How can it be done correctly?

EDIT: The following is the traceback output of the code suggested in @mskimm answer:

plt.show() #Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "<ipython-input-5-ed773f8e3e84>", line 7, in update
    plt.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/pyplot.py", line 466, in draw
    get_current_fig_manager().canvas.draw()
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 240, in draw
    tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
  File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 12, in blit
    tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
RuntimeError: main thread is not in main loop

EDIT 2:

it turns out that same code works when run in qtconsole... (any idea why?) How ever, each print rescaling to plot, so the "animation effect" is missing. I try to use plt.autoscale_view(False,False,False) but that just caused no plot at all.

2
  • Are you trying to show this as a somewhat 'animated' plot, or are you looking for 100 different plots here? Commented Apr 14, 2014 at 11:17
  • @davef yes, an animated plot. sorry for not being clear enough. Commented Apr 14, 2014 at 11:19

3 Answers 3

12

There are better ways to do this using the matplotlib animation API, but here's a quick and dirty approach:

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(0, 10, 0.1)
y = np.sin(x)

plt.ion()
ax = plt.gca()
ax.set_autoscale_on(True)
line, = ax.plot(x, y)

for i in xrange(100):
    line.set_ydata(y)
    ax.relim()
    ax.autoscale_view(True,True,True)
    plt.draw()
    y=y*1.1
    plt.pause(0.1)

The key steps are:

  1. Turn on interactive mode with plt.ion().
  2. Keep track of the line(s) you want to update, and overwrite their data instead of calling plot again.
  3. Give Python some time to update the plot window by calling plt.pause.

I included the code to autoscale the viewport, but that's not strictly necessary.

Sign up to request clarification or add additional context in comments.

2 Comments

when removing the autoscale - it's just what I was looking for, thanks, and have my virtual +1 :)
+1, This is much better. Because I didn't know ion, I implemented several programs using thread.
3

Is it hard to apply animated? How about using thread that updates the figure. Even though plt.show() is blocking, the thread will update the figure.

import numpy as np
import matplotlib.pyplot as plt
import time
import threading

def update(x, y):
    for i in xrange(100):
        # clear
        plt.clf()
        plt.plot(x, y)
        # draw figure
        plt.draw()
        time.sleep(1)
        y=y*1.1

x = np.arange(0, 10, 0.1);
y = np.sin(x)
plt.plot(x, y)

# use thread
t = threading.Thread(target=update, args=(x, y))
t.start()

plt.show() # blocking but thread will update figure.

1 Comment

This code throws an exception (within the new thread). A traceback added to my original post. @mskimm
0

Another solution is to redraw the plot from scratch. Surely it's slower than updating it point by point, but if you have not many points, the overhead is negligible.

from IPython import display
from matplolib import pyplot as plt


def dynamic_plot(X,Y, figsize=[10,5], max_x=None, min_y=None, max_y=None):
    '''plots dependency between X and Y dynamically: after each call current graph is redrawn'''
    plt.gcf().set_size_inches(figsize)
    plt.cla()
    plt.plot(X,Y)
    if max_x: 
        plt.gca().set_xlim(right=max_x)
    if min_y: 
        plt.gca().set_ylim(bottom=min_y)
    if max_y: 
        plt.gca().set_ylim(top=max_y)
        
    display.display(plt.gcf())
    display.clear_output(wait=True)  

And the demo use for jupyter notebook:

import time

X=[]
Y=[]
for i in range(10):
    X.append(i)
    Y.append(i**.5)
    dynamic_plot(X,Y,[14,10], max_x=10, max_y=4)  
    time.sleep(0.3)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.