1

我正在尝试同时运行 QVideoWidget 和 matplotlib 动画,

但是当我运行这个 py 时,QVideoWidget 不起作用。

我得到了 QVideoWidget 代码和 matplotlib 动画代码,然后将这些代码汇总在下面的代码中。

如果我关闭 matplotlib 动画窗口,则 QVideoWidget 正在工作。

我想知道为什么会发生这种情况并解决这个问题

谢谢

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_())
4

1 回答 1

2

每个 GUI 都需要一个事件循环来更新自身并参与用户事件、操作系统事件等。在 matplotlib 的情况下,它在使用时被调用,plt.show()而在 PyQt5 的情况下,在调用app.exec_(). 使用您当前的代码,matplotlib 的事件循环阻塞了 PyQt,因此您无法与 PyQt5 创建的窗口进行交互。

解决方案很简单,matplotlib 支持包括 PyQt5 在内的多个后端,所以解决方案就是使用它,在下面的链接中你可以找到一个示例。

考虑到上述情况,以下代码实现了解决方案:

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_())
于 2018-09-13T06:54:31.970 回答