Cours:Oscillogbf
Voici un principe pour réaliser une interface graphique en python avec la librairie Qt pour dialoguer avec différents équipements (oscilloscope/gbf/...)
python et Qt
Mise en place avec QtCreator
- créer un nouveau projet :
- Application (Qt for Python) Window UI
- pas d'importance pour PySide2 ou PySide6, on va remplacer après
- choisir la version Python 3.x.x sans Virtual Environnement
- ouvrir le fichier widget.py
- remplacer par :
import sys
# communication avec le matériel de mesure
import pyvisa
# modules QT que l'on souhaite utiliser
# à compléter en fonction des besoins
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QApplication, QWidget, QFileDialog, QPushButton, QDial, QAbstractSlider
from PySide2.QtUiTools import QUiLoader
if __name__ == "__main__":
# chargement du fichier form.ui
# qui correspond à l'interface graphique
loader = QUiLoader()
app = QtWidgets.QApplication(sys.argv)
window = loader.load("form.ui", None)
window.show()
# initialisation de la librairie pyVisa
materiel=pyvisa.ResourceManager()
# print(materiel.list_resources())
# exemple pour se connecter sur un appareil par IP
# gbf=materiel.open_resource('TCPIP::192.168.0.236::INSTR')
sys.exit(app.exec_())
Signal/Slot avec PySide2
- dans un 1er temps il faut créer un objet dans l'interface graphique, par exemple "monWidgetBouton" de type QPushButton
- ensuite on connect le signal du widget sur la fonction que l'on souhaite :
window.monWidgetBouton.clicked.connect(maFonction)
- Ce qui donne :
import sys # communication avec le matériel de mesure import pyvisa # modules QT que l'on souhaite utiliser # à compléter en fonction des besoins from PySide2 import QtCore, QtGui, QtWidgets from PySide2.QtWidgets import QApplication, QWidget, QFileDialog, QPushButton, QDial, QAbstractSlider from PySide2.QtUiTools import QUiLoader # fonction qui sera exécutée lors d'un clic def maFonction(): print("clic !") if __name__ == "__main__": # chargement du fichier form.ui # qui correspond à l'interface graphique loader = QUiLoader() app = QtWidgets.QApplication(sys.argv) window = loader.load("form.ui", None) window.show() # associer une action à un QPushButton window.monWidgetBouton.clicked.connect(maFonction) # initialisation de la librairie pyVisa materiel=pyvisa.ResourceManager() # print(materiel.list_resources()) # exemple pour se connecter sur un appareil par IP # gbf=materiel.open_resource('TCPIP::192.168.0.236::INSTR') sys.exit(app.exec_())
exemple pour différents widgets
- QPushButton
- window.monWidgetBouton.clicked.connect(maFonction)
- QDial/QScrollBar/QSlider (hérite de QAbstractSlider)
- window.monWidgetDial.valueChanged.connect(getValue)
def getValue(value): print(f"position du slider : {value}")
- window.monWidgetDial.setSliderPosition(0)
- window.monWidgetDial.valueChanged.connect(getValue)
- QLabel
- window.monWidgetLabel.setText("message")
réaliser une action périodique
https://doc.qt.io/qtforpython-5/PySide2/QtCore/QTimer.html
from PySide2 import QtCore
from PySide2.QtCore import QTimer
def actionPeriodique():
print("tic")
if __name__ == "__main__":
timer = QTimer()
timer.timeout.connect(actionPeriodique)
timer.start(500)
utilisation de matplotlib
- ajouter un container dans l'interface graphique
- ajouter un QWidget qu'on nommera par exemple graphWidget
- ajouter un objet dans ce QWidget (par exemple un QPushButton)
- sélectionner le QWidget
- définir le "lay out" pour cet objet (clic droit/lay out/vertical
- supprimer le QPushButton, il permet juste de pouvoir définir un "lay out"
- la partie python
- inclure les librairies :
import matplotlib import matplotlib.pyplot as matPlotPython from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure
- on crée une figure:
maFigure = matPlotPython.figure()
- et une représentation de cette figure:
monCanvas = FigureCanvas(maFigure)
- on ajoute ce canvas dans l'interface à l'intérieur du container créé précédemment(QWidget par ex):
window.zoneWidget.layout().addWidget(monCanvas)
- on peut ensuite utiliser cette "figure"
- par exemple on ajoute un repère:
repere = maFigure.add_subplot(1,1,1)
- et on trace une courbe dans ce repère:
repere.plot([0,1,2,3,4], [10,1,20,3,40])
- par exemple on ajoute un repère:
- inclure les librairies :
ce qui donne qqch du genre :
import sys
import time
# communication avec le matériel de mesure
import pyvisa
# modules QT que l'on souhaite utiliser
# à compléter en fonction des besoins
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QApplication, QWidget, QFileDialog, QPushButton, QDial, QAbstractSlider, QLabel, QLayout
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import Qt, QTimer, QRect, QPoint
from PySide2.QtGui import QPainter
import matplotlib
import matplotlib.pyplot as matPlotPython
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
matplotlib.use('Qt5Agg')
if __name__ == "__main__":
# chargement du fichier form.ui
# qui correspond à l'interface graphique
loader = QUiLoader()
app = QtWidgets.QApplication(sys.argv)
window = loader.load("form.ui", None)
window.show()
# on crée un nouvel objet de type figure
maFigure = matPlotPython.figure()
# le canvas correspond à une représentation de la figure
monCanvas = FigureCanvas(maFigure)
# on ajoute ce widget dans une zoneWidget
# !! attention, il faut définir un layout pour cette zone dans QtDesigner !!
window.zoneWidget.layout().addWidget(monCanvas)
# on ajoute 1 ou plusieurs repères sur le graphe
# https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.subplot.html
repere = maFigure.add_subplot(1,1,1)
# on place des points sur ce repère
repere.plot([0,1,2,3,4], [10,1,20,3,40])
sys.exit(app.exec_())
créer son widget
il faut modifier la méthode paintEvent pour "dessiner" son widget
import sys
import time
# communication avec le matériel de mesure
import pyvisa
# modules QT que l'on souhaite utiliser
# à compléter en fonction des besoins
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QApplication, QWidget, QFileDialog, QPushButton, QDial, QAbstractSlider, QLabel, QLayout
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import Qt, QTimer, QRect, QPoint
from PySide2.QtGui import QPainter
class WidgetPerso(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
def paintEvent(self, paintEvent):
painter = QPainter()
painter.begin(self)
rect = QRect(QPoint(0, 0),QPoint(100, 100))
painter.fillRect(rect, Qt.GlobalColor.white)
painter.end()
if __name__ == "__main__":
# chargement du fichier form.ui
# qui correspond à l'interface graphique
loader = QUiLoader()
app = QtWidgets.QApplication(sys.argv)
window = loader.load("form.ui", None)
window.show()
# on crée un nouvel objet de type WidgetPerso
monWdiget = WidgetPerso()
# on ajoute ce widget dans un container (Widget ou Frame)
# !! attention, il faut définir un layout pour ce container dans QtDesigner : !!
# ajouter un objet dans le container, changer le layout, puis supprimer l'objet
window.zoneWidget.layout().addWidget(monWdiget)
sys.exit(app.exec_())
dialoguer avec les appareils
initialisation
- il faut initialiser la librairie
materiel=pyvisa.ResourceManager()
- se connecter sur un équipement
gbf=materiel.open_resource('TCPIP::192.168.0.236::INSTR')
- on peut ensuite :
- envoyer des commandes à l'équipement :
gbf.write(":OUTP1 ON")
- demander une valeur à l'équipement :
gbf.query("*IDN?")
- envoyer des commandes à l'équipement :
- Exemple :
materiel=pyvisa.ResourceManager() gbf =materiel.open_resource('TCPIP::192.168.0.236::INSTR') gbf2 =materiel.open_resource('USB0::6833::1603::DG8A231301537::0::INSTR') oscillo =materiel.open_resource('TCPIP::192.168.0.73::INSTR') print(gbf.query("*IDN?")) print(oscillo.query("*IDN?"))
commandes génériques
Les commandes suivantes devraient être valables pour tous les équipements :
commande | paramètre | description |
---|---|---|
*CLS | clear : initialise la communication | |
*IDN? | retourne l'identifiant de l'appareil | |
*OPC? | operation complete : indique si l'opération précédente est terminée (1) ou non (0) |
gbf DG822
Le fichier suivant contient l'ensemble des commandes pour le Rigol DG822 : dg822.pdf
Voici quelques commandes dont vous pourrez avoir besoin :
commande | description | exemple |
---|---|---|
:OUTPx ON/1/OFF/0 | activer la sortie | gbf.write(":OUTP1 ON") |
:SOURx:FREQ value | modifier la fréquence de la sortie | gbf.write(":SOUR1:FREQ") |
:SOURx:APPL:SIN freq,amp,offset,phase | générer un sinus à l'amplitude, offset, déphasage et fréquence souhaitée | gbf.write(":SOUR1:APPL:SIN 100,3,2,1") |
:SOURx:APPL:PULS freq,amp,offset,phase | générer un signal carré à l'amplitude, offset, déphasage et fréquence souhaitée | gbf.write(":SOUR1:APPL:SIN 100,3,2,0") |
:SOURx:APPL:SQU freq,amp,offset,phase | générer un signal PWM à l'amplitude, offset, déphasage et fréquence souhaitée | gbf.write(":SOUR1:APPL:PULS 100,5,2.5,0") |
:SOURx:PULS:DCYC duty | modifie le rapport cyclique d'un signal PWM | gbf.write(":SOUR1:PULS:DCYC 45") |
Exemple :
import sys
# communication avec le matériel de mesure
import pyvisa
# modules QT que l'on souhaite utiliser
# à compléter en fonction des besoins
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QApplication, QWidget, QFileDialog, QPushButton, QDial, QAbstractSlider
from PySide2.QtUiTools import QUiLoader
# fonction qui sera exécutée lors d'un clic
def maFonction():
print("clic !")
gbf.write(":OUTP1 ON")
gbf.write(":SOUR1:APPL:PULS 1000,5,2.5,0")
# fonction qui sera exécutée lors de la modification du slider
def getValue(value):
print(f"position du slider : {value}")
gbf.write(f":SOUR1:PULS:DCYC {value}")
if __name__ == "__main__":
# chargement du fichier form.ui
# qui correspond à l'interface graphique
loader = QUiLoader()
app = QtWidgets.QApplication(sys.argv)
window = loader.load("form.ui", None)
window.show()
# associer une action à un QPushButton
window.monWidgetBouton.clicked.connect(maFonction)
# associer une action à un QDial (hérite de QAbstractSlider)
window.monWidgetDial.valueChanged.connect(getValue)
# initialisation de la librairie pyVisa
materiel=pyvisa.ResourceManager()
# print(materiel.list_resources())
# exemple pour se connecter sur un appareil par IP
gbf =materiel.open_resource('TCPIP::192.168.0.236::INSTR')
#oscillo=materiel.open_resource('TCPIP::192.168.0.73::INSTR')
print(gbf.query("*IDN?"))
#print(oscillo.query("*IDN?"))
sys.exit(app.exec_())
oscillo DS1104Z
Media:MSO_DS1000Z_ProgrammingGuide_EN.pdf
Voici quelques commandes dont vous pourrez avoir besoin :
commande | description | exemple |
---|---|---|
:RUN :STOP :AUT :SING |
run / stop / autoset / single | oscillo.write(":STOP") |
:CHANnelx:DISPlay ON/1/OFF/0 | enable/disable channel | oscillo.write(":CHAN1:DISP ON") |
:CHANnel<n>:COUPling AC/DC/GND | oscillo.write("") | |
:CHANnel<n>:PROBe gain | réglage du gain de la sonde (à modifier avant scale et offset) | oscillo.write("") |
:CHANnel<n>:SCALe | calibre de tension (à modifier avant l'offset) | oscillo.write("") |
:CHANnel<n>:OFFSet | oscillo.write("") | |
:TIMebase[:MAIN]:SCALe | base de temps en s | oscillo.write(":TIMebase:MAIN:SCALe 0.001") |
NORMal|SINGle | oscillo.write("") | |
:TRIGger:EDGe:SOURce CHANx | oscillo.write("") | |
:MEASure:ITEM? PDUTy,CHANnel1 | oscillo.write("") | |
oscillo.write("") |
Exemple :
oscillo.write(":CHANnel1:DISPlay ON")
oscillo.write(":CHANnel2:DISPlay OFF")
oscillo.write(":CHANnel3:DISPlay OFF")
oscillo.write(":CHANnel4:DISPlay OFF")
oscillo.write(":CHANnel1:COUPling DC")
oscillo.write(":CHANnel1:PROBe 1")
oscillo.write(":CHANnel1:SCALe 1")
oscillo.write(":CHANnel1:OFFSet -2")
oscillo.write(":TIMebase:MAIN:SCALe 0.001")
oscillo.write(":TRIGger:SWEep NORMal")
oscillo.write(":TRIGger:EDGe:SOURce CHAN1")
duty=int(float(oscillo.query(":MEASure:ITEM? PDUTy,CHANnel1"))*100)
print(duty)
multimètre DM3058
liens
- Media:Dg822_programming.pdf
- Media:MSO_DS1000Z_ProgrammingGuide_EN.pdf
- https://pyvisa.readthedocs.io/en/latest/introduction/communication.html
- https://connect.ed-diamond.com/GNU-Linux-Magazine/glmf-124/controle-d-instruments-scientifiques-les-protocoles-gpib-vxi11-et-usbtmc
- https://iosignal.fi/wp-content/uploads/DG800_ProgrammingGuide_EN.pdf
- https://github.com/lxi-tools/lxi-tools
- https://www.pythonguis.com/tutorials/pyside-plotting-matplotlib/