PyQt Lab est un outil puissant et intuitif qui permet aux développeurs de créer des applications graphiques avec Qt. Dans cet article, nous allons nous concentrer sur l’utilisation de PyQt Lab pour réaliser une réception d’une valeur numérique sur le port série, sa conversion mesure/unité et son affichage dans des widgets LCD. Nous verrons comment utiliser le widget QwtDial, le widget d’affichage analogique « à aiguille », pour afficher la valeur numérique reçue sur le port série.
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.
Par X. HINAULT – Juin 2013

Ce que l’on va faire ici
- Dans ce code PyQt, interface permettant la 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.
Pré-requis
- python 2.7
- pyqt4.x
- modules :
- python-serial
- python-qwt5-qt4
Téléchargement :
- Ces codes sont disponibles 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: Thu Jan 31 20:52:20 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(610, 275)
self.qwtDial = QwtDial(Form)
self.qwtDial.setGeometry(QtCore.QRect(170, 5, 266, 261))
self.qwtDial.setReadOnly(True)
self.qwtDial.setLineWidth(5)
self.qwtDial.setFrameShadow(QwtDial.Sunken)
self.qwtDial.setMode(QwtDial.RotateNeedle)
self.qwtDial.setOrigin(90.0)
self.qwtDial.setWrapping(False)
self.qwtDial.setObjectName(_fromUtf8(« qwtDial »))
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.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
Form.setWindowTitle(QtGui.QApplication.translate(« Form », « PyQt + PyQwt +PySerial : QwtDial affiche can brute corrigée (0-1023) », 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_qwtdial_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 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
#===== Initialisation QwtDial ========
#– définition plage des valeurs du QwtDial
# void setRange (double vmin, double vmax, double vstep=0.0, int pagesize=1)
self.qwtDial.setRange(0, 1023.0) # modifie la plage de valeurs de l’afficheur
#– paramétrage initial du QwtDial —
self.qwtDial.setOrigin(90.0) # point de référence du tracé – 0°=3H, 90° = 6H, etc..
#self.qwtDial.setScaleArc(90.0, 270.0) # angle début / angle fin du tracé par rapport à l’origine
self.qwtDial.setScaleArc(45.0, 315.0) # angle début / angle fin du tracé par rapport à l’origine
self.qwtDial.setScale(0.0,10.0,100.0) # valeur min/max/pas des graduations de l’échelle – ici pas de 10 avec 10 intermédiaires
#– création de l’aiguille et association au QwtDial
#QwtDialSimpleNeedle ( Style style, bool hasKnob = true, const QColor & mid = Qt::gray, const QColor & base = Qt::darkGray )
aiguille=QwtDialSimpleNeedle(QwtDialSimpleNeedle.Arrow,True,QColor(Qt.red),QColor(Qt.blue)) # défini un QwtDialSimpleNeedle = aiguille simple
# .Arrow = style aiguille « flèche » | .Ray = style simple ligne
self.qwtDial.setNeedle(aiguille) # associe l’aiguille au QwtDial
#– paramétrage de l’aiguille
aiguille.setWidth(6.0) # fixe la largeur de l’aiguille
#— 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)
# positionne QwtDial
self.qwtDial.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
- L’aiguille se positionne en fonction de la valeur reçue.
Articles similaires:
- 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 : Afficher dans un QwtDial, le widget d’affichage analogique « à aiguille », une valeur de conversion analogique numérique (0-1023) reçue sur le port série en provenance d’Arduino.
- 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 : 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’ : 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.
Articles Liés
- 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' : 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…
- Javascript : Graphique Dygraphs simple
Le Javascript est un langage de programmation très populaire et puissant qui permet aux développeurs…