Embedding a live updating matplotlib graph in wxpython - python

I am a newbie into wx python. The following is the code to plot live graph from a text file which can be updated live. Can anybody please help me to embed this code into a wx frame. I desperately need it for my project.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
fig= plt.figure()
ax1=fig.add_subplot(1,1,1)
def animate(i):
pullData= open('C:/test/e.txt','r').read()
dataArray= pullData.split('\n')
xar=[]
yar=[]
for eachLine in dataArray:
if len(eachLine)>1:
x,y= eachLine.split(',')
xar.append(int(x))
yar.append(int(y))
ax1.clear()
ax1.plot(xar,yar)
ani= animation.FuncAnimation(fig,animate, interval=1000)
plt.show()

Here I'll give you an example but you need to change the plotting part for your needs:
import wx
import numpy as np
import matplotlib.figure as mfigure
import matplotlib.animation as manim
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
class MyFrame(wx.Frame):
def __init__(self):
super(MyFrame,self).__init__(None, wx.ID_ANY, size=(800, 600))
self.fig = mfigure.Figure()
self.ax = self.fig.add_subplot(111)
self.canv = FigureCanvasWxAgg(self, wx.ID_ANY, self.fig)
self.values = []
self.animator = manim.FuncAnimation(self.fig,self.anim, interval=1000)
def anim(self,i):
if i%10 == 0:
self.values = []
else:
self.values.append(np.random.rand())
self.ax.clear()
self.ax.set_xlim([0,10])
self.ax.set_ylim([0,1])
return self.ax.plot(np.arange(1,i%10+1),self.values,'d-')
wxa = wx.PySimpleApp()
w = MyFrame()
w.Show(True)
wxa.MainLoop()

Related

Matplotlib save animaiton as video but get empty content

I am trying to make an animation with continue rotating an image, but the output video file has empty content(Only axis left), how to fix it?
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import scipy.misc
from scipy import ndimage
my_image="img.png"
out_file="myvideo.mp4"
class UpdateDist:
def __init__(self, ax):
self.ax = ax
self.img = mpimg.imread(my_image)
self.ax.imshow(self.img)
self.degree = 1
def __call__(self, i):
rotated_img = ndimage.rotate(img, self.degree*10)
self.ax.imshow(rotated_img)
self.degree += 1
return self.ax,
plt.axis(False)
plt.grid(False)
fig, ax = plt.subplots()
ud = UpdateDist(ax)
anim = FuncAnimation(fig, ud, frames=100, interval=10, blit=True)
plt.show()
ani.save(out_file, fps=30, extra_args=['-vcodec', 'libx264'])
I applied some edits to your code:
replaced self.degree with i: i increases by 1 in each iteration, no need for another counter
moved ax.grid(False) and ax.axis(False) (and added ax.clear()) within __call__ method, in order to use them in each frame
removed blit parameter from FuncAnimation
replaced .mp4 output file format with .gif
used imagemagik as writer
Let me know if this code achieves your goal or if you need any further modifications.
Complete Code
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy import ndimage
import numpy as np
my_image='img.png'
out_file='myvideo.gif'
class UpdateDist:
def __init__(self, ax, rotational_speed):
self.ax = ax
self.img = plt.imread(my_image)
self.rotational_speed = rotational_speed
def __call__(self, i):
rotated_img = ndimage.rotate(self.img, self.rotational_speed*i, reshape=False)
self.ax.clear()
self.ax.grid(False)
self.ax.axis(False)
self.ax.imshow((rotated_img*255).astype(np.uint8))
return self.ax,
fig, ax = plt.subplots()
ud = UpdateDist(ax = ax, rotational_speed = 1)
anim = FuncAnimation(fig, ud, frames = 91, interval = 1)
anim.save(filename = out_file, writer = 'pillow', fps = 30)
Animation

Python qt5 matplotlb canvas animation with manual blit

So I'd like to integrate a matplotlib canvas in qt5 with manual blit.
I've found this thread:
Fast Live Plotting in Matplotlib / PyPlot
and the voted answer seems pretty nice however I need it in a qt5 window...
So I have tried to mash the code above together with the matplotlib qt5 tutorial into one script. https://matplotlib.org/gallery/user_interfaces/embedding_in_qt5_sgskip.html
It kinda works, however the animation only works when using the pan/zoom and the background is black :D and if blit is set to false it doesnt even draw...
If somebody could help me that would be amazing :) Its hilariously broken
from __future__ import unicode_literals
import random
import time
import matplotlib
from PyQt5.QtWidgets import QSizePolicy, QApplication, QWidget, QVBoxLayout
from matplotlib import pyplot as plt
import sys
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib.animation import FuncAnimation
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
class MyMplCanvas(FigureCanvas):
# Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.).
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = plt.figure()
FigureCanvas.__init__(self, self.fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.x = np.linspace(0, 50., num=100)
self.X, self.Y = np.meshgrid(self.x, self.x)
# self.fig = plt.figure()
self.ax1 = self.fig.add_subplot(2, 1, 1)
self.ax2 = self.fig.add_subplot(2, 1, 2)
self.img = self.ax1.imshow(self.X, vmin=-1, vmax=1, interpolation="None", cmap="RdBu")
self.line, = self.ax2.plot([], lw=3)
self.text = self.ax2.text(0.8, 0.5, "")
self.ax2.set_xlim(self.x.min(), self.x.max())
self.ax2.set_ylim([-1.1, 1.1])
self.t_start = time.time()
self.k = 0.
#self.fig.canvas.draw() # note that the first draw comes before setting data
#self.update(blit=False)
anim = FuncAnimation(self.fig, self.update, interval=20)
def update(self, blit=True):
if blit:
# cache the background
self.axbackground = self.fig.canvas.copy_from_bbox(self.ax1.bbox)
self.ax2background = self.fig.canvas.copy_from_bbox(self.ax2.bbox)
self.img.set_data(np.sin(self.X / 3. + self.k) * np.cos(self.Y / 3. + self.k))
self.line.set_data(self.x, np.sin(self.x / 3. + self.k))
self.k += 0.11
if blit:
# restore background
self.fig.canvas.restore_region(self.axbackground)
self.fig.canvas.restore_region(self.ax2background)
# redraw just the points
self.ax1.draw_artist(self.img)
self.ax2.draw_artist(self.line)
self.ax2.draw_artist(self.text)
# fill in the axes rectangle
self.fig.canvas.blit(self.ax1.bbox)
self.fig.canvas.blit(self.ax2.bbox)
# in this post http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
# it is mentionned that blit causes strong memory leakage.
# however, I did not observe that.
else:
# redraw everything
self.fig.canvas.draw()
# self.fig.canvas.flush_events()
# alternatively you could use
# plt.pause(0.000000000001)
# however plt.pause calls canvas.draw(), as can be read here:
# http://bastibe.de/2013-05-30-speeding-up-matplotlib.html
class PlotDialog(QWidget):
def __init__(self):
QWidget.__init__(self)
self.plot_layout = QVBoxLayout(self)
self.plot_canvas = MyMplCanvas(self, width=5, height=4, dpi=100)
self.navi_toolbar = NavigationToolbar(self.plot_canvas, self)
self.plot_layout.addWidget(self.plot_canvas)
self.plot_layout.addWidget(self.navi_toolbar)
if __name__ == "__main__":
app = QApplication(sys.argv)
dialog0 = PlotDialog()
dialog0.show()
sys.exit(app.exec_())

Dynamically update multiple axis in matplotlib

I want to display sensor data on a PyQT GUI with a matplotlib animation.
I already have a working Plot which gets updates every time I receive new sensor value from an external source with this code:
def __init__(self):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = []
self.ydata = []
self.entry_limit = 50
self.line, = self.axes.plot([0], [0], 'r')
def update_figure_with_new_value(self, xval: float, yval: float):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
I want now to extend the plot to show another data series with the same x-axis. I tried to achieve this with the following additions to the init-code above:
self.axes2 = self.axes.twinx()
self.y2data = []
self.line2, = self.axes2.plot([0], [0], 'b')
and in the update_figure_with_new_value() function (for test purpose I just tried to add 1 to yval, I will extend the params of the function later):
self.y2data.append(yval+1)
if len(self.y2data) > self.entry_limit:
self.y2data.pop(0)
self.line2.set_data(self.xdata, self.ydata)
self.axes2.relim()
self.axes2.autoscale_view()
But instead of getting two lines in the plot which should have the exact same movement but just shifted by one I get vertical lines for the second plot axis (blue). The first axis (red) remains unchanged and is ok.
How can I use matplotlib to update multiple axis so that they display the right values?
I'm using python 3.4.0 with matplotlib 2.0.0.
Since there is no minimal example available, it's hard to tell the reason for this undesired behaviour. In principle ax.relim() and ax.autoscale_view() should do what you need.
So here is a complete example which works fine and updates both scales when being run with python 2.7, matplotlib 2.0 and PyQt4:
import numpy as np
import matplotlib.pyplot as plt
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtGui.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = Figure(figsize=(5,4), dpi=100)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = [0]
self.ydata = [0]
self.entry_limit = 50
self.line, = self.axes.plot([], [], 'r', lw=3)
self.axes2 = self.axes.twinx()
self.y2data = [0]
self.line2, = self.axes2.plot([], [], 'b')
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.canvas)
self.show()
self.ctimer = QtCore.QTimer()
self.ctimer.timeout.connect(self.update)
self.ctimer.start(150)
def update(self):
y = np.random.rand(1)
self.update_figure_with_new_value(self.xdata[-1]+1,y)
def update_figure_with_new_value(self, xval,yval):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.y2data.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.y2data.append(yval+np.random.rand(1)*0.17)
self.line2.set_data(self.xdata, self.y2data)
self.axes2.relim()
self.axes2.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
if __name__ == "__main__":
qapp = QtGui.QApplication([])
a = Window()
exit(qapp.exec_())
You may want to test this and report back if it is working or not.

make a button for animation in matplotlib

I just made a simple gui using Qt Designer, the gui has 4 buttons and a widget. The widget will show the animation and the buttons are for pause animation,resume, clean the canvas and start animation. I made this code:
import sys
from PyQt4 import QtGui, uic
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
def start():
def datos(t=0):
while True:
t += 0.1
yield t, np.cos(t)
def init():
ax.set_ylim(-1, 1)
ax.set_xlim(0, 5)
def run(data):
t,y = data
xdata.append(t)
ydata.append(y)
line.set_data(xdata, ydata)
xmin,xmax =ax.get_xlim()
if t > xmax:
ax.set_xlim(xmin, 1.5*xmax)
ax.figure.canvas.draw()
ani = animation.FuncAnimation(fig, run, datos, blit=False, interval=50,
repeat=False, init_func=init)
def stop():
ani.event_source.stop()
def borr():
plt.clf()
canvas.draw()
def anim():
ani.event_source.start()
window.resume.clicked.connect(anim)
window.pause.clicked.connect(stop)
window.clean.clicked.connect(borr)
return ani
layout=QtGui.QVBoxLayout()
fig=plt.figure()
canvas=FigureCanvas(fig)
layout.addWidget(canvas)
ax = fig.add_subplot(111)
line,=ax.plot([],[],lw=2)
ax.grid()
xdata, ydata = [], []
app = QtGui.QApplication(sys.argv)
window = uic.loadUi("animacion.ui")
window.start.clicked.connect(start)
window.widget.setLayout(layout)
window.show()
sys.exit(app.exec_())
this shows the grid, but when I press the start button it doesnt show the animation
I also made this code:
import sys
from PyQt4 import QtCore, QtGui, uic
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
def datos(t=0):
while True:
t += 0.1
yield t, np.cos(t)
def init():
ax.set_ylim(-1, 1)
ax.set_xlim(0, 5)
def run(data):
t,y = data
xdata.append(t)
ydata.append(y)
line.set_data(xdata, ydata)
xmin,xmax =ax.get_xlim()
if t > xmax:
ax.set_xlim(xmin, 1.5*xmax)
ax.figure.canvas.draw()
def start():
window.widget.setLayout(layout)
def stop():
ani.event_source.stop()
def borr():
plt.clf()
canvas.draw()
def anim():
ani.event_source.start()
layout=QtGui.QVBoxLayout()
fig=plt.figure('test')
canvas=FigureCanvas(fig)
layout.addWidget(canvas)
ax = fig.add_subplot(111)
line,=ax.plot([],[],lw=2)
ax.grid()
xdata, ydata = [], []
app = QtGui.QApplication(sys.argv)
window = uic.loadUi("animacion.ui")
window.resume.clicked.connect(anim)
window.pause.clicked.connect(stop)
window.clean.clicked.connect(borr)
window.start.clicked.connect(start)
ani = animation.FuncAnimation(fig, run, datos, blit=False, interval=50,
repeat=False, init_func=init)
window.show()
sys.exit(app.exec_())
In this case, when I press start the animation begins, I can pause and resume. But when a clean the canvas an press start again it doesnt show the function.
How can I make it works?
thanks!
Try to provide minimal working examples. Without animacion.ui we cannot run you code.
Refering to the second code: The problem here seems to be that inside borr() you clear the figure (plt.clf()). If the figure is cleared, where should the animation be drawn to?
I solved the problem making a function with the animation
import sys
from PyQt4 import QtGui, uic
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
def start():
def datos(t=0):
while True:
t += 0.1
yield t, np.cos(t)
def init():
ax.set_ylim(-1, 1)
ax.set_xlim(0, 5)
def run(data):
t,y = data
xdata.append(t)
ydata.append(y)
line.set_data(xdata, ydata)
xmin,xmax =ax.get_xlim()
if t > xmax:
ax.set_xlim(xmin, 1.5*xmax)
ax.figure.canvas.draw()
def stop():
ani.event_source.stop()
def borr():
plt.clf()
canvas.draw()
def anim():
ani.event_source.start()
window.resume.clicked.connect(anim)
window.pause.clicked.connect(stop)
window.clean.clicked.connect(borr)
ax = fig.add_subplot(111)
line,=ax.plot([],[],lw=2)
ax.grid()
xdata, ydata = [], []
ani = animation.FuncAnimation(fig, run, datos, blit=False, interval=50,
repeat=False, init_func=init)
canvas.draw()
layout=QtGui.QVBoxLayout()
fig=plt.figure()
canvas=FigureCanvas(fig)
layout.addWidget(canvas)
app = QtGui.QApplication(sys.argv)
window = uic.loadUi("animacion.ui")
window.start.clicked.connect(start)
window.widget.setLayout(layout)
window.show()
sys.exit(app.exec_())

applying the matplotlib draw() freezes window solution to special case

Being new to python, I've come upon the matplotlib draw() freezes window problem myself and found the solution on this site:
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import random
import numpy as np
import sys
import Tkinter as tk
import time
def function1(fig, ax):
ax.cla()
color_grade_classes = ['#80FF00','#FFFF00','#FF8000', '#FF0000']
varsi = random.randint(1, 100)
for colors, rows in zip(color_grade_classes, [3,2,1,0] ):
indexs = np.arange(5)
heights = [varsi,varsi/2,varsi/3,0,0]
ax.bar(indexs, heights, zs = rows, zdir='y', color=colors, alpha=0.8)
return fig
class App():
def __init__(self):
self.root = tk.Tk()
self.root.wm_title("Embedding in TK")
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111, projection='3d')
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
self.fig = function1(self.fig, self.ax)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
self.toolbar = NavigationToolbar2TkAgg( self.canvas, self.root )
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.label = tk.Label(text="")
self.label.pack()
self.update_clock()
self.root.mainloop()
def update_clock(self):
self.fig = function1(self.fig,self.ax)
self.canvas.show()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.root.after(1000, self.update_clock)
app=App()
My problem is incorporating the following plotting code into it. It's not quite the same as the example given. Not sure how to split this up between the function definition and the class declaration. Can anyone help me on this?
t0 = time.time()
while time.time() - t0 <= 10:
data = np.random.random((32, 32))
plt.clf()
im = plt.imshow(data,cmap=cm.gist_gray, interpolation='none')
plt.ion()
cbar = plt.colorbar(im)
cbar.update_normal(im)
cbar.set_clim(0, np.amax(data))
plt.draw()
time.sleep(0.5)
plt.show(block=True)
this seems to work.
Basically the __init__ part initialises the plot and draws the first "frame". Then the function self.update_clockis called every 1000ms, and that function calls function1() which generates new data and redraws the plot.
I moved things around a bit because of the colorbar in your example, but the idea remains the same.
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import random
import numpy as np
import sys
import Tkinter as tk
import time
class App():
def __init__(self):
self.root = tk.Tk()
self.root.wm_title("Embedding in TK")
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111)
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
data = np.random.random((32, 32))
im = self.ax.imshow(data,cmap=cm.gist_gray, interpolation='none')
self.cbar = self.fig.colorbar(im)
self.cbar.update_normal(im)
self.cbar.set_clim(0, np.amax(data))
self.fig = self.function1(self.fig, self.ax)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
self.toolbar = NavigationToolbar2TkAgg( self.canvas, self.root )
self.toolbar.update()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.label = tk.Label(text="")
self.label.pack()
self.update_clock()
self.root.mainloop()
def update_clock(self):
self.fig = self.function1(self.fig,self.ax)
self.canvas.show()
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
now = time.strftime("%H:%M:%S")
self.label.configure(text=now)
self.root.after(1000, self.update_clock)
def function1(self, fig, ax):
ax.cla()
data = np.random.random((32, 32))
im = ax.imshow(data,cmap=cm.gist_gray, interpolation='none')
self.cbar.update_normal(im)
self.cbar.set_clim(0, np.amax(data))
return fig
app=App()

Categories

Resources