Python Forum
Non-blocking real-time plotting - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: General Coding Help (https://python-forum.io/forum-8.html)
+--- Thread: Non-blocking real-time plotting (/thread-39130.html)



Non-blocking real-time plotting - slow_rider - Jan-06-2023

I'm running code with several threads that update readings from 32 sensors and 1 that writes a log file. I would like to plot all that data in real-time. So far I had no luck getting matplotlib working. It either does not graph anything or blocks my other threads from running.

Any tips and suggestions would be highly appreciated.


RE: Non-blocking real-time plotting - Larz60+ - Jan-06-2023

Try a simple example (from the matplotlib docs):
see: Simple example
import matplotlib.pyplot as plt
import numpy as np

plt.style.use('_mpl-gallery')

# make data
x = np.linspace(0, 10, 100)
y = 4 + 2 * np.sin(2 * x)

# plot
fig, ax = plt.subplots()

ax.plot(x, y, linewidth=2.0)

ax.set(xlim=(0, 8), xticks=np.arange(1, 8),
       ylim=(0, 8), yticks=np.arange(1, 8))

plt.show()
can you get that to work?
if not, show all error massages(tracebacks) unaltered and complete.


RE: Non-blocking real-time plotting - deanhystad - Jan-07-2023

"""Plot real-time data using matplotlib and tkinter"""
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time
import math


def SignalSource(amplitude=1, frequency=1):
    """Simulate some signal source that returns an xy data point"""
    start_time = time.time()
    twopi = 2 * math.pi
    period = 1 / frequency
    yield (0, math.sin(0) * amplitude)
    while True:
        x = time.time() - start_time
        phase = x / period * twopi
        yield (x, math.sin(phase) * amplitude)


class PlotApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.signal = SignalSource(frequency=0.5)
        self.xdata = []
        self.ydata = []

        # Create a plot that can be embedded in a tkinter window.
        self.figure = Figure(figsize=(6, 4))
        self.plt = self.figure.add_subplot(111)
        canvas = FigureCanvasTkAgg(self.figure, self)
        canvas.draw()
        canvas.get_tk_widget().pack()
        self.update_plot()

    def update_plot(self):
        """Get new signal data and update plot.  Called periodically"""
        x, y = next(self.signal)
        self.xdata.append(x)
        self.ydata.append(y)
        if len(self.xdata) > 50:
            # Throw away old signal data
            self.xdata.pop(0)
            self.ydata.pop(0)
        # Refresh plot with new signal data.  Clear the plot so it will rescale to the new xy data.
        self.plt.clear()
        self.plt.margins(x=0)
        self.plt.plot(self.xdata, self.ydata)
        self.figure.canvas.draw()
        self.after(10, self.update_plot)


PlotApp().mainloop()



RE: Non-blocking real-time plotting - Gribouillis - Jan-07-2023

Interesting example, however there is a built-in mechanism in matplotlib to create better animation, namely matplotlib.animation.FuncAnimation(). There is a simple example resembling yours here. I wonder if this can be embedded into tkinter.


RE: Non-blocking real-time plotting - deanhystad - Jan-07-2023

This is non-blocking, as requested. Animation is blocking. But blocking should not be a problem since the plot can run in its own thread. Animation is also kind of limiting. The application should be able to plot data when desired, not when the animation decides it is time to plot.


RE: Non-blocking real-time plotting - woooee - Jan-07-2023

You might want to try using a multiprocessing Manager list or dictionary. You can write to it in a process or processes, and can plot from it in another process. This is a simple program that uses a Manager list. It does not plot in real time, but the list creation and use is the same. One of the docs on the web https://pymotw.com/3/multiprocessing/

import random
import time
from multiprocessing import Process, Manager

def add_em(process_num, add_list, mgr_list):
    total=0
    for num in add_list:
         total += num

    mgr_list.append([process_num, total, add_list])

manager = Manager()
mgr_list = manager.list()

processes_list=[]
## start 10 processes
for ctr in range(10):
    ## how many numbers to add up
    length_nums=random.randint(2, 11)
    add_list=[]
    for num in range(length_nums):
        add_list.append(random.randint(1, 100))
    p=Process(target=add_em, args=(ctr, add_list, mgr_list))
    p.start()
    processes_list.append(p)

print("waiting for processes to finish")
for p in processes_list:
    if p.is_alive():
        print("     ", p.name, p.pid, p.is_alive())
        time.sleep(0.5)

print("\nAll processes finished")
import pprint
pprint.pprint(list(mgr_list))