【问题标题】:QVideoWidget content isn't grabed from widgetQVideoWidget 内容未从小部件中获取
【发布时间】:2021-07-08 22:32:01
【问题描述】:

我创建了一个简单的媒体播放器。我想添加对显示的视频进行快照的功能。为此,我使用“self.videoWidget.grab()”函数。但似乎grab() 不能正常工作,因为我得到了一张作为wiget 背景颜色的图片,而不是快照。如果我将 'self.videoWidget.grab()' 替换为 'snapshot = self.grab()' 我得到了小部件的快照,但上面没有 videoWidget 内容(添加了图片)。我去抛出类似的问题,但一无所获。我是 PyQt5 的新手,所以我希望解决方案是显而易见的,但我一个人找不到。

from PyQt5.QtWidgets import QPushButton, QStyle, QVBoxLayout, QWidget, QFileDialog, QLabel, QSlider, QHBoxLayout
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent
from PyQt5.QtCore import QUrl, Qt

class MediaWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Media widget")
        self.initUi()
        self.show()

    def initUi(self):
        # Create media player
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.videoWidget = QVideoWidget()
        self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)

        # Open button configuration
        openButton = QPushButton("Open video")
        openButton.setToolTip("Open video file")
        openButton.setStatusTip("Open video file")
        openButton.setFixedHeight(24)
        openButton.clicked.connect(self.openFile)

        # Snapshot button configuration
        self.snapshotButton = QPushButton("Get snapshot")
        self.snapshotButton.setEnabled(False)
        self.snapshotButton.setShortcut("Ctrl+S")
        self.snapshotButton.setToolTip("Get snapshot (Ctrl+S)")
        self.snapshotButton.setFixedHeight(24)
        self.snapshotButton.clicked.connect(self.getSnapshot)

        # Play button configuration
        self.playButton = QPushButton()
        self.playButton.setEnabled(False)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
        self.playButton.clicked.connect(self.play)

        # Play button configuration
        self.videoSlider = QSlider(Qt.Horizontal)
        self.videoSlider.setRange(0, 0)
        self.videoSlider.sliderMoved.connect(self.setPosition)

        # Create layouts to place inside widget
        contentLayout = QVBoxLayout()
        controlsLayout = QHBoxLayout()
        controlsLayout.addWidget(self.playButton)
        controlsLayout.addWidget(self.snapshotButton)
        controlsLayout.addWidget(self.videoSlider)
        contentLayout.addWidget(self.videoWidget)
        contentLayout.addLayout(controlsLayout)
        contentLayout.addWidget(openButton)
        self.setLayout(contentLayout)

    def openFile(self):
        fileName = QFileDialog.getOpenFileName(self, "Open video", "/home")[0]
        if fileName != '':
            self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(fileName)))
            self.playButton.setEnabled(True)
            self.snapshotButton.setEnabled(True)

    def getSnapshot(self):
        snapshot = self.videoWidget.grab()
        snapshot.save("TestFileName", "jpg")

    def play(self):
        if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
            self.mediaPlayer.pause()
        else:
            self.mediaPlayer.play()

    def mediaStateChanged(self, state):
        if state == QMediaPlayer.PlayingState:
            self.playButton.setIcon(
                    self.style().standardIcon(QStyle.SP_MediaPause))
        else:
            self.playButton.setIcon(
                    self.style().standardIcon(QStyle.SP_MediaPlay))

    def durationChanged(self, duration):
        self.videoSlider.setRange(0, duration)

    def positionChanged(self, position):
        self.videoSlider.setValue(position)

    def setPosition(self, position):
        self.mediaPlayer.setPosition(position) 

如何抓取窗口

窗口中实际发生了什么

【问题讨论】:

    标签: python pyqt5 snapshot qmediaplayer qvideowidget


    【解决方案1】:

    一种可能的解决方案是实现一个QAbstractVideoSurface,它显示最后一帧:

    class SnapshotVideoSurface(QAbstractVideoSurface):
        def __init__(self, parent=None):
            super().__init__(parent)
            self._current_frame = QImage()
    
        @property
        def current_frame(self):
            return self._current_frame
    
        def supportedPixelFormats(self, handleType=QAbstractVideoBuffer.NoHandle):
            formats = [QVideoFrame.PixelFormat()]
            if handleType == QAbstractVideoBuffer.NoHandle:
                for f in [
                    QVideoFrame.Format_RGB32,
                    QVideoFrame.Format_ARGB32,
                    QVideoFrame.Format_ARGB32_Premultiplied,
                    QVideoFrame.Format_RGB565,
                    QVideoFrame.Format_RGB555,
                ]:
                    formats.append(f)
            return formats
    
        def present(self, frame):
            self._current_frame = frame.image()
            return True
    

    然后

    def initUi(self):
        self.snapshotVideoSurface = SnapshotVideoSurface(self)
        # Create media player
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.videoWidget = QVideoWidget()
        # self.mediaPlayer.setVideoOutput(self.videoWidget)
        self.mediaPlayer.setVideoOutput(
            [self.videoWidget.videoSurface(), self.snapshotVideoSurface]
        )
        self.mediaPlayer.durationChanged.connect(self.durationChanged)
        self.mediaPlayer.positionChanged.connect(self.positionChanged)
        self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
        # ...
    
    def getSnapshot(self):
        image = self.snapshotVideoSurface.current_frame
        if not image.isNull():
            image.save("TestFileName", "jpg")
    

    【讨论】:

    • 谢谢,您的回答有帮助!
    • 我目前正在将 PySide2 用于 mensioned 媒体播放器项目,我不能再使用下面的代码:self.mediaPlayer.setVideoOutput( [self.videoWidget.videoSurface(), self.snapshotVideoSurface] ) 。因为 PySide2 不支持 videoWidget.videoSurface()。您知道如何修改我的代码以使其再次工作吗?谢谢!
    • @HannaNezrivnanna 我没有用 pyside2 测试过代码,但是检查了 PySide2 的 QVideoWidget 文档,它确实有一个 videoSurface() 方法,你使用的是最新版本的 pyside2 吗?如果您有问题,那么我建议您使用 MRE 创建一个新帖子(注意:您在帖子中提供的代码不是 MRE)
    【解决方案2】:

    upwoted 的答案对我有用,但一段时间后我找到了解决所描述问题的替代方法。我希望它会帮助某人。我使用 QGraphicsVideoItem 而不是 QAbstractVideoSurface 实现。它帮助我摆脱了多余的 QAbstractVideoSurface 实现并简化了代码。 这是播放器的创建:

    def initUi(self):
        # Create widget to display video frames
        self.graphicsView = QGraphicsView()
        self.scene = QGraphicsScene(self, self.graphicsView)
        self.videoItem = QGraphicsVideoItem()
        self.graphicsView.setScene(self.scene)
        self.graphicsView.scene().addItem(self.videoItem)
    
        # Create media player
        self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
        self.mediaPlayer.setVideoOutput(self.videoItem)
        self.mediaPlayer.durationChanged.connect(self.mediaPlayerDurationChanged)
        self.mediaPlayer.positionChanged.connect(self.mediaPlayerPositionChanged)
        self.mediaPlayer.stateChanged.connect(self.mediaPlayerStateChanged)
        #...
    

    快照就是这样完成的:

        def getSnapshot(self):
        snapshot = self.graphicsView.grab()
    

    【讨论】:

      【解决方案3】:

      另一种对我有用的选择。它有助于避免实施 QAbstractVideoSurface 并保持源代码干净。

      QPixmap.grabWindow(self.videoWidget.winId()).save("Test4", 'jpg')

      【讨论】:

        猜你喜欢
        • 2012-04-06
        • 2015-01-28
        • 2022-12-08
        • 1970-01-01
        • 2019-01-09
        • 1970-01-01
        • 1970-01-01
        • 2016-05-11
        • 1970-01-01
        相关资源
        最近更新 更多