View  Edit  Attributes  History  Attach  Print  Search

ACCUEIL | ARDUINO > S'INSTALLER > DEBUTER > APPROFONDIR | PROCESSING | MECATRONIQUE | MATERIEL | OUTILS | TESTS | Plus...|
Python > Shell > ATELIERS Python + Qt > PyQt apps > PyQt+Arduino | Mes Robots | RepRap | Mes lib'Arduino | Mes shields Arduino | Mes distros | Les Mini-PC |
ATELIERS ARDUINO| VIDEOS | COMPRENDRE | REFERENCES | CODER | TECHNIQUE | GNU/LINUX | LIENS | Rien à voir |

PYQTLAB

PyQt Lab' : Mini-PC : pcDuino : Affichage sous forme de courbe de la mesure d'une voie analogique en temps réel dans une interface graphique (oscillo monovoie simple).


Par X. HINAULT - Juin 2013

Capture du bureau du pcDuino en accès distant par VNC, webcam branchée.

Ce que l'on va faire ici

  • Dans ce programme PyQt, je montre comment réaliser un simple oscilloscope monovoie dans une interface PyQt à l'aide de la librairie PyqtGraph.
  • Truc d'utilisation : branché sur un écran, votre pcDuino devient un oscilloscope opérationnel pour des tests de capteurs, etc... indépendamment de votre PC fixe !

Pré-requis

sudo wget -4 -N https://raw.github.com/sensor56/pyDuino/master/pcduino/pyduino.py /usr/lib/python2.7/dist-packages
  • La librairie PyqtGraph devra être installée sur le système.

Téléchargement des codes

  • L'archive avec les codes est disponible ici :
  • Pour cloner le dépôt :
$ git clone ..

Matériel nécessaire

  • une plaque d'essai pour montage sans soudures,
  • des straps,
  • une résistance variable,

Instructions de montage

  • Connecter la sortie variable de la résistance variable à la broche A2, la deux autres broches de la résistance variable étant connectées entre le 0V et le 3.3V.

Le montage à réaliser

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: Sat Aug 10 15:20:32 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(507, 390)
        self.graph = PlotWidget(Form)
        self.graph.setGeometry(QtCore.QRect(10, 20, 480, 360))
        self.graph.setObjectName(_fromUtf8("graph"))

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "PyQt + Pyduino + pyqtgraph : Oscillo simple", 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

  • Remarquer comment les fonctions Pyduino sont directement appelées dans le code PyQt : simple et efficace !!

#!/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

from pyduino import * # importe Pyduino

from tuto_pyqt_pyduino_pyqtgraph_oscillo_simple 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=100
                self.framerate=10 # 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

                # --- 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=4096 
                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= 500+(np.random.normal(0,1,size=self.nombreValeurs+1) *250) # génére une série de n 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é

        # --- les fonctions appelées, utilisées par les signaux des widgets ---


        # --- les fonctions appelées, utilisées par les signaux hors widgets ---

        #----- 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 graph : valeur = "), # debug

                #value=500

                value=analogRead(A2)
                print value

                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

                #-- initialise la courbe --
                self.courbe.setData(self.x,self.y) # initialisation valeurs courbe

        # --- fonctions de classes autres---    



# -- 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


 

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
  • Au lancement, la fenêtre graphique doit apparaître avec l'interface graphique : la courbe temps réel de la mesure analogique s'affiche à l'écran !