Logo Mon Club Elec

PyQt Lab’ : Graphiques Math : Pyqtgraph : Afficher la courbe du corps noir et la paramétrer à l’aide d’un slider dans un graphique Pyqtgraph.

PyQt Lab est un outil puissant qui permet aux utilisateurs de créer des graphiques mathématiques complexes à l’aide de Pyqtgraph. Dans cet article, nous allons vous montrer comment afficher la courbe du corps noir et la paramétrer à l’aide d’un slider dans un graphique Pyqtgraph. Nous allons également vous expliquer comment utiliser cet outil pour créer des graphiques mathématiques plus complexes et plus intéressants.

PyQt Lab’ : Graphiques Math : Pyqtgraph : Afficher la courbe du corps noir et la paramétrer à l’aide d’un slider dans un graphique Pyqtgraph.

Par X. HINAULT – Juin 2013

PyQt Lab’ : Graphiques Math : Pyqtgraph :  Afficher la courbe du corps noir et la paramétrer à l’aide d’un slider dans un graphique Pyqtgraph.

Ce que l’on va faire ici

  • Afficher la courbe du corps noir et la paramétrer à l’aide d’un slider dans un graphique pyqtgraph.

Pré-requis

  • python 2.7
  • pyqt4.x
  • pyqtgraph
  • numpy
  • scipy

Infos utiles

  • La courbe du corps noir relie l’intensité lumineuse spectrale d’un corps chauffé.
  • Cette loi a été découverte par Planck et a résolu la fameuse « catastrophe ultra-violette », mettant en évidence la notion de « quantum d’énergie », ouvrant ainsi la voie à la relativité restreinte et à la physique quantique.
  • La formule est assez complexe mettant en jeu notamment la constante de Planck, la vitesse de la lumière et la constante Boltzmann :
PyQt Lab’ : Graphiques Math : Pyqtgraph :  Afficher la courbe du corps noir et la paramétrer à l’aide d’un slider dans un graphique Pyqtgraph.
  • Ce code est facilité grâce à l’utilisation de la librairie Scipy qui intégre les constantes physiques principales.

Le fichier d’interface *.ui

<?xml version=« 1.0 » encoding=« UTF-8 »?>
<ui version=« 4.0 »>
 <class>Form</class>
 <widget class=« QWidget » name=« Form »>
  <property name=« geometry »>
   <rect>
    <x>0</x>
    <y>0</y>
    <width>609</width>
    <height>477</height>
   </rect>
  </property>
  <property name=« windowTitle »>
   <string>PyQt + pyqtgraph : Courbe et Sliders : loi corps noir</string>
  </property>
  <widget class=« QSlider » name=« horizontalSlider »>
   <property name=« geometry »>
    <rect>
     <x>5</x>
     <y>415</y>
     <width>596</width>
     <height>19</height>
    </rect>
   </property>
   <property name=« minimum »>
    <number>1</number>
   </property>
   <property name=« maximum »>
    <number>10000</number>
   </property>
   <property name=« singleStep »>
    <number>100</number>
   </property>
   <property name=« pageStep »>
    <number>1000</number>
   </property>
   <property name=« value »>
    <number>5000</number>
   </property>
   <property name=« orientation »>
    <enum>Qt::Horizontal</enum>
   </property>
   <property name=« tickPosition »>
    <enum>QSlider::NoTicks</enum>
   </property>
   <property name=« tickInterval »>
    <number>1</number>
   </property>
  </widget>
  <widget class=« QLabel » name=« label_T »>
   <property name=« geometry »>
    <rect>
     <x>230</x>
     <y>450</y>
     <width>51</width>
     <height>13</height>
    </rect>
   </property>
   <property name=« text »>
    <string>Temp =</string>
   </property>
  </widget>
  <widget class=« QLCDNumber » name=« lcdNumber_w »>
   <property name=« geometry »>
    <rect>
     <x>285</x>
     <y>445</y>
     <width>64</width>
     <height>23</height>
    </rect>
   </property>
   <property name=« styleSheet »>
    <string notr=« true »>background-color: rgb(170, 255, 255);
color: rgb(0, 0, 127);</string>
   </property>
   <property name=« segmentStyle »>
    <enum>QLCDNumber::Flat</enum>
   </property>
   <property name=« intValue » stdset=« 0 »>
    <number>5000</number>
   </property>
  </widget>
  <widget class=« PlotWidget » name=« plotwidget »>
   <property name=« geometry »>
    <rect>
     <x>5</x>
     <y>10</y>
     <width>600</width>
     <height>400</height>
    </rect>
   </property>
  </widget>
  <widget class=« QLabel » name=« label_T_units »>
   <property name=« geometry »>
    <rect>
     <x>355</x>
     <y>450</y>
     <width>51</width>
     <height>13</height>
    </rect>
   </property>
   <property name=« text »>
    <string>Kelvins</string>
   </property>
  </widget>
 </widget>
 <customwidgets>
  <customwidget>
   <class>PlotWidget</class>
   <extends>QGraphicsView</extends>
   <header>pyqtgraph</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections>
  <connection>
   <sender>horizontalSlider</sender>
   <signal>valueChanged(int)</signal>
   <receiver>lcdNumber_w</receiver>
   <slot>display(int)</slot>
   <hints>
    <hint type=« sourcelabel »>
     <x>181</x>
     <y>433</y>
    </hint>
    <hint type=« destinationlabel »>
     <x>309</x>
     <y>460</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

 

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 Sep  6 15:34:14 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(609, 477)
        self.horizontalSlider = QtGui.QSlider(Form)
        self.horizontalSlider.setGeometry(QtCore.QRect(5, 415, 596, 19))
        self.horizontalSlider.setMinimum(1)
        self.horizontalSlider.setMaximum(10000)
        self.horizontalSlider.setSingleStep(100)
        self.horizontalSlider.setPageStep(1000)
        self.horizontalSlider.setProperty(« value », 5000)
        self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal)
        self.horizontalSlider.setTickPosition(QtGui.QSlider.NoTicks)
        self.horizontalSlider.setTickInterval(1)
        self.horizontalSlider.setObjectName(_fromUtf8(« horizontalSlider »))
        self.label_T = QtGui.QLabel(Form)
        self.label_T.setGeometry(QtCore.QRect(230, 450, 51, 13))
        self.label_T.setObjectName(_fromUtf8(« label_T »))
        self.lcdNumber_w = QtGui.QLCDNumber(Form)
        self.lcdNumber_w.setGeometry(QtCore.QRect(285, 445, 64, 23))
        self.lcdNumber_w.setStyleSheet(_fromUtf8(« background-color: rgb(170, 255, 255);\n« 
« color: rgb(0, 0, 127); »))
        self.lcdNumber_w.setSegmentStyle(QtGui.QLCDNumber.Flat)
        self.lcdNumber_w.setProperty(« intValue », 5000)
        self.lcdNumber_w.setObjectName(_fromUtf8(« lcdNumber_w »))
        self.plotwidget = PlotWidget(Form)
        self.plotwidget.setGeometry(QtCore.QRect(5, 10, 600, 400))
        self.plotwidget.setObjectName(_fromUtf8(« plotwidget »))
        self.label_T_units = QtGui.QLabel(Form)
        self.label_T_units.setGeometry(QtCore.QRect(355, 450, 51, 13))
        self.label_T_units.setObjectName(_fromUtf8(« label_T_units »))

        self.retranslateUi(Form)
        QtCore.QObject.connect(self.horizontalSlider, QtCore.SIGNAL(_fromUtf8(« valueChanged(int) »)), self.lcdNumber_w.display)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate(« Form », « PyQt + pyqtgraph : Courbe et Sliders : loi corps noir », None, QtGui.QApplication.UnicodeUTF8))
        self.label_T.setText(QtGui.QApplication.translate(« Form », « Temp = », None, QtGui.QApplication.UnicodeUTF8))
        self.label_T_units.setText(QtGui.QApplication.translate(« Form », « Kelvins », 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 – Tous droits réservés – GPLv3
# Déc 2012 – www.mon-club-elec.fr

# — importation des modules utiles —
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…
# pas indisp. sinon car pyqtgraph est inclut par la déclaration QtDesigner de PlotWidget

import numpy

from scipy import e
from scipy.constants import codata

# — importation du fichier de description GUI —
from tuto_pyqt_pyqtgraph_courbe_corps_noir_slider 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

                #– variables utiles —
                #– variables utiles —
                self.c=codata.value(‘speed of light in vacuum’) # constante vitesse de la lumière
                print (« Constante vitesse de la lumière dans le vide = « + str(self.c))

                self.h=codata.value(‘Planck constant’) # constante vitesse de la lumière
                print (« Constante de Planck = « + str(self.h))

                self.kb=codata.value(‘Boltzmann constant’) # constante de Boltzman
                print (« Constante de Boltzmann = « + str(self.kb))

                self.T=5000.0 # température en kelvin

                #Ici, personnalisez vos widgets si nécessaire en utilisant les noms définis dans QtDesigner

                #Réalisez les connexions supplémentaires entre signaux et slots
                # les widgets sont désignés sous la forme self.nom en utilisant les noms définis dans QtDesigner
                self.connect(self.horizontalSlider, SIGNAL(« valueChanged(int) »), self.horizontalSliderValueChanged)
                # connecte le signal valueChanged de l’objet Slider à l’appel de la fonction voulue

                # —— code actif initial ——

                #– initialise données —
                self.x = numpy.arange(0.1, 3000, 1) # plus précision réduite, plus rapide…
                numpy.seterr(over=‘ignore’) # ignore warning overflow float
                print(self.x.dtype)
                print(self.x) # debug – affiche les valeurs x

                # calcul Y = la loi du corps noir
                self.y=(2*self.h*self.c**2/(self.x*1e-9)**5) *(pow(e,self.h*self.c/(self.x*1e-9*self.kb*self.T))1)**1

                # debug
                print(self.y) # debug – affiche les valeurs y
                print(« max= »+str(numpy.amax(self.y))) # extrait la valeur maxi

                index=numpy.argmax(self.y) # indice du max…
                print(« indice max= »+str(index)) # affiche l’index valeur maxi
                print(« longueur onde max= »+str(self.x[index])+ « nm ») # extrait la long onde du pic maxi

                #– initialise la courbe —
                self.plotwidget.hideAxis(‘left’) # masque axes – ‘left’, ‘bottom’, ‘right’, or ‘top’
                self.plotwidget.setBackgroundBrush(QBrush(QColor(Qt.white))) # la classe PlotWidget est un GraphicsWidget qui est un QGraphics View
                self.plotwidget.showGrid(x=True, y=True)  # affiche la grille
                #self.plotwidget.setInteractive(False) # fonction QGraphics View : pour inactiver interaction souris
                self.plotwidget.getViewBox().setMouseMode(pg.ViewBox.RectMode)  # fonction ViewBox pas accessible depuis PlotWidget : fixe selection par zone

                self.plot1=self.plotwidget.plot(self.x,self.y, pen=(0,0,255)) # avec couleur – mémorise la courbe

                # ——————— ligne violet 400nm  ————————–
                self.vLine400 = pg.InfiniteLine(angle=90, movable=False) # crée une ligne inifinie
                self.vLine400.setPen(pg.mkPen(255,0,255)) # couleur de la ligne
                self.plotwidget.addItem(self.vLine400, ignoreBounds=True) # ajoute la ligne au graphique – fonction ViewBox accessible depuis PlotItem
                self.vLine400.setPos(400)
                # ————————– ligne rouge 700 nm ————————–
                self.vLine700 = pg.InfiniteLine(angle=90, movable=False) # crée une ligne inifinie
                self.vLine700.setPen(pg.mkPen(255,0,0)) # couleur de la ligne
                self.plotwidget.addItem(self.vLine700, ignoreBounds=True) # ajoute la ligne au graphique – fonction ViewBox accessible depuis PlotItem
                self.vLine700.setPos(700)

                # ————————– ligne IRA – 1400nm ————————–
                self.vLine1400 = pg.InfiniteLine(angle=90, movable=False) # crée une ligne inifinie
                self.vLine1400.setPen(pg.mkPen(127,0,0)) # couleur de la ligne
                self.plotwidget.addItem(self.vLine1400, ignoreBounds=True) # ajoute la ligne au graphique – fonction ViewBox accessible depuis PlotItem
                self.vLine1400.setPos(1400)

                # ————————– ligne IRB – 3000nm ————————–
                self.vLine3000 = pg.InfiniteLine(angle=90, movable=False) # crée une ligne inifinie
                self.vLine3000.setPen(pg.mkPen(64,0,0)) # couleur de la ligne
                self.plotwidget.addItem(self.vLine3000, ignoreBounds=True) # ajoute la ligne au graphique – fonction ViewBox accessible depuis PlotItem
                self.vLine3000.setPos(3000)

        # les fonctions appelées, utilisées par les signaux
        def horizontalSliderValueChanged(self, valeur): # fonction appelée si changement valeur slider – reçoit la valeur courante
                print(« Slider H modifié : valeur = «  + str(valeur))

                self.T=valeur
                print self.T

                self.y=(2*self.h*self.c**2/(self.x*1e-9)**5) *(pow(e,self.h*self.c/(self.x*1e-9*self.kb*self.T))1)**1
                #self.y=(pow(self.x,3)/pow(e,self.x)-1)

                print(self.y) # debug – affiche les valeurs y
                print(« max= »+str(numpy.amax(self.y))) # extrait la valeur maxi

                index=numpy.argmax(self.y) # indice du max…
                print(« indice max= »+str(index)) # affiche l’index valeur maxi
                print(« longueur onde max= »+str(self.x[index])+ « nm ») # extrait la long onde du pic maxi

                self.plot1.setData(self.x,self.y) # met à jour les données

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

 

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
  • Faire défiler le slider entraîne la modification de la courbe.
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é.