View  Edit  Attributes  History  Attach  Print  Search

PYQTLAB

PyQt Lab' : Graphiques Math : Pyqtgraph : Interagir avec une courbe d'un graphique pyqtgraph à l'aide de sliders.

Par X. HINAULT - Juin 2013

Ce que l'on va faire ici

  • Ici, 2 sliders (widget graphique de réglage linéaire) permettent d'interagir avec une courbe d'un graphique pyqtgraph en modifiant un coefficient sur l'axe des X pour l'un et l'axe des Y pour l'autre.

Pré-requis

  • python 2.7
  • pyqt4.x
  • pyqtgraph

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>542</width>
    <height>457</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>PyQt + pyqtgraph : Modifier une courbe avec des sliders</string>
  </property>
  <widget class="PlotWidget" name="graph">
   <property name="geometry">
    <rect>
     <x>5</x>
     <y>10</y>
     <width>480</width>
     <height>360</height>
    </rect>
   </property>
  </widget>
  <widget class="QSlider" name="horizontalSlider">
   <property name="geometry">
    <rect>
     <x>40</x>
     <y>385</y>
     <width>441</width>
     <height>26</height>
    </rect>
   </property>
   <property name="maximum">
    <number>10</number>
   </property>
   <property name="value">
    <number>5</number>
   </property>
   <property name="orientation">
    <enum>Qt::Horizontal</enum>
   </property>
   <property name="tickPosition">
    <enum>QSlider::TicksAbove</enum>
   </property>
  </widget>
  <widget class="QSlider" name="verticalSlider">
   <property name="geometry">
    <rect>
     <x>500</x>
     <y>10</y>
     <width>26</width>
     <height>366</height>
    </rect>
   </property>
   <property name="maximum">
    <number>10</number>
   </property>
   <property name="value">
    <number>5</number>
   </property>
   <property name="orientation">
    <enum>Qt::Vertical</enum>
   </property>
   <property name="tickPosition">
    <enum>QSlider::TicksAbove</enum>
   </property>
  </widget>
 </widget>
 <customwidgets>
  <customwidget>
   <class>PlotWidget</class>
   <extends>QGraphicsView</extends>
   <header>pyqtgraph</header>
  </customwidget>
 </customwidgets>
 <resources/>
 <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 May 31 15:18:02 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(542, 457)
        self.graph = PlotWidget(Form)
        self.graph.setGeometry(QtCore.QRect(5, 10, 480, 360))
        self.graph.setObjectName(_fromUtf8("graph"))
        self.horizontalSlider = QtGui.QSlider(Form)
        self.horizontalSlider.setGeometry(QtCore.QRect(40, 385, 441, 26))
        self.horizontalSlider.setMaximum(10)
        self.horizontalSlider.setProperty("value", 5)
        self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal)
        self.horizontalSlider.setTickPosition(QtGui.QSlider.TicksAbove)
        self.horizontalSlider.setObjectName(_fromUtf8("horizontalSlider"))
        self.verticalSlider = QtGui.QSlider(Form)
        self.verticalSlider.setGeometry(QtCore.QRect(500, 10, 26, 366))
        self.verticalSlider.setMaximum(10)
        self.verticalSlider.setProperty("value", 5)
        self.verticalSlider.setOrientation(QtCore.Qt.Vertical)
        self.verticalSlider.setTickPosition(QtGui.QSlider.TicksAbove)
        self.verticalSlider.setObjectName(_fromUtf8("verticalSlider"))

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

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "PyQt + pyqtgraph : Modifier une courbe avec des sliders", 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

from tuto_pyqt_pyqtgraph_courbe_simple_slidersx2 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

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

                self.connect(self.horizontalSlider, SIGNAL("valueChanged(int)"), self.horizontalSliderValueChanged)
                self.connect(self.verticalSlider, SIGNAL("valueChanged(int)"), self.verticalSliderValueChanged)

                # --- 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('X', units='unit', **labelStyle) # label de l'axe
                self.graph.getAxis('left').setLabel('Y', units='unit', **labelStyle) # label de l'axe

                # adaptation échelle axes
                self.graph.enableAutoRange(axis=pg.ViewBox.YAxis, enable=False) # fonction plotItem : désactive autoscale Y
                self.graph.setYRange(-10,10) # fonction plotItem : fixe échelle des Y

                # 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 --
                self.x = np.arange(0.0, 361.0, 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=sin(x)
                self.y=self.verticalSlider.value()*np.sin(np.radians(self.x*self.horizontalSlider.value()))# crée un tableau de valeur y basé sur x
                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

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

        def horizontalSliderValueChanged(self, valeur): # fonction appelée si changement valeur slider - reçoit la valeur courante
                print("Slider modifié : valeur = " + str(valeur))

                # reclacule y
                self.y=self.verticalSlider.value()*np.sin(np.radians(self.x*self.horizontalSlider.value()))# crée un tableau de valeur y basé sur x

                #-- affichage de la courbe --
                self.courbe.setData(self.x,self.y) # actualise données = retrace  même courbe actualisée


        def verticalSliderValueChanged(self, valeur): # fonction appelée si changement valeur slider - reçoit la valeur courante
                print("Slider modifié : valeur = " + str(valeur))

                # reclacule y
                self.y=self.verticalSlider.value()*np.sin(np.radians(self.x*self.horizontalSlider.value()))# crée un tableau de valeur y basé sur x

                #-- affichage de la courbe --
                self.courbe.setData(self.x,self.y) # actualise données = retrace  même courbe actualisée

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

        # --- 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
  • Régler les sliders pour modifier l'affichage de la courbe.
  • La molette de souris assure le zoom