如何使用python运行matplotlib和PyQt5 Video [英] How can run both of matplotlib and PyQt5 Video with python
问题描述
我试图同时运行QVideoWidget和matplotlib动画,
I'm trying to run QVideoWidget and matplotlib animation together,
但是当我运行此py时,QVideoWidget无法正常工作。
but when I run this py, QVideoWidget is not working.
我得到了QVideoWidget代码和matplotlib动画代码,然后将这些代码加到下面的代码中。
I got QVideoWidget code and matplotlib animation code then sum those code together in below code.
如果我关闭matplotlib动画窗口,然后QVideoWidget开始工作。
If I close the matplotlib animation Window, then the QVideoWidget is working.
我想知道为什么会这样并解决此问题
I wonder why this happen and solve this problem
谢谢
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_())
推荐答案
每个GUI都需要一个事件循环才能更新自身并参加用户事件,操作系统事件等。在使用matplotlib的情况下,使用 plt.show()
会被调用,在使用PyQt5的情况下,调用 app时会被调用.exec _()
。使用您当前的代码,matplotlib的事件循环将阻止PyQt,因此您无法与PyQt5创建的窗口进行交互。
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.
该解决方案很简单,matplotlib支持多个后端,包括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_())
这篇关于如何使用python运行matplotlib和PyQt5 Video的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!