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)) |