I'm trying to run QVideoWidget and matplotlib animation together,
but when I run this py, QVideoWidget is not working.
I got QVideoWidget code and matplotlib animation code then sum those code together in below code.
If I close the matplotlib animation Window, then the QVideoWidget is working.
I wonder why this happen and solve this problem
Thank you
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
from PyQt5.QtCore import QDir, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel,
QPushButton, QSizePolicy, QSlider, QStyle, QVBoxLayout, QWidget)
from PyQt5.QtWidgets import QMainWindow,QWidget, QPushButton, QAction
from PyQt5.QtGui import QIcon
import sys
from PyQt5.QtWidgets import (QApplication, QCheckBox, QGridLayout, QGroupBox,
QMenu, QPushButton, QRadioButton, QVBoxLayout, QWidget)
# Parameters
x_len = 200 # Number of points to display
y_range = [10, 40] # Range of possible Y values to display
# Create figure for plotting
fig = plt.figure(facecolor='white')
ax = fig.add_subplot(2, 1, 1)
bx = fig.add_subplot(2, 1, 2)
xs = list(range(0, 200))
ys = [0] * x_len
ax.set_ylim(y_range)
bx.set_ylim(y_range)
# Initialize communication with TMP102
# When everything done, release the capture
# Create a blank line. We will update the line in animate
line, = ax.plot(xs, ys)
line2, = bx.plot(xs, ys)
# Add labels
plt.title('TMP102 Temperature over Time')
plt.xlabel('Samples')
plt.ylabel('Temperature (deg C)')
class VideoWindow(QMainWindow):
def __init__(self, parent=None):
super(VideoWindow, self).__init__(parent)
self.setWindowTitle("PyQt Video Player Widget Example - pythonprogramminglanguage.com")
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
videoWidget = QVideoWidget()
self.playButton = QPushButton()
self.playButton.setEnabled(False)
self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playButton.clicked.connect(self.play)
self.positionSlider = QSlider(Qt.Horizontal)
self.positionSlider.setRange(0, 0)
self.positionSlider.sliderMoved.connect(self.setPosition)
self.errorLabel = QLabel()
self.errorLabel.setSizePolicy(QSizePolicy.Preferred,
QSizePolicy.Maximum)
# Create new action
openAction = QAction(QIcon('open.png'), '&Open', self)
openAction.setShortcut('Ctrl+O')
openAction.setStatusTip('Open movie')
openAction.triggered.connect(self.openFile)
# Create exit action
exitAction = QAction(QIcon('exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.exitCall)
# Create menu bar and add action
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
#fileMenu.addAction(newAction)
fileMenu.addAction(openAction)
fileMenu.addAction(exitAction)
# Create a widget for window contents
wid = QWidget(self)
self.setCentralWidget(wid)
# Create layouts to place inside widget
controlLayout = QHBoxLayout()
controlLayout.setContentsMargins(0, 0, 0, 0)
controlLayout.addWidget(self.playButton)
controlLayout.addWidget(self.positionSlider)
layout = QVBoxLayout()
layout.addWidget(videoWidget)
layout.addLayout(controlLayout)
layout.addWidget(self.errorLabel)
# Set widget to contain window contents
wid.setLayout(layout)
self.mediaPlayer.setVideoOutput(videoWidget)
self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
self.mediaPlayer.positionChanged.connect(self.positionChanged)
self.mediaPlayer.durationChanged.connect(self.durationChanged)
self.mediaPlayer.error.connect(self.handleError)
def openFile(self):
fileName, _ = QFileDialog.getOpenFileName(self, "Open Movie",
QDir.homePath())
if fileName != '':
self.mediaPlayer.setMedia(
QMediaContent(QUrl.fromLocalFile(fileName)))
self.playButton.setEnabled(True)
def exitCall(self):
sys.exit(app.exec_())
def play(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediaStateChanged(self, state):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.playButton.setIcon(
self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playButton.setIcon(
self.style().standardIcon(QStyle.SP_MediaPlay))
def positionChanged(self, position):
self.positionSlider.setValue(position)
def durationChanged(self, duration):
self.positionSlider.setRange(0, duration)
def setPosition(self, position):
self.mediaPlayer.setPosition(position)
def handleError(self):
self.playButton.setEnabled(False)
self.errorLabel.setText("Error: " + self.mediaPlayer.errorString())
def animate(i, ys):
# Read temperature (Celsius) from TMP102
temp_c = random.randint(15, 35)
# Add y to list
ys.append(temp_c)
# Limit y list to set number of items
ys = ys[-x_len:]
# Update line with new Y values
line.set_ydata(ys)
line2.set_ydata(ys)
return line, line2,
ani = animation.FuncAnimation(fig, animate, fargs=(ys,),
interval=50,
blit=True)
# This function is called periodically from FuncAnimation
# Set up plot to call animate() function periodically
if __name__ == '__main__':
app = QApplication(sys.argv)
player = VideoWindow()
player.resize(640, 480)
player.show()
plt.show()
sys.exit(app.exec_())
Every GUI needs an event loop to be able to update itself and attend user events, OS events, etc. And in the case of matplotlib it is called when using plt.show()
and in the case of PyQt5 when calling app.exec_()
. With your current code the event loop of matplotlib is blocking PyQt, so you can not interact with the window created by PyQt5.
The solution is simple, matplotlib supports several backends including PyQt5, so the solution is to use it, in the following link you can find an example.
Considering the above, the following code implements the solution:
import random
import matplotlib
# Make sure that we are using QT5
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtGui, QtWidgets, QtMultimedia, QtMultimediaWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.animation as animation
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
# https://stackoverflow.com/a/6981055/6622587
ax = fig.add_subplot(111) # The big subplot
self.ax = fig.add_subplot(211)
self.bx = fig.add_subplot(212)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
ax.set_title('TMP102 Temperature over Time')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.tick_params(labelcolor='w', top=False, bottom=False, left=False, right=False)
ax.set_xlabel('Samples')
ax.set_ylabel('Temperature (deg C)')
self.x_len = 200 # Number of points to display
self.y_range = [10, 40] # Range of possible Y values to display
self.xs = list(range(0, 200))
self.ys = [0 for _ in range(self.x_len)]
self.anim = animation.FuncAnimation(fig, self.animate, init_func=self.init, interval=50,blit=True)
def init(self):
y_range = [10, 40]
self.ax.set_ylim(*y_range)
self.bx.set_ylim(*y_range)
self.line, = self.ax.plot(self.xs, self.ys)
self.line2, = self.bx.plot(self.xs, self.ys)
return self.line, self.line2
def animate(self, i):
temp_c = random.randint(15, 35)
self.ys.append(temp_c)
self.ys = self.ys[-self.x_len:]
self.line.set_ydata(self.ys)
self.line2.set_ydata(self.ys)
return self.line, self.line2
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(ApplicationWindow, self).__init__(parent)
main_widget = QtWidgets.QWidget()
l = QtWidgets.QVBoxLayout(main_widget)
sc = MyMplCanvas(main_widget, width=5, height=4, dpi=100)
l.addWidget(sc)
self.setCentralWidget(main_widget)
class VideoWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(VideoWindow, self).__init__(parent)
self.setWindowTitle("PyQt Video Player Widget Example - pythonprogramminglanguage.com")
self.mediaPlayer = QtMultimedia.QMediaPlayer(self, QtMultimedia.QMediaPlayer.VideoSurface)
videoWidget = QtMultimediaWidgets.QVideoWidget()
self.playButton = QtWidgets.QPushButton()
self.playButton.setEnabled(False)
self.playButton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_MediaPlay))
self.playButton.clicked.connect(self.play)
self.positionSlider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.positionSlider.setRange(0, 0)
self.positionSlider.sliderMoved.connect(self.setPosition)
self.errorLabel =QtWidgets.QLabel()
self.errorLabel.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
# Create new action
openAction = QtWidgets.QAction(QtGui.QIcon('open.png'), '&Open', self)
openAction.setShortcut('Ctrl+O')
openAction.setStatusTip('Open movie')
openAction.triggered.connect(self.openFile)
# Create exit action
exitAction = QtWidgets.QAction(QtGui.QIcon('exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.exitCall)
# Create menu bar and add action
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
#fileMenu.addAction(newAction)
fileMenu.addAction(openAction)
fileMenu.addAction(exitAction)
# Create a widget for window contents
wid = QtWidgets.QWidget()
self.setCentralWidget(wid)
# Create layouts to place inside widget
controlLayout = QtWidgets.QHBoxLayout()
controlLayout.setContentsMargins(0, 0, 0, 0)
controlLayout.addWidget(self.playButton)
controlLayout.addWidget(self.positionSlider)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(videoWidget)
layout.addLayout(controlLayout)
layout.addWidget(self.errorLabel)
# Set widget to contain window contents
wid.setLayout(layout)
self.mediaPlayer.setVideoOutput(videoWidget)
self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
self.mediaPlayer.positionChanged.connect(self.positionChanged)
self.mediaPlayer.durationChanged.connect(self.durationChanged)
self.mediaPlayer.error.connect(self.handleError)
def openFile(self):
fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open Movie", QtCore.QDir.homePath())
if fileName:
media = QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(fileName))
self.mediaPlayer.setMedia(media)
self.playButton.setEnabled(True)
def exitCall(self):
QtWidgets.QApplication.quit()
def play(self):
if self.mediaPlayer.state() == QtMultimedia.QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediaStateChanged(self, state):
if self.mediaPlayer.state() == QtMultimedia.QMediaPlayer.PlayingState:
self.playButton.setIcon(
self.style().standardIcon(QtWidgets.QStyle.SP_MediaPause))
else:
self.playButton.setIcon(
self.style().standardIcon(QtWidgets.QStyle.SP_MediaPlay))
def positionChanged(self, position):
self.positionSlider.setValue(position)
def durationChanged(self, duration):
self.positionSlider.setRange(0, duration)
def setPosition(self, position):
self.mediaPlayer.setPosition(position)
def handleError(self):
self.playButton.setEnabled(False)
self.errorLabel.setText("Error: " + self.mediaPlayer.errorString())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
player = VideoWindow()
player.resize(640, 480)
player.show()
w = ApplicationWindow()
w.show()
sys.exit(app.exec_())
来源:https://stackoverflow.com/questions/52307237/how-can-run-both-of-matplotlib-and-pyqt5-video-with-python