Animating plots with the Qt backend

Hello,

I've been trying to animate some plots with the qt backend and run into a couple of problems.

Firstly,
I'd like to be able to update the axis limits in an automated fashion as the data changes size.

Secondly,
Resizing figures appears to redraw everything _but_ items with the animation flag. The is causing me problems when the animation is paused or the frames are occurring slow enough such that there is a noticable period where the my lines disappear.

I've sort of solved both of these problems but the solutions seem hackish. In particular updating the axis limits is slowing the animation by 25% and toggling the animated field using the qt events just feels like its asking for trouble.

Below is the qt animation example with my solutions. Adjust the size of the plot after the animation finishes to see the effect of the resize hack. I would be grateful if anyone could suggest some improvements.

Thanks,
Gerald.

# For detailed comments on animation and the techniqes used here, see

# the wiki entry http://www.scipy.org/Cookbook/Matplotlib/Animations

import os

import sys

#import matplotlib

#matplotlib.use('Qt4Agg')

from matplotlib.figure import Figure

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

from PyQt4 import QtCore, QtGui

ITERS = 100

import numpy as np

import time

class BlitQT(FigureCanvas):

     def __init__(self):

         FigureCanvas.__init__(self, Figure())

         self.ax = self.figure.add_subplot(111)

         #self.ax.grid()

         self.draw()

         self.old_size = self.ax.bbox.width, self.ax.bbox.height

         self.ax_background = self.copy_from_bbox(self.ax.bbox)

         self.cnt = 0

         self.x = np.arange(0,2*np.pi,0.01)

         self.sin_line, = self.ax.plot(self.x, np.sin(self.x), animated=True)

         self.cos_line, = self.ax.plot(self.x, np.cos(self.x), animated=True)

         self.draw()

         self.old_limits = self.ax.get_xlim(),self.ax.get_ylim()

         self.tstart = time.time()

         self.maintimer = self.startTimer(10)

     ## HACK for disapearing objects on resize

     def resizeEvent(self,evt):

         super(BlitQT,self).resizeEvent(evt)

         self.sin_line.set_animated(False)

     def paintEvent(self,evt):

         super(BlitQT,self).paintEvent(evt)

         self.sin_line.set_animated(True)

     def timerEvent(self, evt):

         current_size = self.ax.bbox.width, self.ax.bbox.height

         if self.old_size != current_size:

             self.old_size = current_size

             #self.ax.clear()

             #self.ax.grid()

             self.draw()

             self.ax_background = self.copy_from_bbox(self.ax.bbox)

         self.restore_region(self.ax_background)

         # update the data

         self.sin_line.set_ydata(np.sin(self.x+self.cnt/10.0)*self.cnt/100.0)

         self.cos_line.set_ydata(np.cos(self.x+self.cnt/10.0)*self.cnt/100.0)

         ## HACK for updating axis limits

         self.ax.relim()

         self.ax.autoscale_view()

         current_limits = self.ax.get_xlim(),self.ax.get_ylim()

         if self.old_limits != current_limits:

             self.old_limits = current_limits

             self.draw()

             self.ax_background = self.copy_from_bbox(self.ax.bbox)

             self.blit(self.figure.bbox)

         # just draw the animated artist

         self.ax.draw_artist(self.sin_line)

         self.ax.draw_artist(self.cos_line)

         # just redraw the axes rectangle

         self.blit(self.ax.bbox)

         if self.cnt == 0:

             # TODO: this shouldn't be necessary, but if it is excluded the

             # canvas outside the axes is not initially painted.

             self.draw()

         if self.cnt==ITERS:

             # print the timing info and quit

             print 'FPS:' , ITERS/(time.time()-self.tstart)

             #sys.exit()

             self.killTimer(self.maintimer)

         else:

             self.cnt += 1

app = QtGui.QApplication(sys.argv)

widget = BlitQT()

widget.show()

sys.exit(app.exec_())

Gerald,

I haven’t looked at your code, but I would like to point out that if you wish to experiment a little further with animations in matplotlib, there is a animation module that is in the current development branch (but has not been officially released). Maybe using it might help make your code less “hack-ish”? We would also greatly welcome any and all comments on the module before the next release of matplotlib.

Ben Root

···

On Tue, May 3, 2011 at 3:57 AM, Gerald Storer <gds@…3598…> wrote:

Hello,

I’ve been trying to animate some plots with the qt backend and run into

a couple of problems.

Firstly,

I’d like to be able to update the axis limits in an automated fashion as

the data changes size.

Secondly,

Resizing figures appears to redraw everything but items with the

animation flag. The is causing me problems when the animation is paused

or the frames are occurring slow enough such that there is a noticable

period where the my lines disappear.

I’ve sort of solved both of these problems but the solutions seem

hackish. In particular updating the axis limits is slowing the

animation by 25% and toggling the animated field using the qt events

just feels like its asking for trouble.

Below is the qt animation example with my solutions. Adjust the size of

the plot after the animation finishes to see the effect of the resize

hack. I would be grateful if anyone could suggest some improvements.

Thanks,

Gerald.

For detailed comments on animation and the techniqes used here, see

the wiki entry http://www.scipy.org/Cookbook/Matplotlib/Animations

import os

import sys

#import matplotlib

#matplotlib.use(‘Qt4Agg’)

from matplotlib.figure import Figure

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

from PyQt4 import QtCore, QtGui

ITERS = 100

import numpy as np

import time

class BlitQT(FigureCanvas):

 def __init__(self):



     FigureCanvas.__init__(self, Figure())



     [self.ax](http://self.ax) = self.figure.add_subplot(111)



     #self.ax.grid()



     self.draw()



     self.old_size = self.ax.bbox.width, self.ax.bbox.height



     self.ax_background = self.copy_from_bbox(self.ax.bbox)



     self.cnt = 0



     self.x = np.arange(0,2*np.pi,0.01)



     self.sin_line, = self.ax.plot(self.x, np.sin(self.x), animated=True)



     self.cos_line, = self.ax.plot(self.x, np.cos(self.x), animated=True)



     self.draw()



     self.old_limits = self.ax.get_xlim(),self.ax.get_ylim()



     self.tstart = time.time()



     self.maintimer = self.startTimer(10)



 ## HACK for disapearing objects on resize



 def resizeEvent(self,evt):



     super(BlitQT,self).resizeEvent(evt)



     self.sin_line.set_animated(False)



 def paintEvent(self,evt):



     super(BlitQT,self).paintEvent(evt)



     self.sin_line.set_animated(True)



 def timerEvent(self, evt):



     current_size = self.ax.bbox.width, self.ax.bbox.height



     if self.old_size != current_size:



         self.old_size = current_size



         #self.ax.clear()



         #self.ax.grid()



         self.draw()



         self.ax_background = self.copy_from_bbox(self.ax.bbox)



     self.restore_region(self.ax_background)



     # update the data



     self.sin_line.set_ydata(np.sin(self.x+self.cnt/10.0)*self.cnt/100.0)



     self.cos_line.set_ydata(np.cos(self.x+self.cnt/10.0)*self.cnt/100.0)



     ## HACK for updating axis limits



     self.ax.relim()



     self.ax.autoscale_view()



     current_limits = self.ax.get_xlim(),self.ax.get_ylim()



     if self.old_limits != current_limits:



         self.old_limits = current_limits



         self.draw()



         self.ax_background = self.copy_from_bbox(self.ax.bbox)



         self.blit(self.figure.bbox)







     # just draw the animated artist



     self.ax.draw_artist(self.sin_line)



     self.ax.draw_artist(self.cos_line)



     # just redraw the axes rectangle



     self.blit(self.ax.bbox)



     if self.cnt == 0:



         # TODO: this shouldn't be necessary, but if it is excluded the



         # canvas outside the axes is not initially painted.



         self.draw()



     if self.cnt==ITERS:



         # print the timing info and quit



         print 'FPS:' , ITERS/(time.time()-self.tstart)



         #sys.exit()



         self.killTimer(self.maintimer)



     else:



         self.cnt += 1

app = QtGui.QApplication(sys.argv)

widget = BlitQT()

widget.show()

sys.exit(app.exec_())

Ah,
I did not know that. I shall investigate.

Thanks,
Gerald.
···

  Gerald Storer | MRX Technologies

  +61 8 9227 4529 | gds@...3593...
  | [http://www.mrxtech.com.au](http://www.mrxtech.com.au)