QVideoWidget 内容不是从小部件中获取的
QVideoWidget content isn't grabed from widget
我创建了一个简单的媒体播放器。我想添加为显示的视频制作快照的功能。为此,我使用 'self.videoWidget.grab()' 函数。但似乎 grab() 不能正常工作,因为我得到的不是快照,而是一张颜色为 wiget 背景的图片。如果我用 'snapshot = self.grab()' 替换 'self.videoWidget.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)
如何window被抓取
window
中实际发生了什么
一个可能的解决方案是实施显示最后一帧的 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")
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()
另一种适合我的选择。它有助于避免实现 QAbstractVideoSurface 并保持源清洁。
QPixmap.grabWindow(self.videoWidget.winId()).save("Test4", 'jpg')
我创建了一个简单的媒体播放器。我想添加为显示的视频制作快照的功能。为此,我使用 'self.videoWidget.grab()' 函数。但似乎 grab() 不能正常工作,因为我得到的不是快照,而是一张颜色为 wiget 背景的图片。如果我用 'snapshot = self.grab()' 替换 'self.videoWidget.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)
如何window被抓取
window
中实际发生了什么一个可能的解决方案是实施显示最后一帧的 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")
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()
另一种适合我的选择。它有助于避免实现 QAbstractVideoSurface 并保持源清洁。
QPixmap.grabWindow(self.videoWidget.winId()).save("Test4", 'jpg')