How to plot with multiprocessing?

942 views
Skip to first unread message

nullnull...@gmail.com

unread,
Jul 3, 2018, 9:45:56 AM7/3/18
to pyqtgraph
I'm trying to make a program that spawns 2 processes, one of which(```sender```) sends a random integer every second to the other process(```receiver```) and it plots a normal (Gaussian) distribution using the value.


    import multiprocessing as mp
    import time
    import random
    import os
    import numpy as np
    import pyqtgraph as pg
    from pyqtgraph.Qt import QtGui, QtCore
    from pyqtgraph.dockarea import *
    
    class PLT():
        def __init__(self):
            self.win2 = pg.GraphicsWindow(title="Basic plotting examples")
            self.win2.resize(1000,600)
            self.win2.setWindowTitle('pyqtgraph example: Plotting')
            self.p2 = self.win2.addPlot(title="Updating plot")
            self.curve = self.p2.plot(pen='y')
            
            #QtGui.QApplication.instance().exec_()   # ---> if it's in effect, the plotting window shows up, but the data exchange doesn't happen.
            
        def update(self, data):
            self.curve.setData(data)
    
    class sender(mp.Process):
        def __init__(self, pipe):
            mp.Process.__init__(self)
            self.pipe = pipe
    
        def run(self):
            print('SENDER PID: ', os.getpid() )
            while True:
                value = random.randint(0, 10)
                self.pipe.send(value)
                time.sleep(1)
    
    class receiver(mp.Process):
        def __init__(self, pipe):
            mp.Process.__init__(self)
            self.pipe = pipe
    
        def run(self):
            self.p = PLT()
            print('RECEIVER PID: ', os.getpid() )
            while True:
                integer = self.pipe.recv() 
                print(integer)  
    
                self.p.update(np.random.normal(size=(10,1000))[integer%10])
    
    if __name__ == '__main__':
        mp.freeze_support()
        print('MAIN PID: ', os.getpid() )
        
        out_pipe, in_pipe = mp.Pipe() 
        
        p1 = sender(pipe=in_pipe)
        p2 = receiver(pipe=out_pipe)
        p1.start()
        p2.start()
        
But it doesn't work as I expected. If I run it, it shows a pyqtgraph window which is blank(but I can see the data exchange). If I add ```QtGui.QApplication.instance().exec_()```, it shows the window well, but now the data exchange doesn't work. What did I wrong? I'd appreciate it if I could get some advice or help.

Patrick

unread,
Jul 4, 2018, 8:27:45 PM7/4/18
to pyqtgraph
A thought -- you need to create and start your Qt Application (the .exec() line) in the main function. Typically the program initialisation will look something like:


def main():
    import sys
    app = QtWidgets.QApplication(sys.argv)
    mainwindow = YourPlotWindow()
    # Initialise other things here, start your processes etc
    # ...
    mainwindow.show()
    # Start the QApplication. This will block until the window is closed or program is exited
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()


nullnull...@gmail.com

unread,
Jul 4, 2018, 11:43:22 PM7/4/18
to pyqtgraph
Thank you for your comment. I tried chaning the code according to the initialisation, but the same problem occurs. I referred to https://gist.github.com/Overdrivr/efea3d363556c0dcf4b6 this post, the code works well, but once I add some logic to do other jobs while plotting, it just don't work. I guess there's a loop for pyqtgraph and once it has begun, the whole process cannot get out of it.

Patrick

unread,
Jul 5, 2018, 6:06:11 AM7/5/18
to pyqtgraph
OK, I think I now understand what you're trying to do. How about something like this:

import sys, time
import numpy as np
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
from pyqtgraph import GraphicsLayoutWidget
from multiprocessing import Process, Pipe, Event

class PyQtGraphTest(GraphicsLayoutWidget):

    def __init__(self, pipe_head, pipe_tail):
        super().__init__()

        self.pipe_head = pipe_head
        self.pipe_tail = pipe_tail

        # Configure plot area
        self.setWindowTitle('Test pyqtgraph paint signals')
        self.resize(640, 400)
        self.plot = self.addPlot()
        self.spectrum = self.plot.plot()
        self.plot.enableAutoRange(pg.ViewBox.XYAxes)

        # Timer to trigger check for new data and plot updates
        self.update_timer = QTimer()
        self.update_timer.timeout.connect(self.update_plot)
        self.update_timer.start(16)

    # Check for new data in pipe and update plot
    def update_plot(self):
        if self.pipe_tail.poll():
            self.spectrum.setData(self.pipe_tail.recv())

def start_receiver(pipe_head, pipe_tail):
    app = QtWidgets.QApplication(sys.argv)
    window = PyQtGraphTest(pipe_head, pipe_tail)
    window.show()
    app.exec_()

# Routine to acquire and serve data
# This might be a camera driver, notifying when a new frame is available
def start_sender(pipe_head, pipe_tail, closing_event):
    while not closing_event.is_set():
        # Try not to block up the pipe...
        if not pipe_tail.poll():
            n = 1600
            data = np.zeros(n)
            data += np.cos(np.arange(0, 10*np.pi, 10*np.pi/n) - 9*time.monotonic())
            data += np.cos(np.arange(0, 4*np.pi, 4*np.pi/n) + 4*time.monotonic())
            pipe_head.send(data)
        time.sleep(0.01)

if __name__ == '__main__':
    # Event to signal closing of the plot window to the other process
    # (Possibly could instead pass data back the other way through the pipe)
    closing_event = Event()
    # Pipe for data transport
    pipe_head, pipe_tail = Pipe()
    print("Starting reciever...")
    receiver = Process(target=start_receiver, args=(pipe_head, pipe_tail))
    receiver.start()
    print("Starting sender...")
    sender = Process(target=start_sender, args=(pipe_head, pipe_tail, closing_event))
    sender.start()
    print("Waiting for receiver to exit...")
    receiver.join()
    print("Waiting for sender to exit...")
    closing_event.set()
    sender.join()
    print("Processes finished.")
    sys.exit(0)

Luke Campagnola

unread,
Jul 5, 2018, 11:52:58 AM7/5/18
to pyqt...@googlegroups.com
In your receiver process, you have two different loops that want to run--the while loop in receiver.run is pulling values from the pipe and plotting them, whereas the Qt event loop is actually responsible for updating the GUI and handling user input. You have to combine these two loops so they're not blocking each other--either call QApplication.processEvents() from your loop, or use a QTimer to have the Qt event loop handle incoming data.


--
You received this message because you are subscribed to the Google Groups "pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pyqtgraph+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pyqtgraph/dda62ede-6012-4ada-91f9-6f00e2c40e28%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

nullnull...@gmail.com

unread,
Jul 8, 2018, 10:45:38 PM7/8/18
to pyqtgraph
Thank you so much Patrick! It works well and I could use multiple processes thanks to your example. I tried using threading instead of QTimer, and it seems it works well too.

nullnull...@gmail.com

unread,
Jul 8, 2018, 10:46:59 PM7/8/18
to pyqtgraph
Thank you for your answer Luke. I will look processEvents up. Thank you again for making the great module!
Reply all
Reply to author
Forward
0 new messages