7

I'm using matplotlib plotting in python GUI using animation. And below is the code

import sys
from PyQt4 import QtGui

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
class Window(QtGui.QDialog):
    def __init__(self, parent=None):
    super(Window, self).__init__(parent)
    self.figure = plt.figure()
    self.canvas = FigureCanvas(self.figure)
    self.toolbar = NavigationToolbar(self.canvas, self)
    layout = QtGui.QVBoxLayout()
    layout.addWidget(self.toolbar)
    layout.addWidget(self.canvas)
    self.setLayout(layout)
    self.ax=self.figure.add_subplot(111)

    plt.autoscale(enable=True, axis='both', tight=None #for auto scaling

    self.data = [500, -500, 501, -502,.... 623] #some list of data
    self.ax = plt.gca()
    self.ax.grid()
    self.sc = self.ax.scatter(self.data[::2], self.data[1::2]

def plot(self, a):
    for i in range(len(self.data)):
        self.data[i] = int(self.data[i])+5
    self.sc.set_offsets(np.c_[self.data[::2], self.data[1::2]])
    self.canvas.draw()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)

    main = Window()
    ani = matplotlib.animation.FuncAnimation(main.figure, main.plot, 
            frames=4, interval=100, repeat=True) 
    main.show()

    sys.exit(app.exec_())

I'm updating the plot using set_offsets inside the plot function which is called by animation. When plot values is kept increasing and plotting is done, the plot goes out of figure. So i used autoscale(). But its not working. Still axes remains fixed and plots goes out of view. Kindly help me to do autoscale in figure. Thanks in advance.

Anand
  • 343
  • 1
  • 5
  • 18

1 Answers1

13

The problem is that the scatter offsets are not taken into account when the axes are autoscaled. This may be a bug, or a desired feature; in any case two workarounds would be:

using plot

One workaround which may in many cases be acceptable is to use a line plot plt.plot instead of plt.scatter. In this case the axes can be autoscaled using ax.relim followed by ax.autoscale_view().

import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np

fig, ax = plt.subplots()  
ax.grid()  

data = np.cumsum(np.random.normal(size=100)) #some list of data
sc, = ax.plot(data[::2], data[1::2], marker="o", ls="") # set linestyle to none

def plot(a, data):
    data += np.cumsum(np.random.normal(size=100)+3e-2)
    sc.set_data(data[::2], data[1::2])
    ax.relim()
    ax.autoscale_view(True,True,True)

ani = matplotlib.animation.FuncAnimation(fig, plot, fargs=(data,),
            frames=4, interval=100, repeat=True) 
plt.show()

using scatter and set limits programmatically

If the above cannot be used (e.g. because the scatter points should have different size or color), one would need to update the limits depending on the data.

import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np

fig, ax = plt.subplots()    

data = np.cumsum(np.random.normal(size=100)) #some list of data

ax.grid()
sc = ax.scatter(data[::2], data[1::2], c=data[1::2])

def plot(a, data):
    data += np.cumsum(np.random.normal(size=100)+3e-2)
    X = np.c_[data[::2], data[1::2]]
    sc.set_offsets(X)
    # manually relim:
    xmin=X[:,0].min(); xmax=X[:,0].max()
    ymin=X[:,1].min(); ymax=X[:,1].max()
    ax.set_xlim(xmin-0.1*(xmax-xmin),xmax+0.1*(xmax-xmin))
    ax.set_ylim(ymin-0.1*(ymax-ymin),ymax+0.1*(ymax-ymin))

ani = matplotlib.animation.FuncAnimation(fig, plot, fargs=(data,),
            frames=4, interval=100, repeat=True) 
plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • When I tried this approach I got a traceback 'AttributeError: 'PathCollection' object has no attribute 'set_xlim''. Any thoughts why please? – DavidA Jul 13 '17 at 16:58
  • No. First, there are two approaches above. It's not clear which one you took. Second, both are working as they are. If you changed anything, how should anyone know what you changed. Therefore, there is no way one can help you, unless you provide a [mcve] of the issue. Best do that by asking a new question about it. – ImportanceOfBeingErnest Jul 13 '17 at 17:09
  • Thanks for your reply. I was using the second method. I wrongly called set_xlim on the PathCollection instead of the axes object. It's working now. – DavidA Jul 14 '17 at 08:06
  • Thanks for the posting. – Cloud Cho Mar 08 '18 at 03:52
  • Is it possible to add an updating trend line as well as the scatter? – Ohm Apr 23 '18 at 12:57
  • @Ohm Yes it is! If you have a problem doing this, ask a question with a clear problem description ([ask]) and a [mcve] of the issue. – ImportanceOfBeingErnest Apr 23 '18 at 13:04
  • Note that `sc.set_offsets()` doesn't work for 3D plots. It does nothing. No new points appear. Use `sc._offsets3d = (...)`, e.g. [here](https://stackoverflow.com/questions/41602588/matplotlib-3d-scatter-animations). – jozxyqk Dec 13 '21 at 10:17