Related
QUESTION: Whats the cleanest and simplest way to use Python's MATPLOTLIB animation function without the use of global array's or constantly appending a global "list of data points" to a plot?
Here is an example of a animated graph that plots the bid and ask sizes of a stock ticker. In this example the variables time[], ask[], and bid[] are used as global variables.
How do we modify the matplotlib animate() function to not use global variables?
so I'm trying to remove "all" global variables and just run one function call...
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
from random import randint
stock = {'ask': 12.82, 'askSize': 21900, 'bid': 12.81, 'bidSize': 17800}
def get_askSize():
return stock["askSize"] + randint(1,9000) # grab a random integer to be the next y-value in the animation
def get_bidSize():
return stock["bidSize"] + randint(1,9000) # grab a random integer to be the next y-value in the animation
def animate(i):
pt_ask = get_askSize()
pt_bid = get_bidSize()
time.append(i) #x
ask.append(pt_ask) #y
bid.append(pt_bid) #y
ax.clear()
ax.plot(time, ask)
ax.plot(time, bid)
ax.set_xlabel('Time')
ax.set_ylabel('Volume')
ax.set_title('ask and bid size')
ax.set_xlim([0,40])
#axis = axis_size(get_bidSize, get_askSize)
ylim_min = (get_askSize() + get_bidSize())/6
ylim_max = (get_askSize() + get_bidSize())
ax.set_ylim([ylim_min,ylim_max])
# create empty lists for the x and y data
time = []
ask = []
bid = []
# create the figure and axes objects
fig, ax = plt.subplots()
# run the animation
ani = FuncAnimation(fig, animate, frames=40, interval=500, repeat=False)
plt.show()
As #Warren mentioned, you can use the fargs parameter to pass in shared variables to be used in your animation function.
You should also precompute all of your points, and then use your frames to merely act as an expanding window on those frames. This will be a much more performant solution and prevents you from needing to convert between numpy arrays and lists on every tick of your animation in order to update the underlying data for your lines.
This also enables you to precompute your y-limits to prevent your resultant plot from jumping all over the place.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
rng = np.random.default_rng(0)
def animate(i, ask_line, bid_line, data):
i += 1
x = data['x'][:i]
ask_line.set_data(x, data['ask'][:i])
bid_line.set_data(x, data['bid'][:i])
stock = {'ask': 12.82, 'askSize': 21900, 'bid': 12.81, 'bidSize': 17800}
frames = 40
data = {
'x': np.arange(0, frames),
'ask': stock['askSize'] + rng.integers(0, 9000, size=frames),
'bid': stock['bidSize'] + rng.integers(0, 9000, size=frames),
}
fig, ax = plt.subplots()
ask_line, = ax.plot([], [])
bid_line, = ax.plot([], [])
ax.set(xlabel='Time', ylabel='Volume', title='ask and bid size', xlim=(0, 40))
ax.set_ylim(
min(data['ask'].min(), data['bid'].min()),
max(data['ask'].max(), data['bid'].max()),
)
# run the animation
ani = FuncAnimation(
fig, animate, fargs=(ask_line, bid_line, data),
frames=40, interval=500, repeat=False
)
plt.show()
You can use the fargs parameter of FuncAnimation to provide additional arguments to your animate callback function. So animate might start like
def animate(i, askSize, bidSize):
...
and in the call of FuncAnimation, you would add the parameter fargs=(askSize, bidSize). Add whatever variables (in whatever form) that you need to make available within the animate function.
I use this in my example of the use of FuncAnimation with AnimatedPNGWriter in the package numpngw; see Example 8. In that example, my callback function is
def update_line(num, x, data, line):
"""
Animation "call back" function for each frame.
"""
line.set_data(x, data[num, :])
return line,
and FuncAnimation is created with
ani = animation.FuncAnimation(fig, update_line, frames=len(t),
init_func=lambda : None,
fargs=(x, sol, lineplot))
You are using animation wrong, as you are adding and removing lines at each iteration, which makes the animation a lot slower. For line plots, the best way to proceed is:
initialize the figure and axes
initialize empty lines
inside the animate function, update the data of each line.
Something like this:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
from random import randint
stock = {'ask': 12.82, 'askSize': 21900, 'bid': 12.81, 'bidSize': 17800}
def get_askSize():
return stock["askSize"] + randint(1,9000) # grab a random integer to be the next y-value in the animation
def get_bidSize():
return stock["bidSize"] + randint(1,9000) # grab a random integer to be the next y-value in the animation
def add_point_to_line(x, y, line):
# retrieve the previous data in the line
xd, yd = [list(t) for t in line.get_data()]
# append the new point
xd.append(x)
yd.append(y)
# set the new data
line.set_data(xd, yd)
def animate(i):
pt_ask = get_askSize()
pt_bid = get_bidSize()
# append a new value to the lines
add_point_to_line(i, pt_ask, ax.lines[0])
add_point_to_line(i, pt_bid, ax.lines[1])
# update axis limits if necessary
ylim_min = (get_askSize() + get_bidSize())/6
ylim_max = (get_askSize() + get_bidSize())
ax.set_ylim([ylim_min,ylim_max])
# create the figure and axes objects
fig, ax = plt.subplots()
# create empty lines that will be populated on the animate function
ax.plot([], [])
ax.plot([], [])
ax.set_xlabel('Time')
ax.set_ylabel('Volume')
ax.set_title('ask and bid size')
ax.set_xlim([0,40])
# run the animation
ani = FuncAnimation(fig, animate, frames=40, interval=500, repeat=False)
plt.show()
So I'm relatively new to coding and have recently taken the monstrous task of building a few climate models for my MSc thesis. Using this code I have adapted it and it now shows no error messages except now it doesn't show any figure as an output. Any solutions?
I input
%matplotlib notebook at the top of the code, and also put plt.show(); at the bottom of the script (as per some recommendations through some similar queries)... but still doesn't work. Prior to this it was showing <Figure Ssize 432x288 with 0 Axes> which i presumed may be the problem but i can't figure out why there are 0 axes?
Any recommendations/solutions?
Thanks!
As requested - my code:
import iris.quickplot as qplt
import iris.analysis.cartography
import matplotlib.dates as mdates
def main():
Current45 = '....X.nc'
Current45 = iris.load_cube(Current45)
lats = iris.coords.DimCoord(Current45.coords()[1].points[:,0], \
standard_name='latitude', units='degrees')
lons = Current45.coords()[2].points[0]
for i in range(len(lons)):
if lons[i]>100.:
lons[i] = lons[i]-360.
lons = iris.coords.DimCoord(lons, \
standard_name='longitude', units='degrees')
Current45.remove_coord('latitude')
Current45.remove_coord('longitude')
Current45.add_dim_coord(lats, 1)
Current45.add_dim_coord(lons, 2)
Current45.convert_units('Celsius')
Colombia = iris.Constraint(longitude=lambda v: -74.73 <= v <= -76.20, \
latitude=lambda v: 5.30 <= v <= 4.43)
Current45 = Current45.extract(Colombia)
iriscc.add_day_of_year(Current45, 'time')
Current45.coord('latitude').guess_bounds()
Current45.coord('longitude').guess_bounds()
Current45_grid_areas = iris.analysis.cartography.area_weights(Current45)
Current45 = Current45.collapsed(['latitude', 'longitude'],
iris.analysis.MEAN,
weights=Current45_grid_areas)
Histogram = Current45.data
#frq, bins, patches = plt.hist(Histogram, bins=np.arange(20,37,2))
frq, bins, patches = plt.hist(Histogram, bins=np.arange(16,45,2), color='blue')
print (frq)
thresh = 32
plt.axvline(x=thresh, color='green', linestyle='dashed', linewidth=2)
plt.xlabel("Daily Max Temperature / Celsius")
plt.ylabel("Number of days")
fig = plt.gcf()
plt.show();
My code with blank figure at the bottom
In the code, you are never calling the main function, so the figure you are showing is empty.
You should call main() at some point in your code before the plt.gcf() or plt.show.
Edit
In more detail:
You are writing your main() function in this snippet of code, and then, without indent, you are calling pyplot to get the current figure, where pyplot just gives you en empty figure back (the gcf()-call is not necessary anyways in your code) and plt.show() shows no an empty figure.
You can or cannot move the plt.show() into you main() function, but at one point you must definitely call that function otherwise none of it is executed.
Edit 2:
# function definition
def main():
...
# function call
main()
# show figure
plt.show()
I'm having issues with redrawing the figure here. I allow the user to specify the units in the time scale (x-axis) and then I recalculate and call this function plots(). I want the plot to simply update, not append another plot to the figure.
def plots():
global vlgaBuffSorted
cntr()
result = collections.defaultdict(list)
for d in vlgaBuffSorted:
result[d['event']].append(d)
result_list = result.values()
f = Figure()
graph1 = f.add_subplot(211)
graph2 = f.add_subplot(212,sharex=graph1)
for item in result_list:
tL = []
vgsL = []
vdsL = []
isubL = []
for dict in item:
tL.append(dict['time'])
vgsL.append(dict['vgs'])
vdsL.append(dict['vds'])
isubL.append(dict['isub'])
graph1.plot(tL,vdsL,'bo',label='a')
graph1.plot(tL,vgsL,'rp',label='b')
graph2.plot(tL,isubL,'b-',label='c')
plotCanvas = FigureCanvasTkAgg(f, pltFrame)
toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
toolbar.pack(side=BOTTOM)
plotCanvas.get_tk_widget().pack(side=TOP)
You essentially have two options:
Do exactly what you're currently doing, but call graph1.clear() and graph2.clear() before replotting the data. This is the slowest, but most simplest and most robust option.
Instead of replotting, you can just update the data of the plot objects. You'll need to make some changes in your code, but this should be much, much faster than replotting things every time. However, the shape of the data that you're plotting can't change, and if the range of your data is changing, you'll need to manually reset the x and y axis limits.
To give an example of the second option:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
# You probably won't need this if you're embedding things in a tkinter plot...
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
fig.canvas.draw()
fig.canvas.flush_events()
You can also do like the following:
This will draw a 10x1 random matrix data on the plot for 50 cycles of the for loop.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
for i in range(50):
y = np.random.random([10,1])
plt.plot(y)
plt.draw()
plt.pause(0.0001)
plt.clf()
This worked for me. Repeatedly calls a function updating the graph every time.
import matplotlib.pyplot as plt
import matplotlib.animation as anim
def plot_cont(fun, xmax):
y = []
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
def update(i):
yi = fun()
y.append(yi)
x = range(len(y))
ax.clear()
ax.plot(x, y)
print i, ': ', yi
a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
plt.show()
"fun" is a function that returns an integer.
FuncAnimation will repeatedly call "update", it will do that "xmax" times.
This worked for me:
from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
clear_output(wait=True)
y = np.random.random([10,1])
plt.plot(y)
plt.show()
I have released a package called python-drawnow that provides functionality to let a figure update, typically called within a for loop, similar to Matlab's drawnow.
An example usage:
from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
# can be arbitrarily complex; just to draw a figure
#figure() # don't call!
plot(t, x)
#show() # don't call!
N = 1e3
figure() # call here instead!
ion() # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
x = sin(2 * pi * i**2 * t / 100.0)
drawnow(draw_fig)
This package works with any matplotlib figure and provides options to wait after each figure update or drop into the debugger.
In case anyone comes across this article looking for what I was looking for, I found examples at
How to visualize scalar 2D data with Matplotlib?
and
http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop
(on web.archive.org)
then modified them to use imshow with an input stack of frames, instead of generating and using contours on the fly.
Starting with a 3D array of images of shape (nBins, nBins, nBins), called frames.
def animate_frames(frames):
nBins = frames.shape[0]
frame = frames[0]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
for k in range(nBins):
frame = frames[k]
tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
del tempCS1
fig.canvas.draw()
#time.sleep(1e-2) #unnecessary, but useful
fig.clf()
fig = plt.figure()
ax = fig.add_subplot(111)
win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)
I also found a much simpler way to go about this whole process, albeit less robust:
fig = plt.figure()
for k in range(nBins):
plt.clf()
plt.imshow(frames[k],cmap=plt.cm.gray)
fig.canvas.draw()
time.sleep(1e-6) #unnecessary, but useful
Note that both of these only seem to work with ipython --pylab=tk, a.k.a.backend = TkAgg
Thank you for the help with everything.
All of the above might be true, however for me "online-updating" of figures only works with some backends, specifically wx. You just might try to change to this, e.g. by starting ipython/pylab by ipython --pylab=wx! Good luck!
Based on the other answers, I wrapped the figure's update in a python decorator to separate the plot's update mechanism from the actual plot. This way, it is much easier to update any plot.
def plotlive(func):
plt.ion()
#functools.wraps(func)
def new_func(*args, **kwargs):
# Clear all axes in the current figure.
axes = plt.gcf().get_axes()
for axis in axes:
axis.cla()
# Call func to plot something
result = func(*args, **kwargs)
# Draw the plot
plt.draw()
plt.pause(0.01)
return result
return new_func
Usage example
And then you can use it like any other decorator.
#plotlive
def plot_something_live(ax, x, y):
ax.plot(x, y)
ax.set_ylim([0, 100])
The only constraint is that you have to create the figure before the loop:
fig, ax = plt.subplots()
for i in range(100):
x = np.arange(100)
y = np.full([100], fill_value=i)
plot_something_live(ax, x, y)
For years, I've been struggling to get efficient live plotting in matplotlib, and to this day I remain unsatisfied.
I want a redraw_figure function that updates the figure "live" (as the code runs), and will display the latest plots if I stop at a breakpoint.
Here is some demo code:
import time
from matplotlib import pyplot as plt
import numpy as np
def live_update_demo():
plt.subplot(2, 1, 1)
h1 = plt.imshow(np.random.randn(30, 30))
redraw_figure()
plt.subplot(2, 1, 2)
h2, = plt.plot(np.random.randn(50))
redraw_figure()
t_start = time.time()
for i in xrange(1000):
h1.set_data(np.random.randn(30, 30))
redraw_figure()
h2.set_ydata(np.random.randn(50))
redraw_figure()
print 'Mean Frame Rate: %.3gFPS' % ((i+1) / (time.time() - t_start))
def redraw_figure():
plt.draw()
plt.pause(0.00001)
live_update_demo()
Plots should update live when the code is run, and we should see the latest data when stopping at any breakpoint after redraw_figure(). The question is how to best implement redraw_figure()
In the implementation above (plt.draw(); plt.pause(0.00001)), it works, but is very slow (~3.7FPS)
I can implement it as:
def redraw_figure():
plt.gcf().canvas.flush_events()
plt.show(block=False)
And it runs faster (~11FPS), but plots are not up-to date when you stop at breakpoints (eg if I put a breakpoint on the t_start = ... line, the second plot does not appear).
Strangely enough, what does actually work is calling the show twice:
def redraw_figure():
plt.gcf().canvas.flush_events()
plt.show(block=False)
plt.show(block=False)
Which gives ~11FPS and does keep plots up-to-data if your break on any line.
Now I've heard it said that the "block" keyword is deprecated. And calling the same function twice seems like a weird, probably-non-portable hack anyway.
So what can I put in this function that will plot at a reasonable frame rate, isn't a giant kludge, and preferably will work across backends and systems?
Some notes:
I'm on OSX, and using TkAgg backend, but solutions on any backend/system are welcome
Interactive mode "On" will not work, because it does not update live. It just updates when in the Python console when the interpreter waits for user input.
A blog suggested the implementation:
def redraw_figure():
fig = plt.gcf()
fig.canvas.draw()
fig.canvas.flush_events()
But at least on my system, that does not redraw the plots at all.
So, if anybody has an answer, you would directly make me and thousands of others very happy. Their happiness would probably trickle through to their friends and relatives, and their friends and relatives, and so on, so that you could potentially improve the lives of billions.
Conclusions
ImportanceOfBeingErnest shows how you can use blit for faster plotting, but it's not as simple as putting something different in the redraw_figure function (you need to keep track of what things to redraw).
First of all, the code that is posted in the question runs with 7 fps on my machine, with QT4Agg as backend.
Now, as has been suggested in many posts, like here or here, using blit might be an option. Although this article mentions that blit causes strong memory leakage, I could not observe that.
I have modified your code a bit and compared the frame rate with and without the use of blit. The code below gives
28 fps when run without blit
175 fps with blit
Code:
import time
from matplotlib import pyplot as plt
import numpy as np
def live_update_demo(blit = False):
x = np.linspace(0,50., num=100)
X,Y = np.meshgrid(x,x)
fig = plt.figure()
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)
img = ax1.imshow(X, vmin=-1, vmax=1, interpolation="None", cmap="RdBu")
line, = ax2.plot([], lw=3)
text = ax2.text(0.8,0.5, "")
ax2.set_xlim(x.min(), x.max())
ax2.set_ylim([-1.1, 1.1])
fig.canvas.draw() # note that the first draw comes before setting data
if blit:
# cache the background
axbackground = fig.canvas.copy_from_bbox(ax1.bbox)
ax2background = fig.canvas.copy_from_bbox(ax2.bbox)
plt.show(block=False)
t_start = time.time()
k=0.
for i in np.arange(1000):
img.set_data(np.sin(X/3.+k)*np.cos(Y/3.+k))
line.set_data(x, np.sin(x/3.+k))
tx = 'Mean Frame Rate:\n {fps:.3f}FPS'.format(fps= ((i+1) / (time.time() - t_start)) )
text.set_text(tx)
#print tx
k+=0.11
if blit:
# restore background
fig.canvas.restore_region(axbackground)
fig.canvas.restore_region(ax2background)
# redraw just the points
ax1.draw_artist(img)
ax2.draw_artist(line)
ax2.draw_artist(text)
# fill in the axes rectangle
fig.canvas.blit(ax1.bbox)
fig.canvas.blit(ax2.bbox)
# in this post http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
# it is mentionned that blit causes strong memory leakage.
# however, I did not observe that.
else:
# redraw everything
fig.canvas.draw()
fig.canvas.flush_events()
#alternatively you could use
#plt.pause(0.000000000001)
# however plt.pause calls canvas.draw(), as can be read here:
#http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
live_update_demo(True) # 175 fps
#live_update_demo(False) # 28 fps
Update:
For faster plotting, one may consider using pyqtgraph.
As the pyqtgraph documentation puts it: "For plotting, pyqtgraph is not nearly as complete/mature as matplotlib, but runs much faster."
I ported the above example to pyqtgraph. And although it looks kind of ugly, it runs with 250 fps on my machine.
Summing that up,
matplotlib (without blitting): 28 fps
matplotlib (with blitting): 175 fps
pyqtgraph : 250 fps
pyqtgraph code:
import sys
import time
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import pyqtgraph as pg
class App(QtGui.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
#### Create Gui Elements ###########
self.mainbox = QtGui.QWidget()
self.setCentralWidget(self.mainbox)
self.mainbox.setLayout(QtGui.QVBoxLayout())
self.canvas = pg.GraphicsLayoutWidget()
self.mainbox.layout().addWidget(self.canvas)
self.label = QtGui.QLabel()
self.mainbox.layout().addWidget(self.label)
self.view = self.canvas.addViewBox()
self.view.setAspectLocked(True)
self.view.setRange(QtCore.QRectF(0,0, 100, 100))
# image plot
self.img = pg.ImageItem(border='w')
self.view.addItem(self.img)
self.canvas.nextRow()
# line plot
self.otherplot = self.canvas.addPlot()
self.h2 = self.otherplot.plot(pen='y')
#### Set Data #####################
self.x = np.linspace(0,50., num=100)
self.X,self.Y = np.meshgrid(self.x,self.x)
self.counter = 0
self.fps = 0.
self.lastupdate = time.time()
#### Start #####################
self._update()
def _update(self):
self.data = np.sin(self.X/3.+self.counter/9.)*np.cos(self.Y/3.+self.counter/9.)
self.ydata = np.sin(self.x/3.+ self.counter/9.)
self.img.setImage(self.data)
self.h2.setData(self.ydata)
now = time.time()
dt = (now-self.lastupdate)
if dt <= 0:
dt = 0.000000000001
fps2 = 1.0 / dt
self.lastupdate = now
self.fps = self.fps * 0.9 + fps2 * 0.1
tx = 'Mean Frame Rate: {fps:.3f} FPS'.format(fps=self.fps )
self.label.setText(tx)
QtCore.QTimer.singleShot(1, self._update)
self.counter += 1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
thisapp = App()
thisapp.show()
sys.exit(app.exec_())
Here's one way to do live plotting: get the plot as an image array then draw the image to a multithreaded screen.
Example using a pyformulas screen (~30 FPS):
import pyformulas as pf
import matplotlib.pyplot as plt
import numpy as np
import time
fig = plt.figure()
screen = pf.screen(title='Plot')
start = time.time()
for i in range(10000):
t = time.time() - start
x = np.linspace(t-3, t, 100)
y = np.sin(2*np.pi*x) + np.sin(3*np.pi*x)
plt.xlim(t-3,t)
plt.ylim(-3,3)
plt.plot(x, y, c='black')
# If we haven't already shown or saved the plot, then we need to draw the figure first...
fig.canvas.draw()
image = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')
image = image.reshape(fig.canvas.get_width_height()[::-1] + (3,))
screen.update(image)
#screen.close()
Disclaimer: I'm the maintainer of pyformulas
Whenever a new txt file is added to a directory, I would like to plot and show the data from the file. If another file appears, I want the plot to update and show the new data. Creating a plot outside the main caused thread errors, so I made a (not very good) fix using global variables.
The problem is that a white figure appears and the plots do not show. After stopping the program, the white figure disappears and the plot appears. The correct image is saved to file, but I would like the image to be shown in real time. If I comment out the plt.show(), no plots appear.
I tried the "Dynamically updating plot in matplotlib" answer (Dynamically updating plot in matplotlib) but found that because it never called show(), no window appeared. If I tried calling show(), it blocked updates.
Inserting plt.pause() did not work (Real time matplotlib plot is not working while still in a loop)
The example code did not work (How to update a plot in matplotlib?)
Here is my code:
import time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import matplotlib.pyplot as plt
import numpy as np
import config
class MyHandler(PatternMatchingEventHandler):
patterns=["*.txt", "*.TXT"]
def on_created(self, event):
self.process(event)
def process(self, event):
filename = event.src_path
if '_AIt' in filename:
config.isnew=True
config.fname=filename
if __name__ == '__main__':
observer = Observer()
observer.schedule(MyHandler(), path='.', recursive=True)
observer.start()
dat=[0,1]
fig = plt.figure()
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
ax.set_xlim([0,10])
ax.set_ylim([-1,1])
plt.ion()
plt.show()
try:
while True:
time.sleep(1)
if config.isnew:
config.isnew=False
dataarray = np.array(np.transpose(np.loadtxt(config.fname)))
dat = dataarray[15] #AI0
Ln.set_ydata(dat)
Ln.set_xdata(range(len(dat)))
plt.savefig(config.fname[:-4] + '.png', bbox_inches='tight')
plt.draw()
except KeyboardInterrupt:
observer.stop()
observer.join()
config.py (creates default values of the configuration setting)
isnew=False
fname=""
I am confused because the following example code works well (from pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs)
import pylab
import time
import matplotlib.pyplot as plt
import numpy as np
dat=[0,1]
fig = plt.figure()
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
ax.set_xlim([0,20])
ax.set_ylim([0,40])
plt.ion()
plt.show()
for i in range (18):
dat=np.array(range(20))+i
Ln.set_ydata(dat)
Ln.set_xdata(range(len(dat)))
plt.pause(1)
print 'done with loop'
The following should do what you want:
import time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import matplotlib.pyplot as plt
plt.ion() # enter interactive mode
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
ax.set_xlim([0,10])
ax.set_ylim([-1,1])
plt.draw() # non-blocking drawing
plt.pause(.001) # This line is essential, without it the plot won't be shown
try:
while True:
time.sleep(1)
if config.isnew:
...
plt.draw()
plt.pause(.001)
except KeyboardInterrupt:
observer.stop()
observer.join()
The essential thin is to call plt.pause after the plt.draw call, else it won't be drawn, as the other python code block matplotlib. The value .001 is simply a try. I don't really know how it works under the hood, but this seems to work.