PyQt Lab est un outil puissant qui permet aux utilisateurs de créer des graphiques mathématiques à partir de données reçues sur le port série. Il combine Pyqtgraph et Pyserial pour afficher des courbes avec des widgets de paramétrage des axes. Cet article expliquera en détail comment utiliser PyQt Lab pour afficher des courbes à partir de données reçues sur le port série, ainsi que les différents widgets de paramétrage des axes disponibles.
PyQt Lab’ : Graphiques Math : Pyqtgraph + Pyserial : Afficher une courbe à partir de valeurs reçues sur le port série, avec widgets de paramétrage des axes
Par X. HINAULT – Juin 2013

Ce que l’on va faire ici
- Cette interface PyQt permet d’afficher une courbe à partir de valeurs reçues sur le port série, avec widgets de paramétrage des axes.
Pré-requis
- python 2.7
- pyqt4.x
- pyqtgraph
- python-pyserial
Téléchargement :
- L’archive contenant les fichiers est disponible ici : http://ubuntuone.com/3vB0UMIAusGIyedXZy1tXs
Le fichier d’interface *.py
- Fichier obtenu automatiquement avec l’utilitaire pyuic4 à partir du fichier *.ui créé avec QtDesigner :
# Form implementation generated from reading ui file ‘
#
# Created: Fri Jun 7 15:48:24 2013
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName(_fromUtf8(« Form »))
Form.resize(691, 410)
self.graph = PlotWidget(Form)
self.graph.setGeometry(QtCore.QRect(200, 5, 480, 360))
self.graph.setObjectName(_fromUtf8(« graph »))
self.labelReception = QtGui.QLabel(Form)
self.labelReception.setGeometry(QtCore.QRect(10, 290, 171, 16))
self.labelReception.setObjectName(_fromUtf8(« labelReception »))
self.comboBoxFinLigne = QtGui.QComboBox(Form)
self.comboBoxFinLigne.setGeometry(QtCore.QRect(10, 175, 121, 24))
self.comboBoxFinLigne.setObjectName(_fromUtf8(« comboBoxFinLigne »))
self.comboBoxFinLigne.addItem(_fromUtf8(« »))
self.comboBoxFinLigne.addItem(_fromUtf8(« »))
self.comboBoxFinLigne.addItem(_fromUtf8(« »))
self.comboBoxFinLigne.addItem(_fromUtf8(« »))
self.labelPort = QtGui.QLabel(Form)
self.labelPort.setGeometry(QtCore.QRect(10, 10, 101, 16))
self.labelPort.setObjectName(_fromUtf8(« labelPort »))
self.lineEditChaineEnvoi = QtGui.QLineEdit(Form)
self.lineEditChaineEnvoi.setGeometry(QtCore.QRect(10, 135, 166, 23))
self.lineEditChaineEnvoi.setText(_fromUtf8(« »))
self.lineEditChaineEnvoi.setObjectName(_fromUtf8(« lineEditChaineEnvoi »))
self.textEditTraceEnvoiSerie = QtGui.QTextEdit(Form)
self.textEditTraceEnvoiSerie.setGeometry(QtCore.QRect(10, 220, 171, 71))
self.textEditTraceEnvoiSerie.setStyleSheet(_fromUtf8(« color: rgb(0, 0, 255);\n«
« background-color: rgb(170, 255, 255); »))
self.textEditTraceEnvoiSerie.setLineWrapMode(QtGui.QTextEdit.NoWrap)
self.textEditTraceEnvoiSerie.setObjectName(_fromUtf8(« textEditTraceEnvoiSerie »))
self.line_3 = QtGui.QFrame(Form)
self.line_3.setGeometry(QtCore.QRect(180, 10, 16, 371))
self.line_3.setFrameShape(QtGui.QFrame.VLine)
self.line_3.setFrameShadow(QtGui.QFrame.Sunken)
self.line_3.setObjectName(_fromUtf8(« line_3 »))
self.labelDelaiRecept = QtGui.QLabel(Form)
self.labelDelaiRecept.setGeometry(QtCore.QRect(10, 95, 106, 16))
self.labelDelaiRecept.setObjectName(_fromUtf8(« labelDelaiRecept »))
self.labelChaineEnvoi = QtGui.QLabel(Form)
self.labelChaineEnvoi.setGeometry(QtCore.QRect(10, 120, 121, 16))
self.labelChaineEnvoi.setObjectName(_fromUtf8(« labelChaineEnvoi »))
self.pushButtonEnvoi = QtGui.QPushButton(Form)
self.pushButtonEnvoi.setGeometry(QtCore.QRect(135, 160, 41, 41))
self.pushButtonEnvoi.setObjectName(_fromUtf8(« pushButtonEnvoi »))
self.spinBoxDelaiReception = QtGui.QSpinBox(Form)
self.spinBoxDelaiReception.setGeometry(QtCore.QRect(115, 90, 71, 23))
self.spinBoxDelaiReception.setMaximum(10000)
self.spinBoxDelaiReception.setProperty(« value », 20)
self.spinBoxDelaiReception.setObjectName(_fromUtf8(« spinBoxDelaiReception »))
self.line = QtGui.QFrame(Form)
self.line.setGeometry(QtCore.QRect(10, 110, 166, 16))
self.line.setFrameShape(QtGui.QFrame.HLine)
self.line.setFrameShadow(QtGui.QFrame.Sunken)
self.line.setObjectName(_fromUtf8(« line »))
self.labelTraceEnvoiSerie = QtGui.QLabel(Form)
self.labelTraceEnvoiSerie.setGeometry(QtCore.QRect(10, 205, 191, 16))
self.labelTraceEnvoiSerie.setObjectName(_fromUtf8(« labelTraceEnvoiSerie »))
self.pushButtonStop = QtGui.QPushButton(Form)
self.pushButtonStop.setGeometry(QtCore.QRect(135, 55, 41, 31))
self.pushButtonStop.setObjectName(_fromUtf8(« pushButtonStop »))
self.comboBoxDebit = QtGui.QComboBox(Form)
self.comboBoxDebit.setGeometry(QtCore.QRect(10, 65, 121, 24))
self.comboBoxDebit.setObjectName(_fromUtf8(« comboBoxDebit »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.comboBoxDebit.addItem(_fromUtf8(« »))
self.labelFinLigne = QtGui.QLabel(Form)
self.labelFinLigne.setGeometry(QtCore.QRect(10, 160, 71, 16))
self.labelFinLigne.setObjectName(_fromUtf8(« labelFinLigne »))
self.comboBoxPort = QtGui.QComboBox(Form)
self.comboBoxPort.setGeometry(QtCore.QRect(10, 25, 121, 24))
self.comboBoxPort.setEditable(True)
self.comboBoxPort.setObjectName(_fromUtf8(« comboBoxPort »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.comboBoxPort.addItem(_fromUtf8(« »))
self.labelDebit = QtGui.QLabel(Form)
self.labelDebit.setGeometry(QtCore.QRect(10, 50, 111, 16))
self.labelDebit.setObjectName(_fromUtf8(« labelDebit »))
self.pushButtonInitSerial = QtGui.QPushButton(Form)
self.pushButtonInitSerial.setGeometry(QtCore.QRect(135, 10, 41, 41))
self.pushButtonInitSerial.setObjectName(_fromUtf8(« pushButtonInitSerial »))
self.line_2 = QtGui.QFrame(Form)
self.line_2.setGeometry(QtCore.QRect(10, 195, 166, 16))
self.line_2.setFrameShape(QtGui.QFrame.HLine)
self.line_2.setFrameShadow(QtGui.QFrame.Sunken)
self.line_2.setObjectName(_fromUtf8(« line_2 »))
self.textEditReception = QtGui.QTextEdit(Form)
self.textEditReception.setGeometry(QtCore.QRect(10, 305, 171, 76))
self.textEditReception.setStyleSheet(_fromUtf8(« background-color: rgb(244, 255, 190); »))
self.textEditReception.setObjectName(_fromUtf8(« textEditReception »))
self.checkBoxAxisBottom = QtGui.QCheckBox(Form)
self.checkBoxAxisBottom.setGeometry(QtCore.QRect(290, 370, 82, 19))
self.checkBoxAxisBottom.setChecked(True)
self.checkBoxAxisBottom.setObjectName(_fromUtf8(« checkBoxAxisBottom »))
self.checkBoxAxisLeft = QtGui.QCheckBox(Form)
self.checkBoxAxisLeft.setGeometry(QtCore.QRect(390, 370, 82, 19))
self.checkBoxAxisLeft.setChecked(True)
self.checkBoxAxisLeft.setObjectName(_fromUtf8(« checkBoxAxisLeft »))
self.pushButtonInit = QtGui.QPushButton(Form)
self.pushButtonInit.setGeometry(QtCore.QRect(200, 375, 85, 27))
self.pushButtonInit.setObjectName(_fromUtf8(« pushButtonInit »))
self.checkBoxLabelAxisLeft = QtGui.QCheckBox(Form)
self.checkBoxLabelAxisLeft.setGeometry(QtCore.QRect(390, 390, 96, 19))
self.checkBoxLabelAxisLeft.setChecked(True)
self.checkBoxLabelAxisLeft.setObjectName(_fromUtf8(« checkBoxLabelAxisLeft »))
self.checkBoxLabelAxisBottom = QtGui.QCheckBox(Form)
self.checkBoxLabelAxisBottom.setGeometry(QtCore.QRect(290, 390, 96, 19))
self.checkBoxLabelAxisBottom.setChecked(True)
self.checkBoxLabelAxisBottom.setObjectName(_fromUtf8(« checkBoxLabelAxisBottom »))
self.retranslateUi(Form)
self.comboBoxFinLigne.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate(« Form », « PyQt + PySerial + pyqtgraph : Oscillo simple », None, QtGui.QApplication.UnicodeUTF8))
self.labelReception.setText(QtGui.QApplication.translate(« Form », « Réception sur le port série : « , None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxFinLigne.setItemText(0, QtGui.QApplication.translate(« Form », « Rien », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxFinLigne.setItemText(1, QtGui.QApplication.translate(« Form », « Saut de ligne (\\n = LF) », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxFinLigne.setItemText(2, QtGui.QApplication.translate(« Form », « Retour Chariot (\\r = CR) », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxFinLigne.setItemText(3, QtGui.QApplication.translate(« Form », « Les 2 (LF + CR) », None, QtGui.QApplication.UnicodeUTF8))
self.labelPort.setText(QtGui.QApplication.translate(« Form », « Port Série : », None, QtGui.QApplication.UnicodeUTF8))
self.labelDelaiRecept.setText(QtGui.QApplication.translate(« Form », « Délai Récept. (ms) », None, QtGui.QApplication.UnicodeUTF8))
self.labelChaineEnvoi.setText(QtGui.QApplication.translate(« Form », « Chaîne à envoyer : », None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonEnvoi.setText(QtGui.QApplication.translate(« Form », « Envoi », None, QtGui.QApplication.UnicodeUTF8))
self.labelTraceEnvoiSerie.setText(QtGui.QApplication.translate(« Form », « Envoi sur le port série : « , None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonStop.setText(QtGui.QApplication.translate(« Form », « Stop », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(0, QtGui.QApplication.translate(« Form », « 115200 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(1, QtGui.QApplication.translate(« Form », « 57600 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(2, QtGui.QApplication.translate(« Form », « 38400 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(3, QtGui.QApplication.translate(« Form », « 28800 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(4, QtGui.QApplication.translate(« Form », « 19200 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(5, QtGui.QApplication.translate(« Form », « 14400 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(6, QtGui.QApplication.translate(« Form », « 9600 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxDebit.setItemText(7, QtGui.QApplication.translate(« Form », « 4800 », None, QtGui.QApplication.UnicodeUTF8))
self.labelFinLigne.setText(QtGui.QApplication.translate(« Form », « Fin de ligne : », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(0, QtGui.QApplication.translate(« Form », « /dev/ttyACM0 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(1, QtGui.QApplication.translate(« Form », « /dev/ttyACM1 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(2, QtGui.QApplication.translate(« Form », « /dev/ttyUSB0 », None, QtGui.QApplication.UnicodeUTF8))
self.comboBoxPort.setItemText(3, QtGui.QApplication.translate(« Form », « /dev/ttyUSB1 », None, QtGui.QApplication.UnicodeUTF8))
self.labelDebit.setText(QtGui.QApplication.translate(« Form », « Débit Série (bauds) : », None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonInitSerial.setText(QtGui.QApplication.translate(« Form », « Init », None, QtGui.QApplication.UnicodeUTF8))
self.checkBoxAxisBottom.setText(QtGui.QApplication.translate(« Form », « Axe X », None, QtGui.QApplication.UnicodeUTF8))
self.checkBoxAxisLeft.setText(QtGui.QApplication.translate(« Form », « Axe Y », None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonInit.setText(QtGui.QApplication.translate(« Form », « Initialiser », None, QtGui.QApplication.UnicodeUTF8))
self.checkBoxLabelAxisLeft.setText(QtGui.QApplication.translate(« Form », « Label Axe Y », None, QtGui.QApplication.UnicodeUTF8))
self.checkBoxLabelAxisBottom.setText(QtGui.QApplication.translate(« Form », « Label Axe X », None, QtGui.QApplication.UnicodeUTF8))
from pyqtgraph import PlotWidget
if __name__ == « __main__ »:
import sys
app = QtGui.QApplication(sys.argv)
Form = QtGui.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
Le fichier d’application *Main.py
# -*- coding: utf-8 -*-
# par X. HINAULT – Mai 2013 – Tous droits réservés
# GPLv3 – www.mon-club-elec.fr
# modules a importer
from PyQt4.QtGui import *
from PyQt4.QtCore import * # inclut QTimer..
import os,sys
import pyqtgraph as pg # pour accès à certaines constantes pyqtgraph, widget, etc…
import numpy as np # math et tableaux
import serial # communication serie
from tuto_pyqt_pyserial_pyqtgraph_oscillo_param_axes import * # fichier obtenu à partir QtDesigner et pyuic4
# +/- variables et objets globaux
class myApp(QWidget, Ui_Form): # la classe reçoit le Qwidget principal ET la classe définie dans test.py obtenu avec pyuic4
def __init__(self, parent=None):
QWidget.__init__(self) # initialise le Qwidget principal
self.setupUi(parent) # Obligatoire
# — Variables de classe
self.points=None # déclaration initiale
self.compt=0 # variable comptage
self.nombreValeurs=250
self.framerate=20 # nombre d’image par secondes (rafraîchissement de l’affichage)
# — Paramétrage des widgets de l’interface GUI si nécessaire —
# — Connexions entre signaux des widgets et fonctions
# connecte chaque signal utilisé des objets à l’appel de la fonction voulue
#— port série —
self.connect(self.pushButtonInitSerial, SIGNAL(« clicked() »), self.pushButtonInitSerialClicked)
self.connect(self.pushButtonEnvoi, SIGNAL(« clicked() »), self.pushButtonEnvoiClicked)
self.connect(self.pushButtonStop, SIGNAL(« clicked() »), self.pushButtonStopClicked)
# — signaux widgets de paramétrage du graphique pyqtgraph —
self.connect(self.pushButtonInit, SIGNAL(« clicked() »), self.pushButtonInitClicked)
self.connect(self.checkBoxAxisBottom, SIGNAL(« clicked() »), self.checkBoxAxisBottomClicked) # connecte le signal Clicked de l’objet checkBox à l’appel de la fonction voulue
self.connect(self.checkBoxAxisLeft, SIGNAL(« clicked() »), self.checkBoxAxisLeftClicked) # connecte le signal Clicked de l’objet checkBox à l’appel de la fonction voulue
self.connect(self.checkBoxLabelAxisBottom, SIGNAL(« clicked() »), self.checkBoxLabelAxisBottomClicked) # connecte le signal Clicked de l’objet checkBox à l’appel de la fonction voulue
self.connect(self.checkBoxLabelAxisLeft, SIGNAL(« clicked() »), self.checkBoxLabelAxisLeftClicked) # connecte le signal Clicked de l’objet checkBox à l’appel de la fonction voulue
# — Code actif initial —
#– initialise le graphique pyqtgraph —
# l’objet self.graph correspond au plotWidget créé dans QtDesigner
# aspect fond /axes
#self.graph.hideAxis(‘left’) # masque axes – left, bottom, right, or top
self.graph.setBackgroundBrush(QBrush(QColor(Qt.white))) # la classe PlotWidget est un GraphicsWidget qui est un QGraphics View
self.graph.showGrid(x=True, y=True) # affiche la grille
self.graph.getAxis(‘bottom’).setPen(pg.mkPen(0,0,255)) # couleur de l’axe + grille
self.graph.getAxis(‘left’).setPen(pg.mkPen(255,0,0)) # couleur de l’axe + grille
# légende des axes
labelStyle = {‘color’: ‘#00F’, ‘font-size’: ’10pt’} # propriétés CSS à utiliser pour le label
self.graph.getAxis(‘bottom’).setLabel(‘Mesure’, units=‘unit’, **labelStyle) # label de l’axe
self.graph.getAxis(‘left’).setLabel(‘Valeur brute’, units=‘unit’, **labelStyle) # label de l’axe
# adaptation échelle axes
# axe X et Y sont autoscale par défaut
self.graph.enableAutoRange(axis=pg.ViewBox.YAxis, enable=False) # fonction plotItem : désactive autoscale Y
self.minY=0
self.maxY=1023
self.graph.setYRange(self.minY,self.maxY) # fonction plotItem : fixe échelle des Y
self.minX=0
self.maxX=self.nombreValeurs
self.graph.setXRange(self.minX,self.maxX) # fonction plotItem : fixe échelle des X
# interactivité
#self.graph.setInteractive(False) # fonction QGraphics View : pour inactiver interaction souris
self.graph.getViewBox().setMouseMode(pg.ViewBox.RectMode) # fonction ViewBox pas accessible depuis PlotWidget : fixe selection par zone
self.graph.setMouseEnabled(x=False, y=True) # désactive interactivité axe X
#– initialise données —
#– définition des x
self.x = np.arange(0.0, self.nombreValeurs+1, 1.0) # crée un vecteur de n valeurs à intervalle régulier pour les x
print(self.x) # debug – affiche les valeurs x
#– calcul des y : courbe y=f(x)
self.y=np.zeros(self.nombreValeurs+1)# crée un tableau de valeur y basé sur x – courbe y=sin(x)
#self.y= np.random.normal(0,1,size=self.nombreValeurs) # génére une série de 1000 valeurs aléatoires
print(self.y) # debug – affiche les valeurs y
#– affichage de la courbe —
self.courbe=self.graph.plot(self.x,self.y, pen=(0,0,255)) # avec couleur
# — Activation d’un Timer pour MAJ graphique – fixe le fps d’affichage en fait —
self.timerRefreshPlot=QTimer() # déclare un timer Qt
self.connect(self.timerRefreshPlot, SIGNAL(« timeout() »), self.refreshPlot) # connecte le signal timeOut de l’objet timer à l’appel de la fonction voulue
self.timerRefreshPlot.start(int(1000/self.framerate)) # lance le timer – mettre délai assez court pour fps élevé
#— initialisation Timer Série
self.timerSerial=QTimer() # déclare un timer Qt
self.connect(self.timerSerial, SIGNAL(« timeout() »), self.timerSerialEvent) # connecte le signal timeOut de l’objet timer à l’appel de la fonction voulue
#— déclaration utiles —
self.serialPort=None # déclaration initiale
# — les fonctions appelées, utilisées par les signaux des widgets —
# — widgets config Axes Pyqtgraph —
def pushButtonInitClicked(self):
print(« Bouton Init cliqué »)
self.graph.setYRange(self.minY,self.maxY) # fonction plotItem : fixe échelle des Y
self.graph.setXRange(0,self.nombreValeurs) # fonction plotItem : fixe échelle des Y
def checkBoxAxisBottomClicked(self):
print(« Checkbox axe X cliqué »)
if self.checkBoxAxisBottom.isChecked(): # si checkBox sélectionné
print(« Etat checkBox = « + str(self.checkBoxAxisBottom.isChecked()))
self.graph.showAxis(‘bottom’)
# self.graph.showAxis(‘bottom’, True) # équivalent
if self.checkBoxLabelAxisBottom.isChecked(): self.graph.showLabel(‘bottom’,True)
else : # si checkBox pas sélectionné
print(« Etat checkBox = « + str(self.checkBoxAxisBottom.isChecked()))
self.graph.hideAxis(‘bottom’)
# self.graph.showAxis(‘bottom’, False) # équivalent
self.graph.showLabel(‘bottom’,False)
def checkBoxAxisLeftClicked(self):
print(« Checkbox axe Y cliqué »)
if self.checkBoxAxisLeft.isChecked(): # si checkBox sélectionné
print(« Etat checkBox = « + str(self.checkBoxAxisLeft.isChecked()))
self.graph.showAxis(‘left’)
# self.graph.showAxis(‘left’, True) # équivalent
if self.checkBoxLabelAxisLeft.isChecked():self.graph.showLabel(‘left’,True)
else : # si checkBox pas sélectionné
print(« Etat checkBox = « + str(self.checkBoxAxisLeft.isChecked()))
self.graph.hideAxis(‘left’)
self.graph.showLabel(‘left’,False)
def checkBoxLabelAxisBottomClicked(self):
print(« Checkbox Label axe X cliqué »)
if self.checkBoxLabelAxisBottom.isChecked(): # si checkBox sélectionné
print(« Etat checkBox = « + str(self.checkBoxLabelAxisBottom.isChecked()))
self.graph.showLabel(‘bottom’,True)
else : # si checkBox pas sélectionné
print(« Etat checkBox = « + str(self.checkBoxLabelAxisBottom.isChecked()))
self.graph.showLabel(‘bottom’,False)
def checkBoxLabelAxisLeftClicked(self):
print(« Checkbox Label axe Y cliqué »)
if self.checkBoxLabelAxisLeft.isChecked(): # si checkBox sélectionné
print(« Etat checkBox = « + str(self.checkBoxLabelAxisLeft.isChecked()))
self.graph.showLabel(‘left’,True)
else : # si checkBox pas sélectionné
print(« Etat checkBox = « + str(self.checkBoxLabelAxisLeft.isChecked()))
self.graph.showLabel(‘left’,False)
#—– les fonctions des signaux des boutons du Terminal série —-
def pushButtonInitSerialClicked(self): # lors appui bouton initialisation série
print(« Bouton Init cliqué »)
if self.serialPort: # si le port existe déjà
self.serialPort.close() # ferme le port si existe
# — initialise paramètres initialisation
if self.comboBoxPort.currentText()==« » : # si le champ d’initialisation Port est vide = initialisation par défaut
strPortInit=« /dev/ttyACM0 » # port par défaut
else :
strPortInit=str(self.comboBoxPort.currentText()) #sinon utilise paramètre champ texte pour le port
strDebitInit=str(self.comboBoxDebit.currentText()) # paramètre champ texte pour debit
#— initialisation série avec gestion erreur —
try: # essaie d’exécuter les instructions
# initialise port serie avec délai attente en réception en ms
self.serialPort=serial.Serial(strPortInit, strDebitInit, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, 0.100)
#self.serialPort=serial.Serial(strPortInit, strDebitInit) # initialise port serie forme réduite
self.serialPort.flushInput() # vide la file d’attente série
print(« Initialisation Port Série : « + strPortInit + » @ « + strDebitInit + » = OK « ) # affiche debug
#– change aspect bouton init
self.pushButtonInitSerial.setStyleSheet(QString.fromUtf8(« background-color: rgb(0, 255, 0); »)) # bouton en vert
self.pushButtonInitSerial.setText(« OK ») # change titre bouton
except: # si erreur initialisation
print(« Erreur initialisation Série »)
#– change aspect bouton init
self.pushButtonInitSerial.setStyleSheet(QString.fromUtf8(« background-color: rgb(255, 127, 0); »)) # bouton en orange
self.pushButtonInitSerial.setText(QString.fromUtf8(« PB »)) # change titre bouton
#self.timerSerial.start(20) # lance le timer avec délai en ms – 10 pour réception rapide
self.timerSerial.start(self.spinBoxDelaiReception.value()) # lance le timer avec délai en ms avec valeur spinbox
def pushButtonEnvoiClicked(self): # lors appui bouton envoi série du champ du Terminal Série
print(« Bouton ENVOI appuyé »)
self.envoiChaineSerie(str(self.lineEditChaineEnvoi.text())) # envoi le contenu du champ texte sur le port série
#– fonction gestion clicked pushButton Stop
def pushButtonStopClicked(self):
print(« Bouton Stop cliqué »)
#– stoppe la réception série —
if self.serialPort: # si le port existe déjà
self.serialPort.close() # ferme le port si existe
self.timerSerial.stop() # stoppe le timer
#– change aspect bouton init
self.pushButtonInitSerial.setStyleSheet(QString.fromUtf8(« background-color: rgb(255, 127, 0); »)) # bouton en orange
self.pushButtonInitSerial.setText(QString.fromUtf8(« Off »)) # change titre bouton
#– fin fonction gestion clicked pushButton Stop
# — les fonctions appelées, utilisées par les signaux hors widgets —
#—– fonction de gestion du signal timeout du QTimer
def timerSerialEvent(self): # fonction appelée lors de la survenue d’un évènement Timer – nom fonction indiférrent
#– variables de réception —
self.chaineIn=« »;
self.char=« »;
# lecture des données reçues
if self.serialPort: # seulement si le port série existe
self.timerSerial.stop() # stoppe le timer le temps de lire les caractères et éviter « réentrée »
while (self.serialPort.inWaiting()): # tant que au moins un caractère en réception
self.char=self.serialPort.read() # on lit le caractère
#self.chaineIn=self.chaineIn+self.char # forme minimale…
if self.char==‘\n‘: # si saut de ligne, on sort du while
print(« saut ligne reçu ») # debug
break # sort du while
else: #tant que c’est pas le saut de ligne, on l’ajoute à la chaine
self.chaineIn=self.chaineIn+self.char
if len(self.chaineIn)>0: # … pour ne pas avoir d’affichage si « »
print(self.chaineIn) # affiche la chaîne
self.textEditReception.append(self.chaineIn[:-1]) # ajoute le texte au textEdit en enlevant le dernier caractère (saut de ligne)
if self.chaineIn[:1].isdigit(): # si la chaine est un chiffre
self.draw(int(self.chaineIn)) # // ajoute la valeur au graphique
self.timerSerial.start(self.spinBoxDelaiReception.value()) # lance le timer avec délai en ms avec valeur spinbox
#self.timerSerial.start() # redémarre le timer
# ne pas stopper le timerSerial permet plus grande vitesse réception… Il faut que Arduino envoie à même fréquence également
#—- fin timerEvent Serial
#—– fonction de gestion du signal timeout du QTimer
def refreshPlot(self): # fonction appelée lors de la survenue d’un évènement Timer – nom fonction indiférrent
print (« MAJ tracé »)
self.courbe.setData(self.x,self.y) # actualisation valeurs courbe
# — fonctions de classes autres—
#—– fonction de classe commune d’envoi d’une chaîne sur le port série —-
def envoiChaineSerie(self, chaineIn): # la fonction reçoit un objet chaîne Str
if self.serialPort: # seulement si le port série existe – n’existe pas (=None) tant que pas initialisé
self.timerSerial.stop() # stoppe le timer le temps d’envoyer message sur le port série
# combobox avec index 0 = rien, 1=saut de ligne (LF), 2=retour chariot (CR), 3= les 2 LF+CR
if self.comboBoxFinLigne.currentIndex()==0: # si rien sélectionné
# self.serialPort.write(str(self.lineEditChaineEnvoi.text())+’\n’ ) # envoie la chaine sur le port serie
self.serialPort.write(chaineIn) # envoie la chaine sur le port serie – variante ascii
print(« Envoi Série : « + chaineIn )
self.textEditTraceEnvoiSerie.append(chaineIn) # ajoute texteEdit de visualisation
if self.comboBoxFinLigne.currentIndex()==1: # si saut de ligne sélectionné
self.serialPort.write(chaineIn +chr(10) ) # envoie la chaine sur le port serie – variante ascii
print(« Envoi Série : « + chaineIn + ‘\n‘)
self.textEditTraceEnvoiSerie.append(chaineIn) # ajoute texteEdit de visualisation
if self.comboBoxFinLigne.currentIndex()==2: # si retour chariot sélectionné
self.serialPort.write(chaineIn+chr(13) ) # envoie la chaine sur le port serie – variante ascii
print(« Envoi Série : « + chaineIn + ‘\r‘)
self.textEditTraceEnvoiSerie.append(chaineIn) # ajoute texteEdit de visualisation
if self.comboBoxFinLigne.currentIndex()==3: # si saut de ligne + retour chariot sélectionné
self.serialPort.write(chaineIn+chr(10)+chr(13) ) # envoie la chaine sur le port serie – variante ascii
print(« Envoi Série : « + chaineIn + ‘\n‘+‘\r‘)
self.textEditTraceEnvoiSerie.append(chaineIn) # ajoute texteEdit de visualisation
self.timerSerial.start(self.spinBoxDelaiReception.value()) # lance le timer avec délai en ms avec valeur spinbox
#self.timerSerial.start() # redémarre le timer – laisse délai pour réception en réinitialisation Timer à 0
# car sinon l’appui survient n’importe quand et si survient peu de temps avant fin délai
# la réception est hachée
#— fin envoiChaineSerie
#– fonction tracé appelée lors événement TimerSérie
def draw(self, value): # fonction appelée lors de la survenue d’un évènement Timer – nom de la fonction indiférrent
if self.compt==0: # premier passage
self.points= np.array([[self.compt,value]]) # tableau à 2 dimensions – ici 1er points
self.x=self.points[:,0] # la première colonne = les x
self.y=self.points[:,1] # la deuxième colonne = les y
self.compt=self.compt+1 # incrémente compt
elif self.compt<=self.nombreValeurs: # on remplit le tableau de point une première fois
newY=value
#newY=self.compt # x=y – debug
self.points=np.append(self.points,[[self.compt,newY]],axis=0)# ajouter un point au tableau
self.x=self.points[:,0] # la première colonne = les x
self.y=self.points[:,1] # la deuxième colonne = les y
self.compt=self.compt+1 # incrémente compt
else:
#self.points=roll(self.points,1, axis=1) # décale les éléments y de 1 – fonctin numpy
#self.x=self.points[:,0] # la première colonne = les x – existe déjà
#self.y=self.points[:,1] # la deuxième colonne = les y – existe déjà
#self.y=roll(self.y,1) # décale les éléments y de 1 – fonctin numpy – les x ne bougent pas..
self.y=np.roll(self.y,-1) # décale les éléments y de 1 – fonctin numpy – les x ne bougent pas..
#self.y[self.Xmax]=self.y[self.Xmax-1]/2 # nouvelle valeur en dernière position
self.y[self.nombreValeurs]=value # nouvelle valeur en dernière position
# important : pour le tracé, ce n’est pas l’ordre qui est important, mais la coordonnée x,y dans notre cas…
# remarquer la simplicité avec laquelle il es possible d’extraire le tableau des x ou des y à partir du tableau de points
#if self.compt==self.Xmax : self.compt=0 # RAZ compt
# debug
#print self.points
#print self.x
#print self.y
#– actualise la courbe —
#self.courbe.setData(self.x,self.y) # actualisation valeurs courbe
# voir refrehPlot
# — Autres Classes utiles —
# — Classe principale (lancement) —
def main(args):
a=QApplication(args) # crée l’objet application
f=QWidget() # crée le QWidget racine
c=myApp(f) # appelle la classe contenant le code de l’application
f.show() # affiche la fenêtre QWidget
r=a.exec_() # lance l’exécution de l’application
return r
if __name__==« __main__ »: # pour rendre le code exécutable
main(sys.argv) # appelle la fonction main
Code Arduino d’exemple
const int RVar=0; //declaration constante de broche analogique
// — Déclaration des variables globales —
int mesureBrute=0;// Variable pour acquisition résultat brut de conversion analogique numérique
//**************** FONCTION SETUP = Code d’initialisation *****
void setup() { // debut de la fonction setup()
Serial.begin(115200); // initialise connexion série à 115200 bauds
// IMPORTANT : régler le terminal côté PC avec la même valeur de transmission
} // fin de la fonction setup()
//***** FONCTION LOOP = Boucle sans fin *****
void loop(){ // debut de la fonction loop()
// acquisition conversion analogique-numerique (CAN) sur la voie analogique
mesureBrute=analogRead(RVar);
// affiche valeur numerique entière ou à virgule au format décimal
Serial.println(mesureBrute);
delay(500);
} // fin de la fonction loop() – le programme recommence au début de la fonction loop sans fin
Utilisation
- Les 2 fichiers suivants sont à enregistrer dans un même répertoire, l’un en nom.py et l’autre en nomMain.py.
- Puis lancer l’application depuis Geany ou équivalent, en exécutant le fichier nomMain.py
- Initialiser la comunication série, paramétrer le délai avec le même que celui utilisé côté Arduino : la courbe s’affiche.
Articles Liés
- Javascript : Graphique Dygraphs simple
Le Javascript est un langage de programmation très populaire et puissant qui permet aux développeurs…
- Javascript : Afficher 6 widgets graphiques fournis par une librairie graphique externe.
Le Javascript est un langage de programmation très populaire qui permet aux développeurs de créer…
- Javascript : Graphique Dygraphs : afficher date à partir unixtime
Le langage de programmation Javascript est très populaire et est utilisé pour créer des applications…