3

I used this question and tried to use it to do fast live plotting of many subplots.

Unfortunately it is very difficult for me to understand the code and thus I have problems changing it for my needs.

I wanted to create a subplot of 2x2 matrices with 10x10 pixels. Right now I am getting the following:

enter image description here

The code looks like that:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import numpy as np
import time
import sys

class App(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(App, self).__init__(parent)

        #### Create Gui Elements ###########
        self.mainbox = QtGui.QWidget()
        self.setCentralWidget(self.mainbox)
        self.mainbox.setLayout(QtGui.QVBoxLayout())

        self.canvas = pg.GraphicsLayoutWidget()
        self.mainbox.layout().addWidget(self.canvas)

        self.label = QtGui.QLabel()
        self.mainbox.layout().addWidget(self.label)

        self.view = self.canvas.addViewBox()
        self.view.setAspectLocked(True)
        self.view.setRange(QtCore.QRectF(0, 0, 50, 50))

        self.img = []
        for i in range(4): 
            self.img.append(pg.ImageItem(None, border="w"))
            self.canvas.nextRow()
            self.view.addItem(self.img[i])

        self._update()

    def _update(self):
        for i in range(4):
            self.data = np.random.rand(10,10)
            self.img[i].setImage(self.data)

        QtCore.QTimer.singleShot(1, self._update)

def sensor_data(n_sensors, x_res, y_res):
    return np.random.rand(n_sensors, x_res, y_res)

if __name__ == '__main__':
    while True:
        # Get sensor data
        data = sensor_data(4, 10, 10)
        # Pass data to live plot function?

    app = QtGui.QApplication(sys.argv)
    thisapp = App()
    thisapp.show()
    sys.exit(app.exec_())

Can someone please show me what I'm doing wrong?

Gilfoyle
  • 3,282
  • 3
  • 47
  • 83

1 Answers1

1

You have only built a single ViewBox and in it you are adding the items and that causes the problem, what you must do is create several ViewBox and add an item as I show you next:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import numpy as np


class App(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(App, self).__init__(parent)
        self.mainbox = QtGui.QWidget()
        self.setCentralWidget(self.mainbox)

        self.canvas = pg.GraphicsLayoutWidget()
        self.label = QtGui.QLabel()

        lay = QtGui.QVBoxLayout(self.mainbox)
        lay.addWidget(self.canvas)
        lay.addWidget(self.label)

        self.img_items = []

        for i in range(4):
            view = self.canvas.addViewBox()
            view.setAspectLocked(True)
            view.setRange(QtCore.QRectF(0, 0, 10, 10))
            it = pg.ImageItem(None, border="w")
            view.addItem(it)
            self.img_items.append(it)
            self.canvas.nextRow()

        timer = QtCore.QTimer(self, interval=1)
        timer.timeout.connect(self._update)
        timer.start()

    def _update(self):
        for item in self.img_items:
            data = np.random.rand(10, 10)
            item.setImage(data)

if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    thisapp = App()
    thisapp.show()
    sys.exit(app.exec_())

enter image description here

Update:

NxN

n = 2
for i in range(n):
    for j in range(n):
        view = self.canvas.addViewBox(i, j)
        view.setAspectLocked(True)
        view.setRange(QtCore.QRectF(0, 0, 10, 10))
        it = pg.ImageItem(None, border="w")
        view.addItem(it)
        self.img_items.append(it)

enter image description here

Update:

If you want to obtain data within a while True you must do it in a new thread to avoid the GUI being blocked, you must also give a small sleep so that the GUI can be updated:

from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import numpy as np


def sensor_data(n_sensors, x_res, y_res):
    return np.random.rand(n_sensors, x_res, y_res)


class Thread(QtCore.QThread):
    dataChanged = QtCore.pyqtSignal(np.ndarray)
    def run(self):
        while True:
            data = sensor_data(4, 10, 10)
            self.dataChanged.emit(data)
            QtCore.QThread.msleep(10)


class App(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(App, self).__init__(parent)
        self.mainbox = QtGui.QWidget()
        self.setCentralWidget(self.mainbox)

        self.canvas = pg.GraphicsLayoutWidget()
        self.label = QtGui.QLabel()

        lay = QtGui.QVBoxLayout(self.mainbox)
        lay.addWidget(self.canvas)
        lay.addWidget(self.label)

        self.img_items = []
        n = 2

        for i in range(n):
            for j in range(n):
                view = self.canvas.addViewBox(i, j)
                view.setAspectLocked(True)
                view.setRange(QtCore.QRectF(0, 0, 10, 10))
                it = pg.ImageItem(None, border="w")
                view.addItem(it)
                self.img_items.append(it)

    @QtCore.pyqtSlot(np.ndarray)
    def update_data(self, data):
        for i, v in enumerate(data):
            self.img_items[i].setImage(v)


if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    thisapp = App()
    thread = Thread()
    thread.dataChanged.connect(thisapp.update_data)
    thread.start()
    thisapp.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Is it possible to place the subplots as a mosaic? In this case '2 x 2'? And in general 'n x n'? And how can I feed data from the main function to the '_update' function? – Gilfoyle Oct 28 '18 at 19:10
  • @Samuel the first point understood, the second point, do you want to send the data every how often? And how do you get the data? – eyllanesc Oct 28 '18 at 19:12
  • @Samuel the idea is that if you have a live plotting you get the data in _update, something like: `def _update(self): for item in self.img_items: data = foo_get_data(i) item.setImage(data)` – eyllanesc Oct 28 '18 at 19:18
  • I have sensor data which I save in a numpy array of type '(n_sensors, x_res, y_res)'. In the case above I had n_sensors=4 and x_res = y_res = 10. I call that data stream in the main function from where I want to pass it to the plot procedure. This happens in a while loop in the main function. I hope that helps. – Gilfoyle Oct 28 '18 at 19:21
  • @Samuel do not use while True, forget about it, that would block the GUI, show me how you get the data, that is, the function that reads the data and show you the correct way to do it. – eyllanesc Oct 28 '18 at 19:23
  • You could share how you get your numpy data – eyllanesc Oct 28 '18 at 19:31
  • I added a routine to the main function which stream data to `data`. What I finally want to do is calling the live plot procedure from another script from where I get `data`. – Gilfoyle Oct 28 '18 at 20:00