python matplotlib: plotting in another process

Viewed 5561

EDIT: The ultimate requirement for such a Python program is: Receive data from UART from a external circuitry (which probably is equipped with some sensors), the program will process these data, and draw a dynamically updated curve on the computer screen.

So, I want to plot dynamically, the following test script starts a sub-process, and in that process, it accepts data from parent process through a Queue, and plot data accordingly.

But when the script is run, only an empty figure is shown, I can see the console prints "put:" and "got:" messages, meaning both parent and subprocess are running and communicating, but nothing happens in the GUI figure window.

Furthermore, the GUI window is not responsive and if I click on the window, it will crash.

The system is Windows 10, 64 bit. Python version is 2.7 (32bit)

What's the problem here? thank you!

import matplotlib.pyplot as plt
import multiprocessing as mp
import random
import numpy
import time

def worker(q):
    plt.ion()
    ln, = plt.plot([], [])
    plt.show()

    while True:
        obj = q.get()
        n = obj + 0
        print "sub : got:", n

        ln.set_xdata(numpy.append(ln.get_xdata(), n))
        ln.set_ydata(numpy.append(ln.get_ydata(), n))
        plt.draw()

if __name__ == '__main__':
    queue = mp.Queue()
    p = mp.Process(target=worker, args=(queue,))
    p.start()

    while True:
        n = random.random() * 5
        print "main: put:", n
        queue.put(n)
        time.sleep(1.0)
3 Answers

Made samll modifications to above code. I used Process, Queue, Value, Array in multiprocessing package to reduce the code complexity. Script will plot [0,0], [1,1], [2,2] etc on a graph

from multiprocessing import Process, Queue, Value, Array
import time
import matplotlib.pyplot as plt
from queue import Empty

def f(q, num, arr1, arr2):
    plt.ion()
    fig = plt.figure()
    fig = plt.figure(1)
    ax = fig.add_subplot(111)
    ax.margins(0.05, 0.05)

    #ax.set_autoscale_on(True)
    ax.autoscale(enable=True, axis='both')
    #ax.autoscale(enable=True, axis='y')
    ax.set_ylim(0, 100)
    line1, line2 = ax.plot([], [], 'b-', [], [], 'r-')


    while True :
        try:
            #val = q.get_nowait()
            val = q.get(timeout = 0.8) # reducing the timeout value will improve the response time on graph whie mouse is moved on it
            #val = q.get()
            if val == 'Exit':
                break
            if val == 'Go': 
                x = num.value
                #print("num.value = ", x)
                v1 = arr1[0:num.value]
                v2 = arr2[0:num.value]
                #print("v1 :", v1)
                #print("v2 :", v2)
                line1.set_xdata(range(1, len(v1) + 1, 1))
                line1.set_ydata(v1)
                line2.set_xdata(line1.get_xdata())
                line2.set_ydata(v2)

                ax.relim()
                ax.autoscale_view(tight=True, scalex=True, scaley=False)
                fig.canvas.draw()
                #print ("thread: DRAW")
                plt.pause(0.05)
        except Empty as e:
            x = num.value
            line1.set_xdata(range(1, len(v1) + 1, 1))
            line1.set_ydata(v1)
            line2.set_xdata(line1.get_xdata())
            line2.set_ydata(v2)

            ax.relim()
            ax.autoscale_view(tight=True, scalex=True, scaley=False)
            fig.canvas.draw()
            plt.pause(0.05)
            continue



if __name__ == '__main__':
    q = Queue()
    num = Value('i', 0)
    arr1 = Array('d', range(100))
    arr2 = Array('d', range(100))
    p = Process(target=f, args=(q,num, arr1, arr2, ))
    p.start()

    for i in range(10):
        arr1[i] = i
        arr2[i] = i 
        num.value = i+1
        q.put("Go")   # prints "[42, None, 'hello']"
        time.sleep(1)
    q.put("Exit")
    p.join()
Related