Search code examples
pythonnumpymultiprocessingstreaming

How to use funcAnimation while using Multiprocessing in python 3


hope you're all doing great. I am quite new in Python and am working on a tiny client- server project, where I am receiving data from the client and the goal is that the server plot this data in graphic form in real time. This is the code from the server part, which I am having struggles right now.

    import socket
    import sys
    import math
    import numpy as np
    import struct
    import time
    import os
    import ctypes as c
    import multiprocessing
    import matplotlib.pyplot as plt
    from matplotlib import animation
    from matplotlib import style
    
    HOST = '127.0.0.1'
    PORT = 6543
    receive_size = 4096
    
    
    
    def run_server(shared_data_time, shared_data_signal):   
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:  
            server.bind((HOST, PORT))
            server.listen()
            conn, addr = server.accept() 
            with conn:
                print(f"Connected by {addr}")
                while True:
                    data = conn.recv(receive_size)
                    if len(data) == 4096:                     
                        payload  = np.frombuffer(data, dtype = 'float64')
                        print(payload)
                        print('received data')
                        deinterleaved = [payload[idx::2] for idx in range(2)] 
                        shared_data_time = deinterleaved[0]   
                        shared_data_signal = deinterleaved[1]
                        print(f'received {len(data)} bytes')  
                        
    
    
    
    
    
    
    if __name__ == '__main__':
        HOST = '127.0.0.1'
        PORT = 6543
        receive_size = 4096
        
       
        shared_data_time = multiprocessing.Array('f', 2048)
        shared_data_signal = multiprocessing.Array('f', 2048)
        process1 = multiprocessing.Process(target = run_server, args =(shared_data_time, shared_data_signal))
        process1.start()
        
    
        def animate(i, shared_data_time, shared_data_signal):
            ax1.clear()
            ax1.plot(shared_data_time, shared_data_signal)
    
       
        style.use('fivethirtyeight')
        fig = plt.figure()
        ax1 = fig.add_subplot(1,1,1)
        ani = animation.FuncAnimation(fig, animate, fargs = (shared_data_time, shared_data_signal), interval = 100) 
        plt.show() 
    

The communication between server and client works but I am only getting am empty graph, with no actualization. Could everyone helpe me? I would really appreciate it.

Thanks


Solution

  • without having access to the server you connect to, it's difficult to determine the exact problem, but please see this example I made to animate data coming from a child process via shared multiprocessing.Array's:

    import multiprocessing as mp
    import matplotlib.pyplot as plt
    from matplotlib.animation import FuncAnimation
    import numpy as np
    from time import sleep, time
    
    
    def server(arr_x, arr_y):
        #tie shared buffers to numpy arrays
        x = np.frombuffer(arr_x.get_obj(), dtype='f4')
        y = np.frombuffer(arr_y.get_obj(), dtype='f4')
        T = time()
        while True:
            t = time() - T #elapsed time
            #we should technically lock access while we're writing to the array,
            #  but mistakes in the data are not real important if it's just a gui.
            x[:] = np.linspace(t, t + np.pi*2, len(x)) #update data in shared array
            y[:] = np.sin(x)
            sleep(1/30) #data updating faster or slower than animation framerate is not a big issue...
    
    
    if __name__ == "__main__":
        
        fig = plt.figure()
        ax = plt.subplot()
        
        #init data
        arr_x = mp.Array('f', 1000) #type "d" == np.dtype("f8") 
        arr_y = mp.Array('f', 1000)
        
        #tie shared buffers to numpy arrays
        x = np.frombuffer(arr_x.get_obj(), dtype='f4')
        y = np.frombuffer(arr_y.get_obj(), dtype='f4')
        
        #calculate initial value
        x[:] = np.linspace(0, np.pi*2, len(x))
        y[:] = np.sin(x)
        
        #daemon process to update values (server)
        mp.Process(target=server, args=(arr_x, arr_y), daemon=True).start()
        
        #plot initial data because we need a line instance to update continually
        line = ax.plot(x, y)[0]
        #fps counting vars
        last_second = time()
        last_frame = 0
        
        def animate(frame, x, y):
            global last_second, last_frame
            #might be cleaner to wrap animate and use nonlocal rather than global, 
            #  but it would be more complicated for just this simple example.
            
            #set data with most recent values
            line.set_data(x, y)
            line.axes.set_xlim(x[0], x[999])
            
            #periodically report fps
            interval = time() - last_second
            if  interval >= 1:
                print("fps: ", (frame-last_frame) / interval)
                last_second = time()
                last_frame = frame
        
        ani = FuncAnimation(fig, animate, fargs=(x, y), interval = 20)
        plt.show()
    
    

    FPS counting can obviously be removed easily, and converting shared arrays to numpy arrays isn't strictly necessary, but I find it easier to work with, and it is not difficult.