3

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.

second axis of plot (blue) is wrong

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.

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I put everything in the same order as in your example, and now it's working correctly. After that I tried to undo the actions to get more information about what caused that behaviour, but now I'm no more able to reproduce this. Thanks for your code example.
Yes, that's the whole point of minimal reproducible examples. By creating one you often already solve the problem and if not, it makes it far more easy to locate the problem. You may keep that in mind for next time. ;-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.