Networkx : Live ploting the shortest path [duplicate] - python

I am trying to plot some data from a camera in real time using OpenCV. However, the real-time plotting (using matplotlib) doesn't seem to be working.
I've isolated the problem into this simple example:
fig = plt.figure()
plt.axis([0, 1000, 0, 1])
i = 0
x = list()
y = list()
while i < 1000:
temp_y = np.random.random()
x.append(i)
y.append(temp_y)
plt.scatter(i, temp_y)
i += 1
plt.show()
I would expect this example to plot 1000 points individually. What actually happens is that the window pops up with the first point showing (ok with that), then waits for the loop to finish before it populates the rest of the graph.
Any thoughts why I am not seeing points populated one at a time?

Here's the working version of the code in question (requires at least version Matplotlib 1.1.0 from 2011-11-14):
import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 10, 0, 1])
for i in range(10):
y = np.random.random()
plt.scatter(i, y)
plt.pause(0.05)
plt.show()
Note the call to plt.pause(0.05), which both draws the new data and runs the GUI's event loop (allowing for mouse interaction).

If you're interested in realtime plotting, I'd recommend looking into matplotlib's animation API. In particular, using blit to avoid redrawing the background on every frame can give you substantial speed gains (~10x):
#!/usr/bin/env python
import numpy as np
import time
import matplotlib
matplotlib.use('GTKAgg')
from matplotlib import pyplot as plt
def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1):
""" A simple random walk with memory """
r, c = dims
gen = np.random.RandomState(seed)
pos = gen.rand(2, n) * ((r,), (c,))
old_delta = gen.randn(2, n) * sigma
while True:
delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta
pos += delta
for ii in xrange(n):
if not (0. <= pos[0, ii] < r):
pos[0, ii] = abs(pos[0, ii] % r)
if not (0. <= pos[1, ii] < c):
pos[1, ii] = abs(pos[1, ii] % c)
old_delta = delta
yield pos
def run(niter=1000, doblit=True):
"""
Display the simulation using matplotlib, optionally using blit for speed
"""
fig, ax = plt.subplots(1, 1)
ax.set_aspect('equal')
ax.set_xlim(0, 255)
ax.set_ylim(0, 255)
ax.hold(True)
rw = randomwalk()
x, y = rw.next()
plt.show(False)
plt.draw()
if doblit:
# cache the background
background = fig.canvas.copy_from_bbox(ax.bbox)
points = ax.plot(x, y, 'o')[0]
tic = time.time()
for ii in xrange(niter):
# update the xy data
x, y = rw.next()
points.set_data(x, y)
if doblit:
# restore background
fig.canvas.restore_region(background)
# redraw just the points
ax.draw_artist(points)
# fill in the axes rectangle
fig.canvas.blit(ax.bbox)
else:
# redraw everything
fig.canvas.draw()
plt.close(fig)
print "Blit = %s, average FPS: %.2f" % (
str(doblit), niter / (time.time() - tic))
if __name__ == '__main__':
run(doblit=False)
run(doblit=True)
Output:
Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27

I know I'm a bit late to answer this question. Nevertheless, I've made some code a while ago to plot live graphs, that I would like to share:
Code for PyQt4:
###################################################################
# #
# PLOT A LIVE GRAPH (PyQt4) #
# ----------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
def setCustomSize(x, width, height):
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x.setSizePolicy(sizePolicy)
x.setMinimumSize(QtCore.QSize(width, height))
x.setMaximumSize(QtCore.QSize(width, height))
''''''
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QtGui.QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
self.LAYOUT_A = QtGui.QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QtGui.QPushButton(text = 'zoom')
setCustomSize(self.zoomBtn, 100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
''''''
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
''''''
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
def addData(self, value):
self.addedData.append(value)
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
data_signal = QtCore.pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
I recently rewrote the code for PyQt5.
Code for PyQt5:
###################################################################
# #
# PLOT A LIVE GRAPH (PyQt5) #
# ----------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name())
self.LAYOUT_A = QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QPushButton(text = 'zoom')
self.zoomBtn.setFixedSize(100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
return
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
return
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
return
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
return
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
return
def addData(self, value):
self.addedData.append(value)
return
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
return
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
return
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
return
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QObject):
data_signal = pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
Just try it out. Copy-paste this code in a new python-file, and run it. You should get a beautiful, smoothly moving graph:

The top (and many other) answers were built upon plt.pause(), but that was an old way of animating the plot in matplotlib. It is not only slow, but also causes focus to be grabbed upon each update (I had a hard time stopping the plotting python process).
TL;DR: you may want to use matplotlib.animation (as mentioned in documentation).
After digging around various answers and pieces of code, this in fact proved to be a smooth way of drawing incoming data infinitely for me.
Here is my code for a quick start. It plots current time with a random number in [0, 100) every 200ms infinitely, while also handling auto rescaling of the view:
from datetime import datetime
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from random import randrange
x_data, y_data = [], []
figure = pyplot.figure()
line, = pyplot.plot_date(x_data, y_data, '-')
def update(frame):
x_data.append(datetime.now())
y_data.append(randrange(0, 100))
line.set_data(x_data, y_data)
figure.gca().relim()
figure.gca().autoscale_view()
return line,
animation = FuncAnimation(figure, update, interval=200)
pyplot.show()
You can also explore blit for even better performance as in FuncAnimation documentation.
An example from the blit documentation:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()

None of the methods worked for me.
But I have found this
Real time matplotlib plot is not working while still in a loop
All you need is to add
plt.pause(0.0001)
and then you could see the new plots.
So your code should look like this, and it will work
import matplotlib.pyplot as plt
import numpy as np
plt.ion() ## Note this correction
fig=plt.figure()
plt.axis([0,1000,0,1])
i=0
x=list()
y=list()
while i <1000:
temp_y=np.random.random();
x.append(i);
y.append(temp_y);
plt.scatter(i,temp_y);
i+=1;
plt.show()
plt.pause(0.0001) #Note this correction

show is probably not the best choice for this. What I would do is use pyplot.draw() instead. You also might want to include a small time delay (e.g., time.sleep(0.05)) in the loop so that you can see the plots happening. If I make these changes to your example it works for me and I see each point appearing one at a time.

I know this question is old, but there's now a package available called drawnow on GitHub as "python-drawnow". This provides an interface similar to MATLAB's drawnow -- you can easily update a figure.
An example for your use case:
import matplotlib.pyplot as plt
from drawnow import drawnow
def make_fig():
plt.scatter(x, y) # I think you meant this
plt.ion() # enable interactivity
fig = plt.figure() # make a figure
x = list()
y = list()
for i in range(1000):
temp_y = np.random.random()
x.append(i)
y.append(temp_y) # or any arbitrary update to your figure's data
i += 1
drawnow(make_fig)
python-drawnow is a thin wrapper around plt.draw but provides the ability to confirm (or debug) after figure display.

Another option is to go with bokeh. IMO, it is a good alternative at least for real-time plots. Here is a bokeh version of the code in the question:
from bokeh.plotting import curdoc, figure
import random
import time
def update():
global i
temp_y = random.random()
r.data_source.stream({'x': [i], 'y': [temp_y]})
i += 1
i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)
and for running it:
pip3 install bokeh
bokeh serve --show test.py
bokeh shows the result in a web browser via websocket communications. It is especially useful when data is generated by remote headless server processes.

An example use-case to plot CPU usage in real-time.
import time
import psutil
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
i = 0
x, y = [], []
while True:
x.append(i)
y.append(psutil.cpu_percent())
ax.plot(x, y, color='b')
fig.canvas.draw()
ax.set_xlim(left=max(0, i - 50), right=i + 50)
fig.show()
plt.pause(0.05)
i += 1

The problem seems to be that you expect plt.show() to show the window and then to return. It does not do that. The program will stop at that point and only resume once you close the window. You should be able to test that: If you close the window and then another window should pop up.
To resolve that problem just call plt.show() once after your loop. Then you get the complete plot. (But not a 'real-time plotting')
You can try setting the keyword-argument block like this: plt.show(block=False) once at the beginning and then use .draw() to update.

Here is a version that I got to work on my system.
import matplotlib.pyplot as plt
from drawnow import drawnow
import numpy as np
def makeFig():
plt.scatter(xList,yList) # I think you meant this
plt.ion() # enable interactivity
fig=plt.figure() # make a figure
xList=list()
yList=list()
for i in np.arange(50):
y=np.random.random()
xList.append(i)
yList.append(y)
drawnow(makeFig)
#makeFig() The drawnow(makeFig) command can be replaced
#plt.draw() with makeFig(); plt.draw()
plt.pause(0.001)
The drawnow(makeFig) line can be replaced with a makeFig(); plt.draw() sequence and it still works OK.

If you want draw and not freeze your thread as more point are drawn you should use plt.pause() not time.sleep()
im using the following code to plot a series of xy coordinates.
import matplotlib.pyplot as plt
import math
pi = 3.14159
fig, ax = plt.subplots()
x = []
y = []
def PointsInCircum(r,n=20):
circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)]
return circle
circle_list = PointsInCircum(3, 50)
for t in range(len(circle_list)):
if t == 0:
points, = ax.plot(x, y, marker='o', linestyle='--')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
else:
x_coord, y_coord = circle_list.pop()
x.append(x_coord)
y.append(y_coord)
points.set_data(x, y)
plt.pause(0.01)

This is the right way to plot Dynamic real-time matplot plots animation using while loop
There is a medium article on that too:
pip install celluloid # this will capture the image/animation
import matplotlib.pyplot as plt
import numpy as np
from celluloid import Camera # getting the camera
import matplotlib.animation as animation
from IPython import display
import time
from IPython.display import HTML
import warnings
%matplotlib notebook
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')
fig = plt.figure() #Empty fig object
ax = fig.add_subplot() #Empty axis object
camera = Camera(fig) # Camera object to capture the snap
def f(x):
''' function to create a sine wave'''
return np.sin(x) + np.random.normal(scale=0.1, size=len(x))
l = []
while True:
value = np.random.randint(9) #random number generator
l.append(value) # appneds each time number is generated
X = np.linspace(10, len(l)) # creates a line space for x axis, Equal to the length of l
for i in range(10): #plots 10 such lines
plt.plot(X, f(X))
fig.show() #shows the figure object
fig.canvas.draw()
camera.snap() # camera object to capture teh animation
time.sleep(1)
And for saving etc:
animation = camera.animate(interval = 200, repeat = True, repeat_delay = 500)
HTML(animation.to_html5_video())
animation.save('abc.mp4') # to save
output is:

Live plot with circular buffer with line style retained:
import os
import time
import psutil
import collections
import matplotlib.pyplot as plt
pts_n = 100
x = collections.deque(maxlen=pts_n)
y = collections.deque(maxlen=pts_n)
(line, ) = plt.plot(x, y, linestyle="--")
my_process = psutil.Process(os.getpid())
t_start = time.time()
while True:
x.append(time.time() - t_start)
y.append(my_process.cpu_percent())
line.set_xdata(x)
line.set_ydata(y)
plt.gca().relim()
plt.gca().autoscale_view()
plt.pause(0.1)

Related

How to have fig.canvas.draw() in a callback function ROS?

I am trying to make a visualization of a message in ros, but I can't put fig.canvas.draw() in the callback. My goal was a continually updating graph. But each time I try it I get the error "main thread is not in main loop". How do I solve this?
#! /usr/bin/env python
import rospy
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
from sensor_msgs.msg import LaserScan
x = np.linspace(0, 2* np.pi, 100)
y=np.random.random_integers(1, 100, 100)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y, 'b-')
def callback(scan):
distance_list = scan.ranges
print(distance_list)
print("scan angle min", scan.angle_min)
print("scan angle incr", scan.angle_increment)
print("scan angle max", scan.angle_max)
line1.set_ydata(distance_list)
fig.canvas.draw()
rospy.init_node('lidar_visual_node')
sub = rospy.Subscriber('\scan', LaserScan, callback)
rospy.spin()
The issue here is the callback is running in a separate thread and doesn't have access to the fig in the main thread. An object containing the callback and the required variables could solve this problem by introducing persistence.
class Visualiser:
def __init__(self):
x = np.linspace(0, 2* np.pi, 100)
y = np.random.random_integers(1, 100, 100)
plt.ion()
self.fig = plt.figure()
ax = self.fig.add_subplot(111)
self.line1, = ax.plot(x, y, 'b-')
def scanner_callback(self, scan):
distance_list = scan.ranges
print(distance_list)
print("scan angle min", scan.angle_min)
print("scan angle incr", scan.angle_increment)
print("scan angle max", scan.angle_max)
self.line1.set_ydata(distance_list)
self.fig.canvas.draw()
rospy.init_node('lidar_visual_node')
vis = Visualiser()
sub = rospy.Subscriber('\scan', LaserScan, vis.scanner_callback)
rospy.spin()

Python script stops when running plt.show of matplotlib.pyplot [duplicate]

I am trying to plot some data from a camera in real time using OpenCV. However, the real-time plotting (using matplotlib) doesn't seem to be working.
I've isolated the problem into this simple example:
fig = plt.figure()
plt.axis([0, 1000, 0, 1])
i = 0
x = list()
y = list()
while i < 1000:
temp_y = np.random.random()
x.append(i)
y.append(temp_y)
plt.scatter(i, temp_y)
i += 1
plt.show()
I would expect this example to plot 1000 points individually. What actually happens is that the window pops up with the first point showing (ok with that), then waits for the loop to finish before it populates the rest of the graph.
Any thoughts why I am not seeing points populated one at a time?
Here's the working version of the code in question (requires at least version Matplotlib 1.1.0 from 2011-11-14):
import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 10, 0, 1])
for i in range(10):
y = np.random.random()
plt.scatter(i, y)
plt.pause(0.05)
plt.show()
Note the call to plt.pause(0.05), which both draws the new data and runs the GUI's event loop (allowing for mouse interaction).
If you're interested in realtime plotting, I'd recommend looking into matplotlib's animation API. In particular, using blit to avoid redrawing the background on every frame can give you substantial speed gains (~10x):
#!/usr/bin/env python
import numpy as np
import time
import matplotlib
matplotlib.use('GTKAgg')
from matplotlib import pyplot as plt
def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1):
""" A simple random walk with memory """
r, c = dims
gen = np.random.RandomState(seed)
pos = gen.rand(2, n) * ((r,), (c,))
old_delta = gen.randn(2, n) * sigma
while True:
delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta
pos += delta
for ii in xrange(n):
if not (0. <= pos[0, ii] < r):
pos[0, ii] = abs(pos[0, ii] % r)
if not (0. <= pos[1, ii] < c):
pos[1, ii] = abs(pos[1, ii] % c)
old_delta = delta
yield pos
def run(niter=1000, doblit=True):
"""
Display the simulation using matplotlib, optionally using blit for speed
"""
fig, ax = plt.subplots(1, 1)
ax.set_aspect('equal')
ax.set_xlim(0, 255)
ax.set_ylim(0, 255)
ax.hold(True)
rw = randomwalk()
x, y = rw.next()
plt.show(False)
plt.draw()
if doblit:
# cache the background
background = fig.canvas.copy_from_bbox(ax.bbox)
points = ax.plot(x, y, 'o')[0]
tic = time.time()
for ii in xrange(niter):
# update the xy data
x, y = rw.next()
points.set_data(x, y)
if doblit:
# restore background
fig.canvas.restore_region(background)
# redraw just the points
ax.draw_artist(points)
# fill in the axes rectangle
fig.canvas.blit(ax.bbox)
else:
# redraw everything
fig.canvas.draw()
plt.close(fig)
print "Blit = %s, average FPS: %.2f" % (
str(doblit), niter / (time.time() - tic))
if __name__ == '__main__':
run(doblit=False)
run(doblit=True)
Output:
Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27
I know I'm a bit late to answer this question. Nevertheless, I've made some code a while ago to plot live graphs, that I would like to share:
Code for PyQt4:
###################################################################
# #
# PLOT A LIVE GRAPH (PyQt4) #
# ----------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
def setCustomSize(x, width, height):
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x.setSizePolicy(sizePolicy)
x.setMinimumSize(QtCore.QSize(width, height))
x.setMaximumSize(QtCore.QSize(width, height))
''''''
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QtGui.QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
self.LAYOUT_A = QtGui.QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QtGui.QPushButton(text = 'zoom')
setCustomSize(self.zoomBtn, 100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
''''''
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
''''''
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
def addData(self, value):
self.addedData.append(value)
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
data_signal = QtCore.pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
I recently rewrote the code for PyQt5.
Code for PyQt5:
###################################################################
# #
# PLOT A LIVE GRAPH (PyQt5) #
# ----------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name())
self.LAYOUT_A = QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QPushButton(text = 'zoom')
self.zoomBtn.setFixedSize(100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
return
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
return
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
return
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
return
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
return
def addData(self, value):
self.addedData.append(value)
return
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
return
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
return
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
return
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QObject):
data_signal = pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
Just try it out. Copy-paste this code in a new python-file, and run it. You should get a beautiful, smoothly moving graph:
The top (and many other) answers were built upon plt.pause(), but that was an old way of animating the plot in matplotlib. It is not only slow, but also causes focus to be grabbed upon each update (I had a hard time stopping the plotting python process).
TL;DR: you may want to use matplotlib.animation (as mentioned in documentation).
After digging around various answers and pieces of code, this in fact proved to be a smooth way of drawing incoming data infinitely for me.
Here is my code for a quick start. It plots current time with a random number in [0, 100) every 200ms infinitely, while also handling auto rescaling of the view:
from datetime import datetime
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from random import randrange
x_data, y_data = [], []
figure = pyplot.figure()
line, = pyplot.plot_date(x_data, y_data, '-')
def update(frame):
x_data.append(datetime.now())
y_data.append(randrange(0, 100))
line.set_data(x_data, y_data)
figure.gca().relim()
figure.gca().autoscale_view()
return line,
animation = FuncAnimation(figure, update, interval=200)
pyplot.show()
You can also explore blit for even better performance as in FuncAnimation documentation.
An example from the blit documentation:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
None of the methods worked for me.
But I have found this
Real time matplotlib plot is not working while still in a loop
All you need is to add
plt.pause(0.0001)
and then you could see the new plots.
So your code should look like this, and it will work
import matplotlib.pyplot as plt
import numpy as np
plt.ion() ## Note this correction
fig=plt.figure()
plt.axis([0,1000,0,1])
i=0
x=list()
y=list()
while i <1000:
temp_y=np.random.random();
x.append(i);
y.append(temp_y);
plt.scatter(i,temp_y);
i+=1;
plt.show()
plt.pause(0.0001) #Note this correction
show is probably not the best choice for this. What I would do is use pyplot.draw() instead. You also might want to include a small time delay (e.g., time.sleep(0.05)) in the loop so that you can see the plots happening. If I make these changes to your example it works for me and I see each point appearing one at a time.
I know this question is old, but there's now a package available called drawnow on GitHub as "python-drawnow". This provides an interface similar to MATLAB's drawnow -- you can easily update a figure.
An example for your use case:
import matplotlib.pyplot as plt
from drawnow import drawnow
def make_fig():
plt.scatter(x, y) # I think you meant this
plt.ion() # enable interactivity
fig = plt.figure() # make a figure
x = list()
y = list()
for i in range(1000):
temp_y = np.random.random()
x.append(i)
y.append(temp_y) # or any arbitrary update to your figure's data
i += 1
drawnow(make_fig)
python-drawnow is a thin wrapper around plt.draw but provides the ability to confirm (or debug) after figure display.
Another option is to go with bokeh. IMO, it is a good alternative at least for real-time plots. Here is a bokeh version of the code in the question:
from bokeh.plotting import curdoc, figure
import random
import time
def update():
global i
temp_y = random.random()
r.data_source.stream({'x': [i], 'y': [temp_y]})
i += 1
i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)
and for running it:
pip3 install bokeh
bokeh serve --show test.py
bokeh shows the result in a web browser via websocket communications. It is especially useful when data is generated by remote headless server processes.
An example use-case to plot CPU usage in real-time.
import time
import psutil
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
i = 0
x, y = [], []
while True:
x.append(i)
y.append(psutil.cpu_percent())
ax.plot(x, y, color='b')
fig.canvas.draw()
ax.set_xlim(left=max(0, i - 50), right=i + 50)
fig.show()
plt.pause(0.05)
i += 1
The problem seems to be that you expect plt.show() to show the window and then to return. It does not do that. The program will stop at that point and only resume once you close the window. You should be able to test that: If you close the window and then another window should pop up.
To resolve that problem just call plt.show() once after your loop. Then you get the complete plot. (But not a 'real-time plotting')
You can try setting the keyword-argument block like this: plt.show(block=False) once at the beginning and then use .draw() to update.
Here is a version that I got to work on my system.
import matplotlib.pyplot as plt
from drawnow import drawnow
import numpy as np
def makeFig():
plt.scatter(xList,yList) # I think you meant this
plt.ion() # enable interactivity
fig=plt.figure() # make a figure
xList=list()
yList=list()
for i in np.arange(50):
y=np.random.random()
xList.append(i)
yList.append(y)
drawnow(makeFig)
#makeFig() The drawnow(makeFig) command can be replaced
#plt.draw() with makeFig(); plt.draw()
plt.pause(0.001)
The drawnow(makeFig) line can be replaced with a makeFig(); plt.draw() sequence and it still works OK.
If you want draw and not freeze your thread as more point are drawn you should use plt.pause() not time.sleep()
im using the following code to plot a series of xy coordinates.
import matplotlib.pyplot as plt
import math
pi = 3.14159
fig, ax = plt.subplots()
x = []
y = []
def PointsInCircum(r,n=20):
circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)]
return circle
circle_list = PointsInCircum(3, 50)
for t in range(len(circle_list)):
if t == 0:
points, = ax.plot(x, y, marker='o', linestyle='--')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
else:
x_coord, y_coord = circle_list.pop()
x.append(x_coord)
y.append(y_coord)
points.set_data(x, y)
plt.pause(0.01)
This is the right way to plot Dynamic real-time matplot plots animation using while loop
There is a medium article on that too:
pip install celluloid # this will capture the image/animation
import matplotlib.pyplot as plt
import numpy as np
from celluloid import Camera # getting the camera
import matplotlib.animation as animation
from IPython import display
import time
from IPython.display import HTML
import warnings
%matplotlib notebook
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')
fig = plt.figure() #Empty fig object
ax = fig.add_subplot() #Empty axis object
camera = Camera(fig) # Camera object to capture the snap
def f(x):
''' function to create a sine wave'''
return np.sin(x) + np.random.normal(scale=0.1, size=len(x))
l = []
while True:
value = np.random.randint(9) #random number generator
l.append(value) # appneds each time number is generated
X = np.linspace(10, len(l)) # creates a line space for x axis, Equal to the length of l
for i in range(10): #plots 10 such lines
plt.plot(X, f(X))
fig.show() #shows the figure object
fig.canvas.draw()
camera.snap() # camera object to capture teh animation
time.sleep(1)
And for saving etc:
animation = camera.animate(interval = 200, repeat = True, repeat_delay = 500)
HTML(animation.to_html5_video())
animation.save('abc.mp4') # to save
output is:
Live plot with circular buffer with line style retained:
import os
import time
import psutil
import collections
import matplotlib.pyplot as plt
pts_n = 100
x = collections.deque(maxlen=pts_n)
y = collections.deque(maxlen=pts_n)
(line, ) = plt.plot(x, y, linestyle="--")
my_process = psutil.Process(os.getpid())
t_start = time.time()
while True:
x.append(time.time() - t_start)
y.append(my_process.cpu_percent())
line.set_xdata(x)
line.set_ydata(y)
plt.gca().relim()
plt.gca().autoscale_view()
plt.pause(0.1)

Python - Real Time Plotting

I wrote a simple python script that display my sensor data (read from Serial).
I wish to understand why the figure crashes when I'm clicking anywhere...
Is there any - simple way, if possible - to avoid the script to crash if I try to do something else on the computer?
Moreover, if I want to develop a simple GUI, has someone an example to learn how to do it? Does it will avoid this problem ?
import serial
import re
import numpy as np
import time
from matplotlib import pyplot as plt
SERIAL_PORT = 'COM3'
BAUDRATE = 9600
BUFFER_SIZE = 30
print("initialisation ...")
ser = serial.Serial(SERIAL_PORT, BAUDRATE)
buffer = []
plt.ion()
fig = plt.figure()
ydata = [0]*BUFFER_SIZE*2
prevBuf = [0]*BUFFER_SIZE*10
ax1=plt.axes()
line, = plt.plot(prevBuf)
plt.ylim([2000,10000])
def clean_serial():
i=0
while i<=5:
data = ser.readline()
print data
i+=1
ser.flush()
def extract_number(rawdata):
return float(re.sub("[^0-9.]", " ", rawdata))
def process_buffer(buf):
prevBuf[len(prevBuf)-BUFFER_SIZE:] = buf
line.set_xdata(np.arange(len(prevBuf)))
line.set_ydata(prevBuf)
ydataMax = max(prevBuf)+10
ydataMin = min(prevBuf)-100
plt.ylim([ydataMin,ydataMax])
plt.draw()
prevBuf[:] = prevBuf[BUFFER_SIZE:]+buf
print("cleaning serial")
clean_serial()
print("reading value from serial")
while True:
data = extract_number(ser.readline())
buffer.append(data)
if len(buffer)>= BUFFER_SIZE:
process_buffer(buffer)
buffer=[]
ser.close()
which output : script crashing
Thank you !
I wrote a Python app that could be useful for you:
###################################################################
# #
# PLOTTING A LIVE GRAPH #
# ---------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
def setCustomSize(x, width, height):
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x.setSizePolicy(sizePolicy)
x.setMinimumSize(QtCore.QSize(width, height))
x.setMaximumSize(QtCore.QSize(width, height))
''''''
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QtGui.QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
self.LAYOUT_A = QtGui.QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QtGui.QPushButton(text = 'zoom')
setCustomSize(self.zoomBtn, 100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
''''''
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
''''''
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
def addData(self, value):
self.addedData.append(value)
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
data_signal = QtCore.pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
Just try it out. Copy-paste this code in a new python-file, and run it. You should get a beautiful, smoothly moving graph:
The actual data is sent in the dataSendLoop(..) function. This function runs in a separate thread. Notice that the data gets sent in this codeline within that function:
mySrc.data_signal.emit(...)
Just put the value you want to add between the brackets. That value will get added to the moving graph next time it gets a screen refresh.

Can I use Animation with Matplotlib widget for pyqt4?

I found this cool animation for Matlibplot and want to add it to my plot widget in a Pyqt program. This is the method that shows regular plots successfully in my program
def plot(self):
ax = self.ui.figure.add_subplot(111)
ax.hold(False)
ax.plot([1,2,3],[4,5,6])
self.ui.canvas.draw()
I thought I could just add the single def from the animation code to my Form and call the animation the same as above, but alas no. Here is the animation code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
N = 100
ON = 255
OFF = 0
vals = [ON, OFF]
# populate grid with random on/off - more off than on
grid = np.random.choice(vals, N*N, p=[0.2, 0.8]).reshape(N, N)
def update(data):
global grid
# copy grid since we require 8 neighbors for calculation
# and we go line by line
newGrid = grid.copy()
for i in range(N):
for j in range(N):
# compute 8-neghbor sum
# using toroidal boundary conditions - x and y wrap around
# so that the simulaton takes place on a toroidal surface.
total = (grid[i, (j-1)%N] + grid[i, (j+1)%N] +
grid[(i-1)%N, j] + grid[(i+1)%N, j] +
grid[(i-1)%N, (j-1)%N] + grid[(i-1)%N, (j+1)%N] +
grid[(i+1)%N, (j-1)%N] + grid[(i+1)%N, (j+1)%N])/255
# apply Conway's rules
if grid[i, j] == ON:
if (total < 2) or (total > 3):
newGrid[i, j] = OFF
else:
if total == 3:
newGrid[i, j] = ON
# update data
mat.set_data(newGrid)
grid = newGrid
return [mat]
# set up animation
fig, ax = plt.subplots()
mat = ax.matshow(grid)
ani = animation.FuncAnimation(fig, update, interval=50,
save_count=50)
plt.show()
You can use your found code, just change your method to:
def plot(self):
ax = self.ui.figure.add_subplot(111)
global mat
mat = ax.matshow(grid)
ani = animation.FuncAnimation(figure, update, interval=50, save_count=50)
self.ui.canvas.draw()
Note, that you don't have to use ax.hold(False) and it is likely that animation will work more slowly when using subplots(try increasing resolution [N] to see difference). I have such speed problems with 3D plots in my own project - very frustrating =D
I made small sample program using class instead of global variables, maybe it comes in handy:
import sys
import numpy as np
from PyQt4 import QtGui
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from functools import partial
class Game_of_life(QtGui.QWidget):
def __init__(self, N, ON, OFF):
super(Game_of_life, self).__init__()
self.N = N
self.ON = ON
self.OFF = OFF
self.vals = [ON, OFF]
self.grid = np.random.choice(self.vals, N*N, p=[0.2, 0.8]).reshape(N, N)
self.start()
def start(self):
self.setWindowTitle('Game of life')
gridLayout = QtGui.QGridLayout()
self.setLayout(gridLayout)
#Figure and subplot
figure = plt.figure()
canvas = FigureCanvas(figure)
ax = figure.add_subplot(111)
canvas.draw()
self.mat = ax.matshow(self.grid)
ani = animation.FuncAnimation(figure, self.update, interval=50, save_count=50)
#button
restart = QtGui.QPushButton("Restart game of life")
restart.clicked.connect(partial(self.restart_animation, ax=ax, figure=figure))
gridLayout.addWidget(canvas,0,0)
gridLayout.addWidget(restart, 1,0)
self.show()
def update(self, data):
newGrid = self.grid.copy()
for i in range(self.N):
for j in range(self.N):
total = (self.grid[i, (j-1)%self.N] + self.grid[i, (j+1)%self.N] +
self.grid[(i-1)%self.N, j] + self.grid[(i+1)%self.N, j] +
self.grid[(i-1)%self.N, (j-1)%self.N] + self.grid[(i-1)%self.N, (j+1)%self.N] +
self.grid[(i+1)%self.N, (j-1)%self.N] + self.grid[(i+1)%self.N, (j+1)%self.N])/255
if self.grid[i, j] == self.ON:
if (total < 2) or (total > 3):
newGrid[i, j] = self.OFF
else:
if total == 3:
newGrid[i, j] = self.ON
self.mat.set_data(newGrid)
self.grid = newGrid
#simply restarts game data
def restart_animation(self, ax, figure):
self.grid = np.random.choice(self.vals, self.N*self.N, p=[0.2, 0.8]).reshape(self.N, self.N)
self.mat = ax.matshow(self.grid)
def main():
app = QtGui.QApplication(sys.argv)
widget = Game_of_life(100, 255, 0)
#widget can be implement in other layout
sys.exit(app.exec_())
if __name__ == "__main__":
main()

how to plot a Live graph using matplotlib [duplicate]

I am trying to plot some data from a camera in real time using OpenCV. However, the real-time plotting (using matplotlib) doesn't seem to be working.
I've isolated the problem into this simple example:
fig = plt.figure()
plt.axis([0, 1000, 0, 1])
i = 0
x = list()
y = list()
while i < 1000:
temp_y = np.random.random()
x.append(i)
y.append(temp_y)
plt.scatter(i, temp_y)
i += 1
plt.show()
I would expect this example to plot 1000 points individually. What actually happens is that the window pops up with the first point showing (ok with that), then waits for the loop to finish before it populates the rest of the graph.
Any thoughts why I am not seeing points populated one at a time?
Here's the working version of the code in question (requires at least version Matplotlib 1.1.0 from 2011-11-14):
import numpy as np
import matplotlib.pyplot as plt
plt.axis([0, 10, 0, 1])
for i in range(10):
y = np.random.random()
plt.scatter(i, y)
plt.pause(0.05)
plt.show()
Note the call to plt.pause(0.05), which both draws the new data and runs the GUI's event loop (allowing for mouse interaction).
If you're interested in realtime plotting, I'd recommend looking into matplotlib's animation API. In particular, using blit to avoid redrawing the background on every frame can give you substantial speed gains (~10x):
#!/usr/bin/env python
import numpy as np
import time
import matplotlib
matplotlib.use('GTKAgg')
from matplotlib import pyplot as plt
def randomwalk(dims=(256, 256), n=20, sigma=5, alpha=0.95, seed=1):
""" A simple random walk with memory """
r, c = dims
gen = np.random.RandomState(seed)
pos = gen.rand(2, n) * ((r,), (c,))
old_delta = gen.randn(2, n) * sigma
while True:
delta = (1. - alpha) * gen.randn(2, n) * sigma + alpha * old_delta
pos += delta
for ii in xrange(n):
if not (0. <= pos[0, ii] < r):
pos[0, ii] = abs(pos[0, ii] % r)
if not (0. <= pos[1, ii] < c):
pos[1, ii] = abs(pos[1, ii] % c)
old_delta = delta
yield pos
def run(niter=1000, doblit=True):
"""
Display the simulation using matplotlib, optionally using blit for speed
"""
fig, ax = plt.subplots(1, 1)
ax.set_aspect('equal')
ax.set_xlim(0, 255)
ax.set_ylim(0, 255)
ax.hold(True)
rw = randomwalk()
x, y = rw.next()
plt.show(False)
plt.draw()
if doblit:
# cache the background
background = fig.canvas.copy_from_bbox(ax.bbox)
points = ax.plot(x, y, 'o')[0]
tic = time.time()
for ii in xrange(niter):
# update the xy data
x, y = rw.next()
points.set_data(x, y)
if doblit:
# restore background
fig.canvas.restore_region(background)
# redraw just the points
ax.draw_artist(points)
# fill in the axes rectangle
fig.canvas.blit(ax.bbox)
else:
# redraw everything
fig.canvas.draw()
plt.close(fig)
print "Blit = %s, average FPS: %.2f" % (
str(doblit), niter / (time.time() - tic))
if __name__ == '__main__':
run(doblit=False)
run(doblit=True)
Output:
Blit = False, average FPS: 54.37
Blit = True, average FPS: 438.27
I know I'm a bit late to answer this question. Nevertheless, I've made some code a while ago to plot live graphs, that I would like to share:
Code for PyQt4:
###################################################################
# #
# PLOT A LIVE GRAPH (PyQt4) #
# ----------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt4 import QtGui
from PyQt4 import QtCore
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt4Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
def setCustomSize(x, width, height):
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(x.sizePolicy().hasHeightForWidth())
x.setSizePolicy(sizePolicy)
x.setMinimumSize(QtCore.QSize(width, height))
x.setMaximumSize(QtCore.QSize(width, height))
''''''
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QtGui.QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QtGui.QColor(210,210,235,255).name())
self.LAYOUT_A = QtGui.QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QtGui.QPushButton(text = 'zoom')
setCustomSize(self.zoomBtn, 100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
''''''
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
''''''
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
def addData(self, value):
self.addedData.append(value)
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QtCore.QObject):
data_signal = QtCore.pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
I recently rewrote the code for PyQt5.
Code for PyQt5:
###################################################################
# #
# PLOT A LIVE GRAPH (PyQt5) #
# ----------------------------- #
# EMBED A MATPLOTLIB ANIMATION INSIDE YOUR #
# OWN GUI! #
# #
###################################################################
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import functools
import numpy as np
import random as rd
import matplotlib
matplotlib.use("Qt5Agg")
from matplotlib.figure import Figure
from matplotlib.animation import TimedAnimation
from matplotlib.lines import Line2D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import time
import threading
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# Define the geometry of the main window
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("my first window")
# Create FRAME_A
self.FRAME_A = QFrame(self)
self.FRAME_A.setStyleSheet("QWidget { background-color: %s }" % QColor(210,210,235,255).name())
self.LAYOUT_A = QGridLayout()
self.FRAME_A.setLayout(self.LAYOUT_A)
self.setCentralWidget(self.FRAME_A)
# Place the zoom button
self.zoomBtn = QPushButton(text = 'zoom')
self.zoomBtn.setFixedSize(100, 50)
self.zoomBtn.clicked.connect(self.zoomBtnAction)
self.LAYOUT_A.addWidget(self.zoomBtn, *(0,0))
# Place the matplotlib figure
self.myFig = CustomFigCanvas()
self.LAYOUT_A.addWidget(self.myFig, *(0,1))
# Add the callbackfunc to ..
myDataLoop = threading.Thread(name = 'myDataLoop', target = dataSendLoop, daemon = True, args = (self.addData_callbackFunc,))
myDataLoop.start()
self.show()
return
def zoomBtnAction(self):
print("zoom in")
self.myFig.zoomIn(5)
return
def addData_callbackFunc(self, value):
# print("Add data: " + str(value))
self.myFig.addData(value)
return
''' End Class '''
class CustomFigCanvas(FigureCanvas, TimedAnimation):
def __init__(self):
self.addedData = []
print(matplotlib.__version__)
# The data
self.xlim = 200
self.n = np.linspace(0, self.xlim - 1, self.xlim)
a = []
b = []
a.append(2.0)
a.append(4.0)
a.append(2.0)
b.append(4.0)
b.append(3.0)
b.append(4.0)
self.y = (self.n * 0.0) + 50
# The window
self.fig = Figure(figsize=(5,5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
# self.ax1 settings
self.ax1.set_xlabel('time')
self.ax1.set_ylabel('raw data')
self.line1 = Line2D([], [], color='blue')
self.line1_tail = Line2D([], [], color='red', linewidth=2)
self.line1_head = Line2D([], [], color='red', marker='o', markeredgecolor='r')
self.ax1.add_line(self.line1)
self.ax1.add_line(self.line1_tail)
self.ax1.add_line(self.line1_head)
self.ax1.set_xlim(0, self.xlim - 1)
self.ax1.set_ylim(0, 100)
FigureCanvas.__init__(self, self.fig)
TimedAnimation.__init__(self, self.fig, interval = 50, blit = True)
return
def new_frame_seq(self):
return iter(range(self.n.size))
def _init_draw(self):
lines = [self.line1, self.line1_tail, self.line1_head]
for l in lines:
l.set_data([], [])
return
def addData(self, value):
self.addedData.append(value)
return
def zoomIn(self, value):
bottom = self.ax1.get_ylim()[0]
top = self.ax1.get_ylim()[1]
bottom += value
top -= value
self.ax1.set_ylim(bottom,top)
self.draw()
return
def _step(self, *args):
# Extends the _step() method for the TimedAnimation class.
try:
TimedAnimation._step(self, *args)
except Exception as e:
self.abc += 1
print(str(self.abc))
TimedAnimation._stop(self)
pass
return
def _draw_frame(self, framedata):
margin = 2
while(len(self.addedData) > 0):
self.y = np.roll(self.y, -1)
self.y[-1] = self.addedData[0]
del(self.addedData[0])
self.line1.set_data(self.n[ 0 : self.n.size - margin ], self.y[ 0 : self.n.size - margin ])
self.line1_tail.set_data(np.append(self.n[-10:-1 - margin], self.n[-1 - margin]), np.append(self.y[-10:-1 - margin], self.y[-1 - margin]))
self.line1_head.set_data(self.n[-1 - margin], self.y[-1 - margin])
self._drawn_artists = [self.line1, self.line1_tail, self.line1_head]
return
''' End Class '''
# You need to setup a signal slot mechanism, to
# send data to your GUI in a thread-safe way.
# Believe me, if you don't do this right, things
# go very very wrong..
class Communicate(QObject):
data_signal = pyqtSignal(float)
''' End Class '''
def dataSendLoop(addData_callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.data_signal.connect(addData_callbackFunc)
# Simulate some data
n = np.linspace(0, 499, 500)
y = 50 + 25*(np.sin(n / 8.3)) + 10*(np.sin(n / 7.5)) - 5*(np.sin(n / 1.5))
i = 0
while(True):
if(i > 499):
i = 0
time.sleep(0.1)
mySrc.data_signal.emit(y[i]) # <- Here you emit a signal!
i += 1
###
###
if __name__== '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
Just try it out. Copy-paste this code in a new python-file, and run it. You should get a beautiful, smoothly moving graph:
The top (and many other) answers were built upon plt.pause(), but that was an old way of animating the plot in matplotlib. It is not only slow, but also causes focus to be grabbed upon each update (I had a hard time stopping the plotting python process).
TL;DR: you may want to use matplotlib.animation (as mentioned in documentation).
After digging around various answers and pieces of code, this in fact proved to be a smooth way of drawing incoming data infinitely for me.
Here is my code for a quick start. It plots current time with a random number in [0, 100) every 200ms infinitely, while also handling auto rescaling of the view:
from datetime import datetime
from matplotlib import pyplot
from matplotlib.animation import FuncAnimation
from random import randrange
x_data, y_data = [], []
figure = pyplot.figure()
line, = pyplot.plot_date(x_data, y_data, '-')
def update(frame):
x_data.append(datetime.now())
y_data.append(randrange(0, 100))
line.set_data(x_data, y_data)
figure.gca().relim()
figure.gca().autoscale_view()
return line,
animation = FuncAnimation(figure, update, interval=200)
pyplot.show()
You can also explore blit for even better performance as in FuncAnimation documentation.
An example from the blit documentation:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
xdata, ydata = [], []
ln, = plt.plot([], [], 'ro')
def init():
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
return ln,
def update(frame):
xdata.append(frame)
ydata.append(np.sin(frame))
ln.set_data(xdata, ydata)
return ln,
ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
init_func=init, blit=True)
plt.show()
None of the methods worked for me.
But I have found this
Real time matplotlib plot is not working while still in a loop
All you need is to add
plt.pause(0.0001)
and then you could see the new plots.
So your code should look like this, and it will work
import matplotlib.pyplot as plt
import numpy as np
plt.ion() ## Note this correction
fig=plt.figure()
plt.axis([0,1000,0,1])
i=0
x=list()
y=list()
while i <1000:
temp_y=np.random.random();
x.append(i);
y.append(temp_y);
plt.scatter(i,temp_y);
i+=1;
plt.show()
plt.pause(0.0001) #Note this correction
show is probably not the best choice for this. What I would do is use pyplot.draw() instead. You also might want to include a small time delay (e.g., time.sleep(0.05)) in the loop so that you can see the plots happening. If I make these changes to your example it works for me and I see each point appearing one at a time.
I know this question is old, but there's now a package available called drawnow on GitHub as "python-drawnow". This provides an interface similar to MATLAB's drawnow -- you can easily update a figure.
An example for your use case:
import matplotlib.pyplot as plt
from drawnow import drawnow
def make_fig():
plt.scatter(x, y) # I think you meant this
plt.ion() # enable interactivity
fig = plt.figure() # make a figure
x = list()
y = list()
for i in range(1000):
temp_y = np.random.random()
x.append(i)
y.append(temp_y) # or any arbitrary update to your figure's data
i += 1
drawnow(make_fig)
python-drawnow is a thin wrapper around plt.draw but provides the ability to confirm (or debug) after figure display.
Another option is to go with bokeh. IMO, it is a good alternative at least for real-time plots. Here is a bokeh version of the code in the question:
from bokeh.plotting import curdoc, figure
import random
import time
def update():
global i
temp_y = random.random()
r.data_source.stream({'x': [i], 'y': [temp_y]})
i += 1
i = 0
p = figure()
r = p.circle([], [])
curdoc().add_root(p)
curdoc().add_periodic_callback(update, 100)
and for running it:
pip3 install bokeh
bokeh serve --show test.py
bokeh shows the result in a web browser via websocket communications. It is especially useful when data is generated by remote headless server processes.
An example use-case to plot CPU usage in real-time.
import time
import psutil
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
i = 0
x, y = [], []
while True:
x.append(i)
y.append(psutil.cpu_percent())
ax.plot(x, y, color='b')
fig.canvas.draw()
ax.set_xlim(left=max(0, i - 50), right=i + 50)
fig.show()
plt.pause(0.05)
i += 1
The problem seems to be that you expect plt.show() to show the window and then to return. It does not do that. The program will stop at that point and only resume once you close the window. You should be able to test that: If you close the window and then another window should pop up.
To resolve that problem just call plt.show() once after your loop. Then you get the complete plot. (But not a 'real-time plotting')
You can try setting the keyword-argument block like this: plt.show(block=False) once at the beginning and then use .draw() to update.
Here is a version that I got to work on my system.
import matplotlib.pyplot as plt
from drawnow import drawnow
import numpy as np
def makeFig():
plt.scatter(xList,yList) # I think you meant this
plt.ion() # enable interactivity
fig=plt.figure() # make a figure
xList=list()
yList=list()
for i in np.arange(50):
y=np.random.random()
xList.append(i)
yList.append(y)
drawnow(makeFig)
#makeFig() The drawnow(makeFig) command can be replaced
#plt.draw() with makeFig(); plt.draw()
plt.pause(0.001)
The drawnow(makeFig) line can be replaced with a makeFig(); plt.draw() sequence and it still works OK.
If you want draw and not freeze your thread as more point are drawn you should use plt.pause() not time.sleep()
im using the following code to plot a series of xy coordinates.
import matplotlib.pyplot as plt
import math
pi = 3.14159
fig, ax = plt.subplots()
x = []
y = []
def PointsInCircum(r,n=20):
circle = [(math.cos(2*pi/n*x)*r,math.sin(2*pi/n*x)*r) for x in xrange(0,n+1)]
return circle
circle_list = PointsInCircum(3, 50)
for t in range(len(circle_list)):
if t == 0:
points, = ax.plot(x, y, marker='o', linestyle='--')
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)
else:
x_coord, y_coord = circle_list.pop()
x.append(x_coord)
y.append(y_coord)
points.set_data(x, y)
plt.pause(0.01)
This is the right way to plot Dynamic real-time matplot plots animation using while loop
There is a medium article on that too:
pip install celluloid # this will capture the image/animation
import matplotlib.pyplot as plt
import numpy as np
from celluloid import Camera # getting the camera
import matplotlib.animation as animation
from IPython import display
import time
from IPython.display import HTML
import warnings
%matplotlib notebook
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')
fig = plt.figure() #Empty fig object
ax = fig.add_subplot() #Empty axis object
camera = Camera(fig) # Camera object to capture the snap
def f(x):
''' function to create a sine wave'''
return np.sin(x) + np.random.normal(scale=0.1, size=len(x))
l = []
while True:
value = np.random.randint(9) #random number generator
l.append(value) # appneds each time number is generated
X = np.linspace(10, len(l)) # creates a line space for x axis, Equal to the length of l
for i in range(10): #plots 10 such lines
plt.plot(X, f(X))
fig.show() #shows the figure object
fig.canvas.draw()
camera.snap() # camera object to capture teh animation
time.sleep(1)
And for saving etc:
animation = camera.animate(interval = 200, repeat = True, repeat_delay = 500)
HTML(animation.to_html5_video())
animation.save('abc.mp4') # to save
output is:
Live plot with circular buffer with line style retained:
import os
import time
import psutil
import collections
import matplotlib.pyplot as plt
pts_n = 100
x = collections.deque(maxlen=pts_n)
y = collections.deque(maxlen=pts_n)
(line, ) = plt.plot(x, y, linestyle="--")
my_process = psutil.Process(os.getpid())
t_start = time.time()
while True:
x.append(time.time() - t_start)
y.append(my_process.cpu_percent())
line.set_xdata(x)
line.set_ydata(y)
plt.gca().relim()
plt.gca().autoscale_view()
plt.pause(0.1)

Categories

Resources