5

I'm trying to open up multiple plots but I ran into a few problems. When I tried to create plots using threading, python would first open a number of windows, then close all but the first.

In another question it was recommended that I use multiprocessing which I have tried. The code runs without error, it just doesn't show any plot.
I'm trying to get something very simple to work before moving on to my main project.

# Import the necessary packages and modules
import matplotlib.pyplot as plt
import numpy as np


#from threading import Thread
import multiprocessing

def plot(datax, datay, name):
    # Prepare the data
    x = datax
    y = datay**2
    # Plot the data
    plt.scatter(x, y, label=name)

    # Add a legend
    plt.legend()

    # Show the plot
    plt.show()



#plot(3,3,)
'''
for i in range(10):
    t = Thread(target=plot, args=(i,i+1,i,))
    t.start()
    '''
for i in range(2):
    p = multiprocessing.Process(target=plot, args=(i, i, i))
    p.start()

update: for some reason, multiprocessing stopped working again. I tried creating a function multiP() only to open the processes but it didn't work when I added the input('value: '). scine I can't figure out how to send data to a specific thread I'm going to save data like this: dat = [[x,y0,y1,...yn],[x,y0,y1,...yn],...] and each plot process with check the if something was added to dat.

import matplotlib.pyplot as plt
import numpy as np

import multiprocessing
#multiprocessing.freeze_support() # <- may be required on windows

def plot(datax, datay, name):
    x = datax
    y = datay**2
    plt.scatter(x, y, label=name)
    plt.legend()
    plt.show()

def multiP():
    if __name__ == "__main__":   
        for i in range(2):
            p = multiprocessing.Process(target=plot, args=(i, i, i))
            p.start()
if True:
    #input('Vlaue: ') # while commented plots are shown
    multiP()
user169808
  • 503
  • 1
  • 6
  • 27
  • What exactly is the purpose of using multiprocessing or threading? – ImportanceOfBeingErnest Apr 23 '17 at 14:06
  • Well, I basically just need to be able to plot data that I'm getting via pyusb and plot it to multiple graphs in realtime(or as close as I can get it) – user169808 Apr 23 '17 at 20:18
  • I doubt that multiprocessing as used here will be helpful for realtime plotting because you would need to communicate with the process once it's been started. – ImportanceOfBeingErnest Apr 23 '17 at 20:26
  • do you know of any other way to plot multiple graphs in real time or similar? – user169808 Apr 23 '17 at 20:57
  • I mean you can still try it, I may be wrong here. I would certainly first update them sequentially and only if this is really not fast enough think about other options. – ImportanceOfBeingErnest Apr 23 '17 at 21:03
  • how would I go about updating them? I'm having a bit of a hard time with multiprocessing to be honest. I tried multiprocessing.Queue().put(val) but I keep getting BrokenPipeError: [WinError 232] The pipe is being closed which is a bit of a pain. and if I put val='' outside the if statement and x=val inside plot() no window opens, I get no error so I'm assuming that multithreading doesn't read val and doesn't return any errors – user169808 Apr 23 '17 at 21:32
  • Possible duplicate of [python matplotlib: plotting in another process](https://stackoverflow.com/questions/36181316/python-matplotlib-plotting-in-another-process) – csandy83 Mar 18 '18 at 19:58

2 Answers2

4

The following code produces two figures as desired.

import matplotlib.pyplot as plt
import numpy as np

import multiprocessing
#multiprocessing.freeze_support() # <- may be required on windows

def plot(datax, datay, name):
    x = datax
    y = datay**2
    plt.scatter(x, y, label=name)
    plt.legend()
    plt.show()

def multiP():
    for i in range(2):
        p = multiprocessing.Process(target=plot, args=(i, i, i))
        p.start()

if __name__ == "__main__": 
    input('Value: ') 
    multiP()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
0

Taking ImportanceOfBeingErnest answer, I leave below an implementation which only shows one window and waits for the window to close, which can be very handy. Every time it is called, it displays a new window with the corresponding image (a new pocess will be started for each image). I used a lot to view images when stopped at some breakpoint during debug.

# Library packages needed
import numpy as np
import datetime
import sys
import queue
import multiprocessing

# Plot related packages
import matplotlib.pyplot as plt


def showImage(img: np.ndarray, title: str = str(datetime.datetime.today())):
    """Show an image in a new process without blocking. Usefull for debugging.

    Args:
        img (np.ndarray): Image to be shown
        title (str, optional): Title to be shown. Defaults to
    str(datetime.datetime.today()).
    """

    def plot(q, title):
        fig = plt.figure()
        fig.suptitle(title)
        try:
            q.get(True, 2.0)  # Wait a couple of seconds
        except queue.Empty:
            print('Not image received to plot...quitting')
            sys.exit()

        plt.imshow(img)
        plt.show()
        sys.exit()

    # Create a queue to share data between process
    q = multiprocessing.Queue()

    # Create and start the process
    proc = multiprocessing.Process(None, plot, args=(q, title))
    proc.start()
    q.put(img)

To run it, just save this to a show_image.py file and call

from show_image.py import showImage
show_image(img, title)
Hugo
  • 31
  • 5
  • I encountered an error: `AttributeError: Can't pickle local object 'showImage..plot'` – shz Aug 05 '21 at 08:04