PyQt Lab’ : Port Série : en réception : Réception d’une valeur numérique sur le port série, dans un QwtThermo, le widget d’affichage analogique « vu-mètre », conversion mesure/unité et affichage dans des widgets LCD.
Par X. HINAULT – Juin 2013

Ce que l’on va faire ici
- Dans ce code PyQt, une interface permettant la réception d’une valeur numérique sur le port série, dans un QwtThermo, le widget d’affichage analogique « vu-mètre », conversion mesure/unité et affichage dans des widgets LCD.
Pré-requis
- python 2.7
- pyqt4.x
- modules :
- python-serial
- python-qwt5-qt4
Téléchargement :
- L’archive des codes est disponible ici : ..
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: Sun Feb 3 11:24:41 2013
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
from PyQt4.Qwt5 import * # module Qwt
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(610, 275)
self.labelDebit = QtGui.QLabel(Form)
self.labelDebit.setGeometry(QtCore.QRect(10, 45, 111, 16))
self.labelDebit.setObjectName(_fromUtf8(« labelDebit »))
self.comboBoxPort = QtGui.QComboBox(Form)
self.comboBoxPort.setGeometry(QtCore.QRect(5, 20, 151, 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.labelReception = QtGui.QLabel(Form)
self.labelReception.setGeometry(QtCore.QRect(5, 135, 191, 16))
self.labelReception.setObjectName(_fromUtf8(« labelReception »))
self.textEditReception = QtGui.QTextEdit(Form)
self.textEditReception.setGeometry(QtCore.QRect(5, 155, 156, 111))
self.textEditReception.setLineWrapMode(QtGui.QTextEdit.NoWrap)
self.textEditReception.setObjectName(_fromUtf8(« textEditReception »))
self.pushButtonInitSerial = QtGui.QPushButton(Form)
self.pushButtonInitSerial.setGeometry(QtCore.QRect(5, 95, 101, 31))
self.pushButtonInitSerial.setObjectName(_fromUtf8(« pushButtonInitSerial »))
self.comboBoxDebit = QtGui.QComboBox(Form)
self.comboBoxDebit.setGeometry(QtCore.QRect(5, 60, 111, 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.labelPort = QtGui.QLabel(Form)
self.labelPort.setGeometry(QtCore.QRect(10, 5, 101, 16))
self.labelPort.setObjectName(_fromUtf8(« labelPort »))
self.lcdNumberValeurCalc = QtGui.QLCDNumber(Form)
self.lcdNumberValeurCalc.setGeometry(QtCore.QRect(450, 140, 96, 36))
self.lcdNumberValeurCalc.setStyleSheet(_fromUtf8(« background-color: rgb(255, 255, 0);\n«
« color: rgb(255, 0, 0); »))
self.lcdNumberValeurCalc.setSmallDecimalPoint(True)
self.lcdNumberValeurCalc.setMode(QtGui.QLCDNumber.Dec)
self.lcdNumberValeurCalc.setObjectName(_fromUtf8(« lcdNumberValeurCalc »))
self.lineEditValeurMax = QtGui.QLineEdit(Form)
self.lineEditValeurMax.setGeometry(QtCore.QRect(530, 90, 66, 23))
self.lineEditValeurMax.setObjectName(_fromUtf8(« lineEditValeurMax »))
self.labelValeurBrute = QtGui.QLabel(Form)
self.labelValeurBrute.setGeometry(QtCore.QRect(450, 20, 81, 16))
self.labelValeurBrute.setObjectName(_fromUtf8(« labelValeurBrute »))
self.labelValeurMin = QtGui.QLabel(Form)
self.labelValeurMin.setGeometry(QtCore.QRect(455, 75, 71, 16))
self.labelValeurMin.setObjectName(_fromUtf8(« labelValeurMin »))
self.labelValeurMax = QtGui.QLabel(Form)
self.labelValeurMax.setGeometry(QtCore.QRect(530, 75, 71, 16))
self.labelValeurMax.setObjectName(_fromUtf8(« labelValeurMax »))
self.lineEditUnite = QtGui.QLineEdit(Form)
self.lineEditUnite.setGeometry(QtCore.QRect(550, 155, 56, 23))
self.lineEditUnite.setObjectName(_fromUtf8(« lineEditUnite »))
self.lineEditValeurMin = QtGui.QLineEdit(Form)
self.lineEditValeurMin.setGeometry(QtCore.QRect(455, 90, 66, 23))
self.lineEditValeurMin.setObjectName(_fromUtf8(« lineEditValeurMin »))
self.labelValeurCalc = QtGui.QLabel(Form)
self.labelValeurCalc.setGeometry(QtCore.QRect(455, 120, 106, 16))
self.labelValeurCalc.setObjectName(_fromUtf8(« labelValeurCalc »))
self.labelUnite = QtGui.QLabel(Form)
self.labelUnite.setGeometry(QtCore.QRect(550, 140, 51, 13))
self.labelUnite.setObjectName(_fromUtf8(« labelUnite »))
self.lcdNumberValeurBrute = QtGui.QLCDNumber(Form)
self.lcdNumberValeurBrute.setGeometry(QtCore.QRect(450, 35, 96, 36))
self.lcdNumberValeurBrute.setStyleSheet(_fromUtf8(« background-color: rgb(170, 255, 127);\n«
« color: rgb(0, 170, 0); »))
self.lcdNumberValeurBrute.setSmallDecimalPoint(False)
self.lcdNumberValeurBrute.setObjectName(_fromUtf8(« lcdNumberValeurBrute »))
self.line = QtGui.QFrame(Form)
self.line.setGeometry(QtCore.QRect(160, 5, 16, 266))
self.line.setFrameShape(QtGui.QFrame.VLine)
self.line.setFrameShadow(QtGui.QFrame.Sunken)
self.line.setObjectName(_fromUtf8(« line »))
self.line_2 = QtGui.QFrame(Form)
self.line_2.setGeometry(QtCore.QRect(435, 5, 16, 266))
self.line_2.setFrameShape(QtGui.QFrame.VLine)
self.line_2.setFrameShadow(QtGui.QFrame.Sunken)
self.line_2.setObjectName(_fromUtf8(« line_2 »))
self.pushButtonStop = QtGui.QPushButton(Form)
self.pushButtonStop.setGeometry(QtCore.QRect(115, 85, 41, 41))
self.pushButtonStop.setObjectName(_fromUtf8(« pushButtonStop »))
self.qwtThermo = QwtThermo(Form)
self.qwtThermo.setGeometry(QtCore.QRect(250, 15, 103, 244))
self.qwtThermo.setAutoFillBackground(True)
self.qwtThermo.setAlarmEnabled(True)
self.qwtThermo.setAlarmLevel(512.0)
self.qwtThermo.setMaxValue(1023.0)
self.qwtThermo.setPipeWidth(50)
self.qwtThermo.setProperty(« value », 0.0)
self.qwtThermo.setObjectName(_fromUtf8(« qwtThermo »))
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate(« Form », « PyQt + PyQwt +PySerial : QwtThermo affiche can brute (0-1023) + conversion », None, QtGui.QApplication.UnicodeUTF8))
self.labelDebit.setText(QtGui.QApplication.translate(« Form », « Débit Série (bauds) : », 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.labelReception.setText(QtGui.QApplication.translate(« Form », « Réception sur le port série : « , None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonInitSerial.setText(QtGui.QApplication.translate(« Form », « Initialiser », 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.labelPort.setText(QtGui.QApplication.translate(« Form », « Port Série : », None, QtGui.QApplication.UnicodeUTF8))
self.lineEditValeurMax.setText(QtGui.QApplication.translate(« Form », « 5000 », None, QtGui.QApplication.UnicodeUTF8))
self.labelValeurBrute.setText(QtGui.QApplication.translate(« Form », « Valeur brute : », None, QtGui.QApplication.UnicodeUTF8))
self.labelValeurMin.setText(QtGui.QApplication.translate(« Form », « Valeur mini », None, QtGui.QApplication.UnicodeUTF8))
self.labelValeurMax.setText(QtGui.QApplication.translate(« Form », « Valeur maxi », None, QtGui.QApplication.UnicodeUTF8))
self.lineEditUnite.setText(QtGui.QApplication.translate(« Form », « mV », None, QtGui.QApplication.UnicodeUTF8))
self.lineEditValeurMin.setText(QtGui.QApplication.translate(« Form », « 0 », None, QtGui.QApplication.UnicodeUTF8))
self.labelValeurCalc.setText(QtGui.QApplication.translate(« Form », « Valeur calculée : », None, QtGui.QApplication.UnicodeUTF8))
self.labelUnite.setText(QtGui.QApplication.translate(« Form », « Unité : », None, QtGui.QApplication.UnicodeUTF8))
self.pushButtonStop.setText(QtGui.QApplication.translate(« Form », « Stop », None, QtGui.QApplication.UnicodeUTF8))
from PyQt4.Qwt5 import * # module Qwt
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 – Tous droits réservés – GPLv3
# Jan 2013 – www.mon-club-elec.fr
# — importation des modules utiles —
from PyQt4.QtGui import *
from PyQt4.QtCore import * # inclut Qtimer..
import os,sys
import serial # communication serie
from PyQt4.Qwt5 import * # module Qwt
#from PyQt4.Qwt5.anynumpy import * # pour les fonctions math..
from numpy import interp # pour équivalent map
# — importation du fichier de description GUI —
from tuto_pyqt_pyqwt_pyserial_qwtthermo_can_brutex1_lineedit import *
# classe principale contenant le code actif
class myApp(QWidget, Ui_Form): # la classe reçoit le Qwidget principal ET la classe définie dans test.py obtenu avec pyuic4
# Note : ici self représente la classe
def __init__(self, parent=None):
QWidget.__init__(self) # initialise le Qwidget principal
self.setupUi(parent) # Obligatoire
# —— code actif initial ——
#Réalisez les connexions supplémentaires entre signaux et slots
self.connect(self.pushButtonInitSerial, SIGNAL(« clicked() »), self.pushButtonInitSerialClicked)
self.connect(self.pushButtonStop, SIGNAL(« clicked() »), self.pushButtonStopClicked)
#===== Initialisation QwtThermo – à mettre avant QTimer ++ ========
self.qwtThermo.setFillColor(QColor(0,255,0)) # fixe la couleur de remplissage sous niveau alarme
self.qwtThermo.setAlarmColor(QColor(255,0,0)) # fixe couleur remplissage au dessus niveau alarme
#initialisation Timer
self.timer=QTimer() # déclare un timer Qt
self.connect(self.timer, SIGNAL(« timeout() »), self.timerEvent) # connecte le signal timeOut de l’objet timer à l’appel de la fonction voulue
#— déclaration utiles —
self.serialPort=None # déclaration initiale
self.chaineIn=« » # variable pour réception
#— les fonctions appelées, utilisées par les signaux
def pushButtonInitSerialClicked(self):
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 sec
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(« Connexion 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(« Non connecté »)) # change titre bouton
self.timer.start(10) # lance le timer avec délai en ms – 10 pour réception rapide
# pour optimiser la réception d’une chaîne envoyée à répétition, utiliser une valeur similaire à la valeur d’émission sur le port série
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.timer.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(« Non connecté »)) # change titre bouton
def timerEvent(self): # fonction appelée lors de la survenue d’un évènement Timer – nom fonction indiférrent
#– variables de réception —
#self.chaineIn= » » – pas systématique – seulement si un saut de ligne a été reçu
self.char=« »;
self.flagSautLigne=False # drapeau saut de ligne reçu
# lecture des données reçues
if self.serialPort: # seulement si le port série existe
self.timer.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
self.flagSautLigne=True # drapeau saut de ligne reçu
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
# fin while
# en sortie du while, on se place ici
print self.chaineIn
print self.flagSautLigne
# teste si chaine valide = non vide et saut de ligne = True…
if len(self.chaineIn)>1 and self.flagSautLigne==True: # … 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 (CR)
self.chaineIn=self.chaineIn[:-1] # retire dernier caractère avant extraire valeur numérique (LF)
if self.chaineIn.isalnum(): # si la chaine est un chiffre
print(« que des chiffres ») # debug
self.valeur=float(self.chaineIn)
self.qwtThermo.setValue(self.valeur) # fixe la valeur du qwtDial à partir de la valeur reçue sur port série
# actualise lcdNumber
self.lcdNumberValeurBrute.display(self.valeur) # affiche la valeur
# équiv map : interp(256,[1,512],[5,10]) (package numpy)
self.valeurCalc=interp(self.valeur, [0,1023],[float(self.lineEditValeurMin.text()),float(self.lineEditValeurMax.text())]) # équiv map
self.lcdNumberValeurCalc.display(self.valeurCalc)
# fin if isalnum
self.chaineIn=« »; # dans le if : une fois chaine valide (=non vide et avec saut de ligne) traitée
# fin if len>0 # fin traitement chaine valide…
self.timer.start() # redémarre le timer
# fin if Serial Port
# fin def timerEvent
#— fonctions actives
# …
# fonction principale exécutant l’application Qt
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
# pour rendre le fichier *.py exécutable
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
- La valeur numérique reçue sur le port série s’affiche sous forme d’un widget analogique de type bargraph avec champ de conversion de la valeur.
Articles similaires:
- PyQt Lab’ : Port Série : en réception : Afficher dans un QwtThermo, le widget d’affichage analogique « vu-mètre », une valeur de conversion analogique numérique (0-1023) reçue sur le port série en provenance d’Arduino.
- PyQt Lab’ : Série : Interface graphique de base pour initialiser la connexion au port série (sélection graphique du débit, du port utilisé)
- PyQt Lab’ : Port Série : en réception : Réception d’une valeur numérique sur le port série et affichage dans un widget LCD.
- PyQt Lab’ : Port Série : en réception : Réception d’une valeur numérique sur le port série, conversion mesure/unité et affichage dans des widgets LCD.
- PyQt Lab’ : Série : Interface graphique de base pour configuration, paramétrage, réception et affichage de chaines en provenance d’Arduino par le port série
Articles Liés
- PyQt Lab' : Port Série : en réception : Réception de 6 valeurs numériques sur le port série, dans 6 QwtDial, le widget d'affichage analogique « à aiguille », conversion mesure/unité et affichage dans 12 widgets LCD.
PyQt Lab est un outil puissant qui permet aux utilisateurs de créer des applications graphiques…
- PyQt Lab' : Port Série : en réception : Recevoir de façon asynchrone des paramètres numériques par réception de chaines spécifiques avec paramètre numérique reçue sur le port série (= décodage de "fonctions" différentes avec un seul paramètre numérique reçues sur le port série - affichage dans lcdNumber)
PyQt Lab' : Port Série : en réception : Recevoir de façon asynchrone des paramètres…
- PyQt Lab' : Port Série : en réception : Réception d'une valeur numérique sur le port série, dans un QwtDial, le widget d'affichage analogique « à aiguille », conversion mesure/unité et affichage dans des widgets LCD.
PyQt Lab est un outil puissant et intuitif qui permet aux développeurs de créer des…