2023-01-15 16:56:46 +01:00
|
|
|
import sys
|
|
|
|
import random
|
2023-01-25 06:03:02 +01:00
|
|
|
import importlib
|
2023-01-15 16:56:46 +01:00
|
|
|
import matplotlib
|
2023-01-25 06:57:05 +01:00
|
|
|
import matplotlib.pyplot as plts
|
2023-01-25 06:03:02 +01:00
|
|
|
import csv
|
|
|
|
import xml.etree.ElementTree as ET # Creating/parsing XML file
|
|
|
|
|
2023-01-15 16:56:46 +01:00
|
|
|
matplotlib.use('Qt5Agg')
|
|
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
|
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, NavigationToolbar2QT as NavigationToolbar
|
2023-01-25 06:57:05 +01:00
|
|
|
import matplotlib.pyplot as plts
|
2023-01-15 16:56:46 +01:00
|
|
|
|
|
|
|
###############################################################################
|
2023-01-21 10:39:24 +01:00
|
|
|
# twin_plot_qt.py
|
2023-01-27 21:26:33 +01:00
|
|
|
# @title: Visualisation des données (pyQt5+Matplotlib)
|
2023-01-15 16:56:46 +01:00
|
|
|
# @project: Blender-EduTech
|
|
|
|
# @lang: fr
|
|
|
|
# @authors: Philippe Roy <philippe.roy@ac-grenoble.fr>
|
|
|
|
# @copyright: Copyright (C) 2023 Philippe Roy
|
|
|
|
# @license: GNU GPL
|
|
|
|
###############################################################################
|
|
|
|
|
2023-01-25 06:03:02 +01:00
|
|
|
# Lecture des configurations
|
|
|
|
twin_config = ET.parse('twin_config.xml').getroot()
|
|
|
|
plot_config_tree = ET.parse('plot_config.xml').getroot()
|
|
|
|
plot_config={}
|
2023-01-15 16:56:46 +01:00
|
|
|
|
2023-01-25 06:03:02 +01:00
|
|
|
###############################################################################
|
|
|
|
# Configuration des plots
|
|
|
|
###############################################################################
|
|
|
|
|
2023-01-28 23:06:55 +01:00
|
|
|
##
|
2023-01-25 22:38:26 +01:00
|
|
|
# Génère le dictionnaire plot_config à partir du fichier XML plot_config.xml
|
2023-01-28 23:06:55 +01:00
|
|
|
##
|
|
|
|
|
2023-01-25 06:03:02 +01:00
|
|
|
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})
|
2023-01-15 16:56:46 +01:00
|
|
|
|
2023-01-28 23:06:55 +01:00
|
|
|
##
|
|
|
|
# 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
|
|
|
|
|
|
|
|
##
|
2023-01-25 22:38:26 +01:00
|
|
|
# Nombre de groupe de courbe(s)
|
2023-01-28 23:06:55 +01:00
|
|
|
##
|
|
|
|
|
2023-01-25 22:38:26 +01:00
|
|
|
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
|
|
|
|
|
2023-01-15 16:56:46 +01:00
|
|
|
###############################################################################
|
2023-01-21 10:39:24 +01:00
|
|
|
# Zone de dessin
|
2023-01-15 16:56:46 +01:00
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
class MplCanvas(FigureCanvasQTAgg):
|
|
|
|
def __init__(self, parent=None, width=5, height=4, dpi=100):
|
2023-01-28 23:06:55 +01:00
|
|
|
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
|
2023-01-15 16:56:46 +01:00
|
|
|
super(MplCanvas, self).__init__(fig)
|
|
|
|
|
2023-01-21 10:39:24 +01:00
|
|
|
###############################################################################
|
|
|
|
# Graphique dynamique
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
class DynamicPlot(QtWidgets.QMainWindow):
|
2023-01-15 16:56:46 +01:00
|
|
|
|
|
|
|
# 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
|
2023-01-17 01:24:21 +01:00
|
|
|
fps = 120 # Blender est cadencé à 60 fps
|
2023-01-15 16:56:46 +01:00
|
|
|
self.timer = QtCore.QTimer()
|
2023-01-17 01:24:21 +01:00
|
|
|
# self.timer.setInterval(int(1000/fps))
|
|
|
|
self.timer.setInterval(1)
|
2023-01-15 16:56:46 +01:00
|
|
|
self.timer.timeout.connect(self.update_plot)
|
|
|
|
self.timer.start()
|
2023-01-17 01:24:21 +01:00
|
|
|
print("Qt : Canvas ready\n")
|
2023-01-15 16:56:46 +01:00
|
|
|
|
|
|
|
# Lecture des données et mise à jour du graphique
|
|
|
|
def update_plot(self):
|
|
|
|
plt = self.canvas.subplot
|
|
|
|
|
|
|
|
# Données
|
2023-01-18 06:06:15 +01:00
|
|
|
msg=""
|
|
|
|
for line in sys.stdin:
|
|
|
|
msg = line.rstrip()
|
|
|
|
break
|
|
|
|
print("Qt : ", msg)
|
2023-01-15 16:56:46 +01:00
|
|
|
|
2023-01-17 01:24:21 +01:00
|
|
|
# Essai
|
2023-01-18 06:06:15 +01:00
|
|
|
new_x=self.xdata[len(self.xdata)-1]+1
|
|
|
|
self.xdata.append(new_x)
|
|
|
|
self.ydata.append(random.randint(0, 10))
|
2023-01-15 16:56:46 +01:00
|
|
|
|
2023-01-17 01:24:21 +01:00
|
|
|
# Lecture du Pipe simple
|
2023-01-18 06:06:15 +01:00
|
|
|
# msg=""
|
2023-01-17 01:24:21 +01:00
|
|
|
# print (sys.stdin)
|
|
|
|
# contents = sys.stdin.read(1)
|
|
|
|
# # contents = sys.stdin.buffer
|
|
|
|
# print ("contents :", contents)
|
|
|
|
|
2023-01-18 06:06:15 +01:00
|
|
|
# for line in sys.stdin:
|
|
|
|
# msg = line.rstrip()
|
|
|
|
# break
|
|
|
|
# print(msg)
|
2023-01-15 16:56:46 +01:00
|
|
|
|
|
|
|
# X et Y
|
|
|
|
# FIXME : temps et une valeur
|
2023-01-17 01:24:21 +01:00
|
|
|
# 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])]
|
2023-01-25 06:03:02 +01:00
|
|
|
|
2023-01-17 01:24:21 +01:00
|
|
|
|
|
|
|
# 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])]
|
|
|
|
|
2023-01-15 16:56:46 +01:00
|
|
|
# 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()
|
|
|
|
|
2023-01-21 10:39:24 +01:00
|
|
|
###############################################################################
|
|
|
|
# Graphique statique
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
class MainWindow(QtWidgets.QMainWindow):
|
|
|
|
|
|
|
|
# Création du graphique
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
2023-01-25 22:38:26 +01:00
|
|
|
# Configuration des plots
|
|
|
|
plot_config_dict()
|
|
|
|
|
2023-01-21 10:39:24 +01:00
|
|
|
# Zone graphique (barre d'outil et graphique)
|
2023-01-25 22:38:26 +01:00
|
|
|
# FIXME : perd de la place quand on agrandi
|
2023-01-21 10:39:24 +01:00
|
|
|
super(MainWindow, self).__init__(*args, **kwargs)
|
|
|
|
self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
|
2023-01-25 22:38:26 +01:00
|
|
|
# print (self.canvas.getContentsMargins())
|
2023-01-21 10:39:24 +01:00
|
|
|
toolbar = NavigationToolbar(self.canvas, self)
|
2023-01-25 22:38:26 +01:00
|
|
|
# print (toolbar.getContentsMargins())
|
2023-01-25 06:57:05 +01:00
|
|
|
|
2023-01-25 06:03:02 +01:00
|
|
|
# Implantation de la fenêtre
|
2023-01-21 10:39:24 +01:00
|
|
|
layout = QtWidgets.QVBoxLayout()
|
2023-01-25 22:38:26 +01:00
|
|
|
# print (layout.getContentsMargins())
|
|
|
|
# layout.setContentsMargins(0,0,0,0)
|
2023-01-21 10:39:24 +01:00
|
|
|
layout.addWidget(toolbar)
|
|
|
|
layout.addWidget(self.canvas)
|
|
|
|
widget = QtWidgets.QWidget()
|
2023-01-25 22:38:26 +01:00
|
|
|
# print (widget.getContentsMargins())
|
2023-01-21 10:39:24 +01:00
|
|
|
widget.setLayout(layout)
|
|
|
|
self.setCentralWidget(widget)
|
2023-01-25 22:38:26 +01:00
|
|
|
# print (self.getContentsMargins())
|
2023-01-21 10:39:24 +01:00
|
|
|
|
2023-01-25 06:03:02 +01:00
|
|
|
# Lecture fichier CSV
|
|
|
|
fields = []
|
|
|
|
rows = []
|
2023-01-28 17:24:21 +01:00
|
|
|
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:
|
2023-01-25 06:03:02 +01:00
|
|
|
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
|
2023-01-28 23:06:55 +01:00
|
|
|
# Groupe de plots : si group = 0 -> nouveau groupe, plot solitaire
|
|
|
|
# si group = -1 -> pas de plot
|
|
|
|
# si group > 0 -> numéro du groupe
|
2023-01-25 22:38:26 +01:00
|
|
|
plt_i=0 # Compteur de plot
|
2023-01-28 23:06:55 +01:00
|
|
|
plt_grp=[] # Groupe de plot [[numéro du plot, groupe du fichier CSV]]
|
2023-01-25 06:03:02 +01:00
|
|
|
for i in range(len(fields)):
|
2023-01-25 22:38:26 +01:00
|
|
|
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
|
2023-01-25 23:37:55 +01:00
|
|
|
plt_current = plt_grp[j][0]
|
2023-01-25 22:38:26 +01:00
|
|
|
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
|
|
|
|
|
2023-01-28 23:06:55 +01:00
|
|
|
# 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()
|
2023-01-25 06:03:02 +01:00
|
|
|
|
|
|
|
# Décoration
|
2023-01-28 23:06:55 +01:00
|
|
|
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()
|
2023-01-21 10:39:24 +01:00
|
|
|
|
|
|
|
# Redraw
|
|
|
|
self.canvas.draw()
|
|
|
|
self.show()
|
|
|
|
|
2023-01-15 16:56:46 +01:00
|
|
|
###############################################################################
|
|
|
|
# Application
|
|
|
|
###############################################################################
|
|
|
|
|
|
|
|
app = QtWidgets.QApplication(sys.argv)
|
2023-01-21 10:39:24 +01:00
|
|
|
# w = DynamicPlot()
|
|
|
|
w = MainWindow() # StaticPlot()
|
2023-01-15 16:56:46 +01:00
|
|
|
app.exec_()
|
2023-01-28 01:37:28 +01:00
|
|
|
sys.exit(0)
|