Logo Mon Club Elec

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

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

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

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 :

Le fichier d’interface *.py

  • Fichier obtenu automatiquement avec l’utilitaire pyuic4 à partir du fichier *.ui créé avec QtDesigner :
# -*- coding: utf-8 -*-

# 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

#!/usr/bin/python
# -*- 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

// — constantes des broches —

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.
Noter cet article

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Archive Mon Club Elec

Articles populaires

Newsletter

Inscrivez-vous maintenant et bénéficiez d'un soutien continu pour réaliser vos travaux électriques en toute sécurité.