Python Forum
Non-blocking real-time plotting
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Non-blocking real-time plotting
#1
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.
Reply
#2
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.
Reply
#3
"""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()
Gribouillis likes this post
Reply
#4
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.
Reply
#5
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.
Reply
#6
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)) 
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [SOLVED] Why is this asyncio task blocking? SecureCoop 1 784 Jun-06-2023, 02:43 PM
Last Post: SecureCoop
  Plotting by Time, error mansoorahs 1 740 May-16-2023, 09:46 AM
Last Post: Larz60+
  Make code non-blocking? Extra 0 1,144 Dec-03-2022, 10:07 PM
Last Post: Extra
  Real time database satyanarayana 3 1,674 Feb-16-2022, 01:37 PM
Last Post: buran
  Real time data satyanarayana 3 24,488 Feb-16-2022, 07:46 AM
Last Post: satyanarayana
  Real time Detection and Display Gilush 0 1,796 Feb-05-2022, 08:28 PM
Last Post: Gilush
  time setup for realtime plotting of serial datas at high sampling rate alice93 6 3,765 Jan-07-2022, 05:41 PM
Last Post: deanhystad
  Real-Time output of server script on a client script. throwaway34 2 2,065 Oct-03-2021, 09:37 AM
Last Post: ibreeden
  Real Time Audio Processing with Python Sound-Device not working Slartybartfast 2 4,003 Mar-14-2021, 07:20 PM
Last Post: Slartybartfast
  Plotting A Time Series With Shaded Recession Bars adamszymanski 1 3,171 Jan-24-2021, 09:08 PM
Last Post: nealc

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020