import sys import random import importlib import matplotlib import matplotlib.pyplot as plts import csv import xml.etree.ElementTree as ET # Creating/parsing XML file matplotlib.use('Qt5Agg') from PyQt5 import QtCore, QtGui, QtWidgets from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar import matplotlib.pyplot as plts ############################################################################### # twin_plot_qt.py # @title: Visualisation des données (pyQt5+Matplotlib) # @project: Blender-EduTech # @lang: fr # @authors: Philippe Roy # @copyright: Copyright (C) 2023 Philippe Roy # @license: GNU GPL ############################################################################### # Lecture des configurations twin_config = ET.parse('twin_config.xml').getroot() plot_config_tree = ET.parse('plot_config.xml').getroot() plot_config={} ############################################################################### # Configuration des plots ############################################################################### ## # Génère le dictionnaire plot_config à partir du fichier XML plot_config.xml ## def plot_config_dict(): for var in plot_config_tree[0][0]: var_dict={} for i in range (len(var)): if var[i] is not None: var_dict.update({var[i].tag : var[i].text}) plot_config.update({var.text: var_dict}) ## # Returne une valeur du dictionnaire plot_config ## def plot_config_get(var, key): if key in plot_config[var]: return plot_config[var][key] else: return None ## # Nombre de groupe de courbe(s) ## def plot_nb(): plot_config_list=list(plot_config) nbgroup = 0 # Regroupement ('group' > 0) for var in plot_config_list: if ('group' in plot_config[var]): if nbgroup < int(plot_config[var]['group']): nbgroup = int(plot_config[var]['group']) # Plot solitaire ('group' = 0) # 'group' = -1 -> variable non affichée for var in plot_config_list: if ('group' in plot_config[var]): if int(plot_config[var]['group']) == 0: nbgroup +=1 return nbgroup ############################################################################### # Zone de dessin ############################################################################### class MplCanvas(FigureCanvasQTAgg): def __init__(self, parent=None, width=5, height=4, dpi=100): if plot_nb() ==1: # plot_nb() : nombre de graphiques fig, self.plt = plts.subplots() # plot_nb() : nombre de graphiques else: fig, self.plt = plts.subplots(plot_nb(), 1, sharex=True) # plot_nb() : nombre de graphiques super(MplCanvas, self).__init__(fig) ############################################################################### # Graphique dynamique ############################################################################### class DynamicPlot(QtWidgets.QMainWindow): # Création du graphique def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.canvas = MplCanvas(self, width=5, height=4, dpi=100) self.setCentralWidget(self.canvas) n_data = 50 # self.xdata = list(range(n_data)) # self.ydata = [random.randint(0, 10) for i in range(n_data)] self.xdata = [0] self.ydata = [0] self.update_plot() self.show() # Timer pour le update_plot fps = 120 # Blender est cadencé à 60 fps self.timer = QtCore.QTimer() # self.timer.setInterval(int(1000/fps)) self.timer.setInterval(1) self.timer.timeout.connect(self.update_plot) self.timer.start() print("Qt : Canvas ready\n") # Lecture des données et mise à jour du graphique def update_plot(self): plt = self.canvas.subplot # Données msg="" for line in sys.stdin: msg = line.rstrip() break print("Qt : ", msg) # Essai new_x=self.xdata[len(self.xdata)-1]+1 self.xdata.append(new_x) self.ydata.append(random.randint(0, 10)) # Lecture du Pipe simple # msg="" # print (sys.stdin) # contents = sys.stdin.read(1) # # contents = sys.stdin.buffer # print ("contents :", contents) # for line in sys.stdin: # msg = line.rstrip() # break # print(msg) # X et Y # FIXME : temps et une valeur # msg_list=msg.split(',') # print(msg_list) # if msg_list[0] != "": # self.xdata = self.xdata + [float(msg_list[0])] # self.ydata = self.ydata + [float(msg_list[1])] # # Lecture du Pipe # # i=0 # # lines = sys.stdin.readlines() # # print (lines) # # print ("b") # msg_line="" # # msg_lines=[] # for line in sys.stdin: # # i+=1 # # print (i) # msg_line = line.rstrip() # # msg_lines.append(msg_line) # # print("Qt msg_lines :", msg) # # if i==2: # # break # break # print("Qt :", msg_line) # # print("Qt msg_lines :", msg_lines) # # X et Y # # FIXME : temps msg_list[0] et une seule valeur msg_list[1] # msg_list=msg_line.split(',') # # print(msg_list) # if msg_list[0] != "": # self.xdata = self.xdata + [float(msg_list[0])] # self.ydata = self.ydata + [float(msg_list[1])] # for line in msg_lines: # msg_list=line.split(',') # # print(msg_list) # if msg_list[0] != "": # self.xdata = self.xdata + [float(msg_list[0])] # self.ydata = self.ydata + [float(msg_list[1])] # for line in sys.stdin: # msg_list=msg.split(',') # print(msg_list) # if msg_list[0] != "": # self.xdata = self.xdata + [float(msg_list[0])] # self.ydata = self.ydata + [float(msg_list[1])] # self.ydata = self.ydata + [random.randint(0, 10)] # Drop off the first y element, append a new one. # self.ydata = self.ydata[1:] + [random.randint(0, 10)] # Redraw plt.cla() plt.plot(self.xdata, self.ydata, 'r') self.canvas.draw() ############################################################################### # Graphique statique ############################################################################### class MainWindow(QtWidgets.QMainWindow): # Création du graphique def __init__(self, *args, **kwargs): # Configuration des plots plot_config_dict() # Zone graphique (barre d'outil et graphique) # FIXME : perd de la place quand on agrandi super(MainWindow, self).__init__(*args, **kwargs) self.canvas = MplCanvas(self, width=5, height=4, dpi=100) # print (self.canvas.getContentsMargins()) toolbar = NavigationToolbar(self.canvas, self) # print (toolbar.getContentsMargins()) # Implantation de la fenêtre layout = QtWidgets.QVBoxLayout() # print (layout.getContentsMargins()) # layout.setContentsMargins(0,0,0,0) layout.addWidget(toolbar) layout.addWidget(self.canvas) widget = QtWidgets.QWidget() # print (widget.getContentsMargins()) widget.setLayout(layout) self.setCentralWidget(widget) # print (self.getContentsMargins()) # Lecture fichier CSV fields = [] rows = [] with open(sys.argv[1], newline='') as csv_buff: csv_reader = csv.reader(csv_buff, delimiter=';') fields = next(csv_reader) for row in csv_reader: rows.append(row) # Mise en tableau à deux colonnes (xdata,ydata) xdata=[] ydata=[] i=0 for field in fields: xdata_row=[] ydata_row=[] for row in rows: xdata_row.append(float(row[0].replace(',', '.'))) # Revenir au format US des décimaux ydata_row.append(float(row[i].replace(',', '.'))) # Revenir au format US des décimaux xdata.append(xdata_row) ydata.append(ydata_row) i+=1 # Plots # Groupe de plots : si group = 0 -> nouveau groupe, plot solitaire # si group = -1 -> pas de plot # si group > 0 -> numéro du groupe plt_i=0 # Compteur de plot plt_grp=[] # Groupe de plot [[numéro du plot, groupe du fichier CSV]] for i in range(len(fields)): var = fields[i] plt_current=-1 # Numéro du plot à créer if ('group' in plot_config[var]): # Pas de Plot if int(plot_config[var]['group']) !=-1: # Pas de Plot # Plot solitaire if int(plot_config[var]['group']) ==0: plt_current = plt_i plt_grp.append([plt_i, 0]) plt_i +=1 # Plot groupé else: plt_new = True # Flag d'un nouveau groupe for j in range(len(plt_grp)): if plt_grp[j][1] == int(plot_config[var]['group']): # Groupe déjà existant plt_current = plt_grp[j][0] plt_new = False break # Nouveau groupe if plt_new: plt_current = plt_i plt_grp.append([plt_i, int(plot_config[var]['group'])]) plt_i +=1 # Création du plot unique if plot_nb() ==1: if twin_config[1][0].text == "True": # Configuration des plots activée self.canvas.plt.plot(xdata[i], ydata[i], label=var, color=plot_config_get(var, 'color'), linewidth=plot_config_get(var, 'linewidth'), linestyle=plot_config_get(var, 'linestyle'), marker=plot_config_get(var, 'marker')) else: self.canvas.plt.plot(xdata[i], ydata[i], '.-', label=var) # Configuration matplotlib par défaut # Légende ou titre d'axe y if plt_grp[plt_current][1]==0: self.canvas.plt.set_ylabel(var) else: self.canvas.plt.legend() # Création des subplots if plot_nb() >1: if twin_config[1][0].text == "True": # Configuration des plots activée self.canvas.plt[plt_current].plot(xdata[i], ydata[i], label=var, color=plot_config_get(var, 'color'), linewidth=plot_config_get(var, 'linewidth'), linestyle=plot_config_get(var, 'linestyle'), marker=plot_config_get(var, 'marker')) else: self.canvas.plt[plt_current].plot(xdata[i], ydata[i], '.-', label=var) # Configuration matplotlib par défaut # Légende ou titre d'axe y if plt_grp[plt_current][1]==0: self.canvas.plt[plt_current].set_ylabel(var) else: self.canvas.plt[plt_current].legend() # Décoration if plot_nb() ==1: # 1 plot self.canvas.plt.set_xlabel("Temps (s)") # self.canvas.plt[0].set_ylabel("Valeurs") self.canvas.plt.set_title(sys.argv[1]) self.canvas.plt.axhline(linewidth=1, color='k') self.canvas.plt.grid(True, linestyle='--') # self.canvas.plt.legend() else: # Plusieurs plots self.canvas.plt[plt_i-1].set_xlabel("Temps (s)") # self.canvas.plt[0].set_ylabel("Valeurs") self.canvas.plt[0].set_title(sys.argv[1]) for i in range (plt_i): self.canvas.plt[i].axhline(linewidth=1, color='k') self.canvas.plt[i].grid(True, linestyle='--') # self.canvas.plt[i].legend() # Redraw self.canvas.draw() self.show() ############################################################################### # Application ############################################################################### if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) # w = DynamicPlot() w = MainWindow() # StaticPlot() app.exec_() sys.exit(0)