View  Edit  Attributes  History  Attach  Print  Search

PYQTLAB

PyQt Lab' : Graphiques Math : Pyqtgraph : Créer/chargerharger/ajouter/enregistrer données dans un fichier texte et afficher une courbe dans un graphique Pyqtgraph.

Par X. HINAULT - Mai 2013

Ce que l'on va faire ici

  • Afficher une courbe à partir de données chargées ou enregistrées à partir/dans un fichier texte. Les données sont à saisir ligne à ligne au format x,y + saut de ligne.

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>720</width>
    <height>418</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>PyQt + pyqtgraph : Affichage courbe à partir fichier texte</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="QLineEdit" name="lineEditChemin">
   <property name="geometry">
    <rect>
     <x>5</x>
     <y>385</y>
     <width>421</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QTextEdit" name="textEdit">
   <property name="geometry">
    <rect>
     <x>490</x>
     <y>10</y>
     <width>216</width>
     <height>361</height>
    </rect>
   </property>
   <property name="font">
    <font>
     <stylestrategy>PreferAntialias</stylestrategy>
    </font>
   </property>
   <property name="lineWrapMode">
    <enum>QTextEdit::NoWrap</enum>
   </property>
  </widget>
  <widget class="QPushButton" name="pushButtonEnregistrer">
   <property name="geometry">
    <rect>
     <x>620</x>
     <y>380</y>
     <width>86</width>
     <height>27</height>
    </rect>
   </property>
   <property name="text">
    <string>Enregistrer</string>
   </property>
  </widget>
  <widget class="QPushButton" name="pushButtonNouveau">
   <property name="geometry">
    <rect>
     <x>435</x>
     <y>380</y>
     <width>86</width>
     <height>27</height>
    </rect>
   </property>
   <property name="text">
    <string>Nouveau</string>
   </property>
  </widget>
  <widget class="QPushButton" name="pushButtonOuvrir">
   <property name="geometry">
    <rect>
     <x>530</x>
     <y>380</y>
     <width>85</width>
     <height>27</height>
    </rect>
   </property>
   <property name="text">
    <string>Ouvrir</string>
   </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: Wed Jun  5 15:19:35 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(720, 418)
        self.graph = PlotWidget(Form)
        self.graph.setGeometry(QtCore.QRect(5, 10, 480, 360))
        self.graph.setObjectName(_fromUtf8("graph"))
        self.lineEditChemin = QtGui.QLineEdit(Form)
        self.lineEditChemin.setGeometry(QtCore.QRect(5, 385, 421, 23))
        self.lineEditChemin.setObjectName(_fromUtf8("lineEditChemin"))
        self.textEdit = QtGui.QTextEdit(Form)
        self.textEdit.setGeometry(QtCore.QRect(490, 10, 216, 361))
        font = QtGui.QFont()
        font.setStyleStrategy(QtGui.QFont.PreferAntialias)
        self.textEdit.setFont(font)
        self.textEdit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
        self.textEdit.setObjectName(_fromUtf8("textEdit"))
        self.pushButtonEnregistrer = QtGui.QPushButton(Form)
        self.pushButtonEnregistrer.setGeometry(QtCore.QRect(620, 380, 86, 27))
        self.pushButtonEnregistrer.setObjectName(_fromUtf8("pushButtonEnregistrer"))
        self.pushButtonNouveau = QtGui.QPushButton(Form)
        self.pushButtonNouveau.setGeometry(QtCore.QRect(435, 380, 86, 27))
        self.pushButtonNouveau.setObjectName(_fromUtf8("pushButtonNouveau"))
        self.pushButtonOuvrir = QtGui.QPushButton(Form)
        self.pushButtonOuvrir.setGeometry(QtCore.QRect(530, 380, 85, 27))
        self.pushButtonOuvrir.setObjectName(_fromUtf8("pushButtonOuvrir"))

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

    def retranslateUi(self, Form):
        Form.setWindowTitle(QtGui.QApplication.translate("Form", "PyQt + pyqtgraph : Affichage courbe à partir fichier texte", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButtonEnregistrer.setText(QtGui.QApplication.translate("Form", "Enregistrer", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButtonNouveau.setText(QtGui.QApplication.translate("Form", "Nouveau", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButtonOuvrir.setText(QtGui.QApplication.translate("Form", "Ouvrir", 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_fichier_save_load 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.pushButtonOuvrir, SIGNAL("clicked()"), self.pushButtonOuvrirClicked)  # connecte le signal Clicked de l'objet à l'appel de la fonction voulue
                self.connect(self.pushButtonEnregistrer, SIGNAL("clicked()"), self.pushButtonEnregistrerClicked) # connecte le signal Clicked de l'objet bouton à l'appel de la fonction voulue
                self.connect(self.pushButtonNouveau, SIGNAL("clicked()"), self.pushButtonNouveauClicked) # connecte le signal Clicked de l'objet bouton à 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('X', units='unit', **labelStyle) # label de l'axe
                self.graph.getAxis('left').setLabel('Y', 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=-1
                #self.maxY=1           
                #self.graph.setYRange(self.minY,self.maxY) # 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

                #-- affichage de la courbe vide --
                self.courbe=self.graph.plot(pen=(0,0,255)) # avec couleur

                print ("Saisir les donnees ligne à ligne sous la forme x,y")

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

        def pushButtonOuvrirClicked(self):
                print("Bouton <Sélectionner Fichier> appuyé")

                # ouvre fichier en tenant compte du chemin déjà saisi dans le champ
                if self.lineEditChemin.text()=="":
                        self.filename=QFileDialog.getOpenFileName(self, 'Ouvrir fichier', os.getenv('HOME')) # ouvre l'interface fichier - home par défaut
                        #self.filename=QFileDialog.getOpenFileName(self, 'Ouvrir fichier', QDir.currentPath()) # ouvre l'interface fichier - chemin courant par défaut
                else:
                        info=QFileInfo(self.lineEditChemin.text()) # définit un objet pour manipuler info sur fichier à partir chaine champ
                        print info.absoluteFilePath() # debug  
                        self.filename=QFileDialog.getOpenFileName(self, 'Ouvrir fichier', info.absoluteFilePath()) # ouvre l'interface fichier - à partir chemin

                print(self.filename) # affiche le chemin obtenu dans la console
                self.lineEditChemin.setText(self.filename) # affiche le chemin obtenu dans le champ texte


                #-- ouverture du fichier Ui et récupération du contenu
                myFile=open(self.filename,"r") # ouvre le fichier en lecture
                myFileContent=myFile.read() # lit le contenu du fichier
                myFile.close() # ferme le fichier - tant que le fichier reste ouvert, il est inacessible à d'autres ressources

                self.textEdit.setText(myFileContent) # copie le contenu dans la zone texte

                # extraction des valeurs à partir du fichier
                # numpy.loadtxt(fname, dtype=<type 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0)
                data=np.loadtxt(str(self.filename), delimiter=",") # on attend des données séparées par , et un saut de ligne après chaque donnée x,y
                print data

                self.x =data[:,0] # extrait 1ère colonne
                print self.x

                self.y=data[:,1] # extrait 2ème colonne
                print self.y

                self.courbe.setData(self.x, self.y) # met à jour la courbe




        def pushButtonNouveauClicked(self):
                print("Bouton NOUVEAU appuyé")

                # ouvre fichier en tenant compte du chemin déjà saisi dans le champ
                if self.lineEditChemin.text()=="":
                        self.filename=QFileDialog.getSaveFileName(self, 'Nouveau fichier', os.getenv('HOME')) # ouvre l'interface fichier - home par défaut
                        #self.filename=QFileDialog.getOpenFileName(self, 'Ouvrir fichier', QDir.currentPath()) # ouvre l'interface fichier - chemin courant par défaut
                else:
                        info=QFileInfo(self.lineEditChemin.text()) # définit un objet pour manipuler info sur fichier à partir chaine champ
                        print info.absoluteFilePath() # debug  
                        self.filename=QFileDialog.getSaveFileName(self, 'Nouveau fichier', info.absoluteFilePath()) # ouvre l'interface fichier - à partir chemin

                print(self.filename)
                self.lineEditChemin.setText(self.filename)


        def pushButtonEnregistrerClicked(self):        
                print("Bouton <ENREGISTRE> appuyé")                    

                if self.lineEditChemin.text()!="":
                        #self.myFile = open(self.filename, 'a') # ouverture du fichier en mode écriture append
                        self.myFile = open(self.filename, 'w') # ouverture du fichier en mode écriture write - efface contenu existant
                        # open est une fonction du langage python : http://docs.python.org/2/library/functions.html#open
                        # mode peut-être r, w, a (append)              
                        self.myFile.write(str(self.textEdit.toPlainText())) # écrit les données dans le fichier        
                        self.myFile.close() # ferme le fichier         

                        # extraction des valeurs à partir du fichier
                        # numpy.loadtxt(fname, dtype=<type 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0)
                        data=np.loadtxt(str(self.filename), delimiter=",") # on attend des données séparées par , et un saut de ligne après chaque donnée x,y
                        print data

                        self.x =data[:,0] # extrait 1ère colonne
                        print self.x

                        self.y=data[:,1] # extrait 2ème colonne
                        print self.y

                        self.courbe.setData(self.x, self.y) # met à jour la courbe


        # --- 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
  • La molette de souris assure le zoom
  • Créer un nouveau fichier
  • Saisir des valeurs ligne à ligne au format x,y
  • Clic sur enregistrer : la courbe doit s'afficher.