Run matplotlib without blocking the console - python

i try to make a dynamical plot in a method of a class. here is more or less the method
def plot():
axes = plt.gca(bock=False)
ydata = []
xdata = []
axes.set_xlim(0, 200)
axes.set_ylim(-1,1)
line, = axes.plot(ydata, 'r-')
i=0
while True:
xdata.append(i/10)
ydata.append(np.sin(i/10))
line.set_ydata(ydata)
line.set_xdata(xdata)
plt.draw()
plt.pause(1e-17)
i+=1
plt.show()
The problem is the fact that it's an infinity loop and during this loop function, i can do nothing. i can't use my Ipython console. I would like make run this method without block the console. i arrived to do something like that using just print and threading but matplotlib dont support threading. I tried using multiprocessing but that still block the console. any options?

So there were many problems with this code.
First: that bock argument you passed to plt.gca() threw errors.
Second: plt.show() stops execution so the animation will not start.
To go around this problem you must trigger the animation after plt.show() was called.
One way to do it is making use of events. You can read more about them here:
https://matplotlib.org/3.2.1/users/event_handling.html
Lastly, you can use a conditional and break to make sure the loop is not infinite.
Here is an example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
def plot(lim=100):
"""
Parameters
----------
lim -> int: Iteration where you want to stop
"""
axes = plt.gca()#bock=False Was removed because it threw errors
fig = plt.gcf()
canvas = fig.canvas
ydata = []
xdata = []
axes.set_xlim(0, 200)
axes.set_ylim(-1,1)
line, = axes.plot(xdata, ydata,'r-')
#Now, we can't just start the loop because plt.show() will
#Stop execcution. Instead we can make a trigger that starts
#the loop. I will use a mouse event.
def start_loop():
i=0
while True:
xdata.append(i/10)
ydata.append(np.sin(i/10))
line.set_ydata(ydata)
line.set_xdata(xdata)
canvas.draw()
canvas.flush_events()#This makes the updating less laggy
plt.pause(1e-17)#Removable
i+=1
if i==lim:
break
canvas.mpl_connect("button_press_event",
lambda click: start_loop())
#Click the plot to start the animation
plt.show()
plot()
Furthermore, if you want faster execution, make use of blit or animation functions
from matplotlib like FuncAnimation.

Related

How can I get matplotlib's imshow to refresh once every second?

Have a sensor that gives me an 8x8 grid of temps that I am to display on a live heatmap. I have made an 8x8 rand to simulate that data. This heatmap should be able to run until I tell it not to run anymore. I'm using python3 and matplotlib to attempt this visualization.
I've tried may ways to make this work, including clearing the screen, turning the plt into a figure, telling show() to not block, etc. You can see many of my attempts in the comments. It either displays once only, or never displays at all (e.g. ion() and plt.show(block=False) never display any data). I've hit my head against a wall for 2 whole work days and I can't figure out why it won't display properly.
import time
import socket
import matplotlib.pyplot as plt
import numpy as np
import random
first = True
randArray = []
#plt.ion()
plt.show(block=False)
#fig = plt.figure()
#str1 = ''.join(str(e) for e in amg.pixels)
#print (type(str1))
while True:
#create a bunch of random numbers
randArray = np.random.randint(0,50, size=(8,8))
#print the array, just so I know you're not broken
for x in randArray:
print(x)
#This is my attempt to clear.
if (first == False):
plt.clf()
first = False
#basical visualization
plt.imshow(randArray, cmap='hot', interpolation='nearest')
plt.draw()
plt.show()
#fig.canvas.draw()
#plt.canvas.draw()
#plt.display.update
print("Pausing...")
time.sleep(5)
I expect the code to generate a new set of numbers every 5 seconds, and then refresh the screen with the colors of those new numbers. This should be able to run for hours if I don't interrupt, but the screen never refreshes.
More: I have tried everything listed in the post "How to update a plot in matplotlib?" and everything they do just makes it so that no graph ever populates. The launcher acts like it's going to do something by showing up in the task bar, but then does nothing. I've tried it on a Mac and a Pi, both have the same issue. Maybe it's because that post is 8 years old, and this is python 3 not python 2? Maybe it's because I use imshow() instead of plot()? I haven't figured out how to make their code work on my machine either.
Edit: I've gotten it to work on the raspberry pi thanks to the first commenters recommendations. But now I'm left wondering.... what's wrong with my Mac??
This is a similar question to this one.
You could try to modify your code to something like this:
import time
import socket
import matplotlib.pyplot as plt
import numpy as np
import random
first = True
randArray = []
#plt.ion()
plt.show(block=False)
#fig = plt.figure()
#str1 = ''.join(str(e) for e in amg.pixels)
#print (type(str1))
fig = plt.figure()
for i in range(0,5):
#create a bunch of random numbers
randArray = np.random.randint(0,50, size=(8,8))
#print the array, just so I know you're not broken
for x in randArray:
print(x)
#This is my attempt to clear.
if (first == False):
plt.clf()
first = False
#basical visualization
ax = fig.add_subplot(111)
ax.imshow(randArray, cmap='hot', interpolation='nearest')
fig.canvas.draw()
fig.canvas.flush_events()
#fig.canvas.draw()
#plt.canvas.draw()
#plt.display.update
print("Pausing...")
time.sleep(2)
Have a nice day and get some rest :).
Try this one:
import matplotlib.pyplot as plt
import numpy as np
while True:
#create a bunch of random numbers
random_array = np.random.randint(0,50, size=(8,8))
#print the array, just so I know you're not broken
print(random_array)
#clear the image because we didn't close it
plt.clf()
#show the image
# plt.figure(figsize=(5, 5))
plt.imshow(random_array, cmap='hot', interpolation='nearest')
plt.colorbar()
print("Pausing...")
plt.pause(5)
#uncomment this line and comment the line with plt.clf()
# plt.close()
The magic is with the line plt.pause(5), it shows the image for five seconds. It's up to you if you want to close it (plt.close()) or clear it (plt.clf()). When you want to update constantly your plot, you don't use plt.show() or plt.draw(), you use plt.pause().
Uncomment some lines to try some variations... of course, some of them won't work.

A blocking, interactive plot in Jupyter notebook

I am trying to get an interactive, blocking matplotlib window out of Jupyter notebook. That is, I want the matplotlib window to come up and for execution in the notebook to pause until it closes. But various, reasonable-seeming permutations of my code don't seem to work.
The following produces the expected result:
%matplotlib
import matplotlib.pyplot as plt
a=[1,2,3]
b=[4,5,6]
plt.figure()
plt.plot(a,b)
plt.show(block=True)
print("hi")
But only once. If the code is run the second time, the kernel seems to lock up and I have to restart it, which is a no-go for my application.
The following alternatives produce an interactive window, but the code proceeds directly to the print statement without waiting for the window to be closed:
%matplotlib
import matplotlib.pyplot as plt
a=[1,2,3]
b=[4,5,6]
plt.figure()
plt.plot(a,b)
plt.show()
print("hi")
I get the same result from:
%matplotlib
import matplotlib.pyplot as plt
a=[1,2,3]
b=[4,5,6]
plt.figure()
plt.plot(a,b)
plt.ioff()
plt.show()
print("hi")
and
%matplotlib
import matplotlib.pyplot as plt
a=[1,2,3]
b=[4,5,6]
plt.figure()
plt.plot(a,b)
plt.ion()
plt.show()
print("hi")
How can I accomplish this goal? (The goal being to have print("hi") not execute until after the interactive matplotlib window closes.)
(I'm using Python 3.5.3 and Jupyter notebook server 5.0.0.)
I guess you cannot block the execution of a cell in the middle. However the usecase described in the comments seems to allow to process everything within the event loop of the figure itself.
# cell 1:
%matplotlib
import matplotlib.pyplot as plt
import numpy as np
class InterAct():
def __init__(self):
self.fig, self.ax = plt.subplots()
self.ax.axis([0,1,0,1])
self.ax.set_title("Click to choose points, close to proceed")
self.plot, = self.ax.plot([],[], color="crimson", ls="", marker="o")
self.points = []
self.fig.canvas.mpl_connect("button_press_event", self.select_point)
self.fig.canvas.mpl_connect("close_event", self.process)
plt.show()
def select_point(self, event):
self.points.append((event.xdata,event.ydata))
x,y = list(zip(*self.points))
self.plot.set_data(x,y)
self.fig.canvas.draw_idle()
def process(self, event):
points = np.array(self.points)
mean = points.mean(axis=0)
r = np.sqrt(np.sum((points-mean)**2, axis=1)).max()
self.fig2, self.ax2 = plt.subplots()
self.ax2.axis([0,1,0,1])
self.ax2.set_title("The result is:")
poly = plt.Polygon(points, edgecolor="C0", fill=True, alpha=0.5)
circ = plt.Circle(mean, r, color="crimson", fill=False)
self.ax2.add_patch(circ)
self.ax2.add_patch(poly)
self.fig2.show()
#plt.show()
And then
# cell 2
i = InterAct()
This would first show a matplotlib figure, where the user can interactively do something (in this case click to select points). Then when the user closes the figure, the points are processed and a new figure with the result is shown.
In case anybody looks for "blocking execution" until window is closed, here is a simple solution. Put this code in a cell.
# Provided the backend is interactive
# e.g. %matplotlib qt5
fig = plt.figure()
plt.show()
try:
while fig.number in plt.get_fignums():
plt.pause(0.1)
except:
plt.close(fig.number)
raise
print("hi!")
The loop in this snippet waits until the current figure is in the list of active figures. When a user closes the figure's window, its number disappears from the list returned by plt.get_fignums().
To make the cell interruptable the snippet catches exceptions. When the user stops the cell by Interrupt the kernel (aka Stop) button then KeyboardInterrupt error is injected into the Python event loop. The snippet catches it and closes the figure fig.
The duration in plt.pause defines delay in the script reaction. The bigger this value is, the longer is the delay between closing the window and printing hi!
The cell could be re-executed any number of times.

How to update Matplotlib graph when new data arrives?

I'm building a bot which fetches data from the internet every 30 seconds.
I want to plot this data using matplotlib and be able to update the graphs when I fetch some new one.
At the beginning of my script I initialise my plots.
Then I run a function to update these plots every 30 seconds, but my plot window freezes at this moment.
I've done some research but can't seem to find any working solution:
plt.show(block=False)
plt.pause(0.001)
What am I doing wrong ?
General structure of the code:
import matplotlib.pyplot as plt
import time
def init_plots():
global fig
global close_line
plt.ion()
fig = plt.figure()
main_fig = fig.add_subplot(111)
x = [datetime.fromtimestamp(x) for x in time_series]
close_line, = main_fig.plot(x, close_history, 'k-')
plt.draw()
def update_all_plots():
global close_line
x = [datetime.fromtimestamp(x) for x in time_series]
close_line.set_xdata(time_series)
close_line.set_ydata(close_history)
plt.draw()
# SCRIPT :
init_plots()
while(True):
# Fetch new data...
# Update time series...
# Update close_history...
update_plots()
time.sleep(30)
There is a module in matplotlib for specifically for plots that change over time: https://matplotlib.org/api/animation_api.html
Basically you define an update function, that updates the data for your line objects. That update function can then be used to create a FuncAnimation object which automatically calls the update function every x milliseconds:
ani = FuncAnimation(figure, update_function, repeat=True, interval=x)
There is a simple way to do it, given a panda dataframe . You would usually do something like this to draw(df is dataframe) :
ax = df.plot.line()
Using
df.plot.line(reuse_plot=True,ax=ax)
one can reuse the same figure to redraw it elegantly and probably fast enough.
Possibly duplicate of Matplotlib updating live plot

Animating with matplotlib without animation function

Is there a way to animate a graph in matplotlib without resorting to the built in animation functions? I find them extremely awkward to use and feel it would be much simpler to just plot a point, wipe the graph, then plot the next point.
I envision something such as:
def f():
# do stuff here
return x, y, t
where each t would be a different frame.
I mean, I've tried stuff like using plt.clf(), plt.close() etc. but nothing seems to work.
It is sure possible to animate without FuncAnimation. The purpose of "the enivisioned function", however, is not really clear. In an animation, the time is the independent variable, i.e. for each time step you produce some new data to plot or similar. Therefore the function would take t as an input and give some data back.
import matplotlib.pyplot as plt
import numpy as np
def f(t):
x=np.random.rand(1)
y=np.random.rand(1)
return x,y
fig, ax = plt.subplots()
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for t in range(100):
x,y = f(t)
# optionally clear axes and reset limits
#plt.gca().cla()
#ax.set_xlim(0,1)
#ax.set_ylim(0,1)
ax.plot(x, y, marker="s")
ax.set_title(str(t))
fig.canvas.draw()
plt.pause(0.1)
plt.show()
Also, it is not clear why you would want to avoid FuncAnimation. The same animation as above can be produced with FuncAnimation as follows:
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
def f(t):
x=np.random.rand(1)
y=np.random.rand(1)
return x,y
fig, ax = plt.subplots()
ax.set_xlim(0,1)
ax.set_ylim(0,1)
def update(t):
x,y = f(t)
# optionally clear axes and reset limits
#plt.gca().cla()
#ax.set_xlim(0,1)
#ax.set_ylim(0,1)
ax.plot(x, y, marker="s")
ax.set_title(str(t))
ani = matplotlib.animation.FuncAnimation(fig, update, frames=100)
plt.show()
There is not much changed, you have the same number of lines, nothing really awkward to see here.
Plus you have all the benefits from FuncAnimation when the animation gets more complex, when you want to repeat the animation, when you want to use blitting, or when you want to export it to a file.
it is not clear why you would want to avoid FuncAnimation.
For very simple tests, where you want to check a situation deep inside a loop, it is not easy to set up an animation function.
For instance, I wanted to visualize what happens with this strange sort algorithm: https://arxiv.org/pdf/2110.01111.pdf. To my opinion, the simplest way to do it is:
import numpy as np
import matplotlib.pyplot as plt
def sort(table):
n = len(table)
for i in range (n):
for j in range (n):
if table[i] < table[j]:
tmp = table[i]
table[i] = table[j]
table[j] = tmp
plt.plot(table, 'ro')
plt.title(f"i {i} j {j}")
plt.pause(0.001)
plt.clf() # clear figure
return table
n = 50
table = np.random.randint(1,101,n)
sort(table)
```python
I agree that FuncAnimation is awkward to use (not pythonic at all). Actually I believe this function doesn't make too much sense. What is the advantage to have it?
Yes, it introduces an implicit loop that you do not have to write yourself. But the reader cannot fully control this loop and -unless he knows the syntax of the function in advance- he cannot even understand it. Personally I avoid FuncAnimation for reasons of clarity and versatility. Here's a minimal pseudocode example to do that:
fig=plt.figure("animation")
M=zeros((sizeX,sizeY)) # initialize the data (your image)
im=plt.imshow(M) # make an initial plot
########### RUN THE "ANIMATION" ###########################
while {some condition}:
M=yourfunction() # updates your image
im.set_array(M) # prepare the new image
fig.canvas.draw() # draw the image
plt.pause(0.1) # slow down the "animation"
Very simple and you can see what is happening in your code.

Dynamically updating a graphed line in python [duplicate]

This question already has answers here:
pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs
(2 answers)
Closed 9 years ago.
I'm plotting a line using matplotlib and would like to update my line data as soon as new values are generated. However, once in the loop, no window appears. Even though the printed line indicates the loop is running.
Here's my code:
def inteprolate(u,X):
...
return XX
# generate initial data
XX = inteprolate(u,X)
#initial plot
xdata = XX[:,0]
ydata = XX[:,1]
ax=plt.axes()
line, = plt.plot(xdata,ydata)
# If this is in, The plot works the first time, and then pauses
# until the window is closed.
# plt.show()
# get new values and re-plot
while True:
print "!"
XX = inteprolate(u,XX)
line.set_xdata(XX[:,0])
line.set_ydata(XX[:,1])
plt.draw() # no window
How do I update my plot in real-time when the plt.show() is blocking and plt.draw doesn't update/display the window?
You need to call plt.pause in your loop to give the gui a chance to process all of the events you have given it to process. If you do not it can get backed up and never show you your graph.
# get new values and re-plot
plt.ion() # make show non-blocking
plt.show() # show the figure
while True:
print "!"
XX = inteprolate(u,XX)
line.set_xdata(XX[:,0])
line.set_ydata(XX[:,1])
plt.draw() # re-draw the figure
plt.pause(.1) # give the gui time to process the draw events
If you want to do animations, you really should learn how to use the animation module. See this awesome tutorial to get started.
You'll need plt.ion(). Take a look a this: pylab.ion() in python 2, matplotlib 1.1.1 and updating of the plot while the program runs. Also you can explore the Matplotlib animation classes : http://jakevdp.github.io/blog/2012/08/18/matplotlib-animation-tutorial/
An efficient way to do the same as #Alejandro is:
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
x = np.linspace(0,2*np.pi,num=100)
y = np.sin(x)
plt.xlim(0,2*np.pi)
plt.ylim(-1,1)
plot = plt.plot(x[0], y[0])[0]
for i in xrange(x.size):
plot.set_data(x[0:i],y[0:i])
plt.draw()
I think this toy code clarify the answer of #ardoi:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,2*np.pi,num=100)
plt.ion()
for i in xrange(x.size):
plt.plot(x[:i], np.sin(x[:i]))
plt.xlim(0,2*np.pi)
plt.ylim(-1,1)
plt.draw()
plt.clf()
Edit:
The previous code display a sinusoidal function by animating it in the screen.

Categories