from datetime import datetime import numpy as np import matplotlib import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import os, json, traceback, ui, wx matplotlib.use("WXAgg") # for JetBrains IDE to force use the wx backend class SerialPlotter: def __init__(self) -> None: self.settings = json.load(open('settings.json', 'r')) self.sensors = len(self.settings['sensor_ports']) self.windowsize = self.settings['winSize'] self.delay = self.settings["delay"] / 1000 self.colors = ['blue', 'orange', 'green', 'yellow'] self.fig, self.axs = plt.subplots(1, 1, figsize=(7,5)) # TODO: make the figure size an UI option and pass into the settings.json self.fig.canvas.mpl_connect('close_event', self._close_event) self.timeStamps = {} self.sensorsData = {} for i in range(self.sensors): self.timeStamps[i] = ['']#*self.windowsize self.sensorsData[i] = [0]#*self.windowsize plt.tight_layout() def _close_event(self, event) -> None: """ Actiions need to be executed when the graph has closed. Start a new .csv file to get read for new graph and bring back the UI, if hidden """ file = self.settings["file_name"] wx.MessageBox(f"File has saved as {os.path.split(file)[1]} under {os.path.split(file)[0]} directory!\n") def animation(self, t:int) -> None: """ render a frame of the animated graph """ try: plt.cla() # clear previous frame # read the last line from the .csv file, the data start from the second colunm so omit index # 0 file = open(self.settings["file_name"], "r") ndata = np.array([np.asarray(line.split(", ")[1:], dtype=np.float32) for line in file]) if len(ndata) > 0: row = ndata[-1] i = 0 while i < self.sensors: # shift all data left by 1 index, pop out the leftmost value if self.windowsize > 0 and len(self.timeStamps[i]) > self.windowsize: # TODO: make sure the two lists have the same size self.timeStamps[i].pop(0) self.sensorsData[i].pop(0) self.timeStamps[i].append(datetime.now().strftime('%H:%M:%S')) self.sensorsData[i].append(row[i]) # plot a line # TODO: round the number to 1-2- decimal places self.axs.plot(self.timeStamps[i] ,self.sensorsData[i], color=self.colors[i], label=f'sensor {i + 1}, latest: {np.int32(self.sensorsData[i][-1])}') i += 1 # Acknowledgement: https://stackoverflow.com/a/13589144 handles, labels = self.axs.get_legend_handles_labels() by_label = dict(zip(labels, handles)) self.axs.legend(by_label.values(), by_label.keys(), loc='best') except: traceback.print_exc() def plotting(self) -> FuncAnimation: ani = FuncAnimation(self.fig, self.animation, blit=False) return ani