Son et vidéo avec PyQt
Introduction
Il est de plus en plus fréquemment nécessaire de développer des applications qui intègrent des flux audio ou vidéo. La manipulation du son et de l’image et son intégration dans une application ne sont pas toujours aisées, et PyQt a l’immense avantage de disposer d’un sous-module dédié : QMultimedia.
La documentation de QMultimedia est disponible ici : https://doc.qt.io/qt-5/qmultimedia.html
L’idée directrice du présent chapitre est donc d’apprendre à lire, mais aussi d’enregistrer, de manipuler et finalement de travailler avec le son ou la vidéo, en PyQt.
Le son dans PyQt
1. Jouer un simple son
Une telle fonctionnalité peut avoir de multiples utilisations, de l’illustration sonore à l’émission d’un son particulier sur une action donnée, afin d’améliorer l’ergonomie. Les sons peuvent jouer un rôle important par rapport à l’enjeu de l’accessibilité de l’application, par exemple en direction des personnes malvoyantes. Ainsi, le survol d’un widget avec la souris peut impliquer l’émission d’un son indiquant la nature du widget survolé. Ce type de disposition est en général optionnel et activable dans les préférences du logiciel.
Ici, nous allons associer un son de basse au clic sur le bouton de l’interface.
On commence, comme d’habitude, par déclarer nos imports. Pour cet exemple, nous utilisons une méthode de déclaration différente avec la syntaxe suivante :
from ... import ... as
Cela donne les lignes suivantes :
import sys
from PyQt5 import QtMultimedia as qtmm
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtCore as qtc
On définit ensuite la classe de notre bouton en la faisant hériter de la classe QPushButton. On définit dans cette classe un lecteur de son avec QSoundEffect. Le son émis est passé en paramètre (paramètre nommé wav). Enfin, on associe sur l’évènement click du bouton le fait de jouer effectivement ce son.
class BoutonSon(qtw.QPushButton):
def __init__(self, wav, *args, **kwargs):
super().__init__(*args, **kwargs)
self.wav = wav
self.player = qtmm.QSoundEffect()
self.player.setSource(qtc.QUrl.fromLocalFile(wav))
self.clicked.connect(self.player.play)
Il nous reste alors à définir la fenêtre de notre application en lui adjoignant le bouton défini précédemment.
class FenetrePrincipal(qtw.QMainWindow):
def __init__(self):
super().__init__() ...
La vidéo dans PyQt
1. Introduction
Le but de cette partie est d’effectuer une rapide synthèse des possibilités offertes par PyQt en matière de vidéo. Le traitement de la vidéo est en définitive symétrique à ce qui se pratique avec le son. On retrouve ainsi la classe QMediaPlayer. À cette classe s’ajoutent deux autres classes : QCamera et QMediaRecorder, cette dernière étant particulièrement dédiée à l’enregistrement vidéo.
Les documentations Qt en ligne de chacune de ces deux classes sont disponibles ici :
https://doc.qt.io/qt-5/qcamera.html
https://doc.qt.io/qt-5/qmediarecorder.html
On utilise également la classe QCameraViewFinder qui permet de diffuser facilement un retour vidéo.
https://doc.qt.io/qt-5/qcameraviewfinder.html
Procédons à présent au développement d’une petite application impliquant la vidéo. L’idée sera de pouvoir afficher un flux vidéo issu de la webcam et d’offrir en plus la fonctionnalité de copie d’écran.
2. Application de capture d’image depuis un flux vidéo
a. Vérification de la présence d’une caméra
Comme pour le son, il s’agit en premier lieu d’évaluer ce qui est possible en matière de vidéo au niveau du matériel de l’utilisateur. On réalise donc le script suivant de manière à savoir s’il y a des caméras connectées à l’ordinateur, ainsi que les différents codecs disponibles sur la machine courante.
from PyQt5.QtCore import *
from PyQt5.QtMultimedia import *
app = QCoreApplication([])
for une_camera in QCameraInfo.availableCameras():
print('Mes caméras : ', une_camera.deviceName())
camera = QCamera(une_camera)
r = QMediaRecorder(camera)
print('\tCodecs audios : ', r.supportedAudioCodecs())
print('\tCodecs vidéos : ', r.supportedVideoCodecs())
print('\tRésolutions : ', r.supportedResolutions())
print('\tTypes (container) : ', r.supportedContainers()) ...