I am creating a program to play videos and then process them. I am able to play the videos with QMediaPlayer. How do I access specific frames as images or something similar. My end goal would be to format the video into a 4-d tensor of size [Num of frames, width_of_video, height_of_video, channels].
Here is the code that loads my video.:
self.clear_layout(self.vlayout)
videoItem = QVideoWidget()
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.mediaPlayer.durationChanged.connect(self.update_duration)
self.mediaPlayer.positionChanged.connect(self.update_slider_position)
self.vlayout.addWidget(videoItem)
self.mediaPlayer.setVideoOutput(videoItem)
local = QUrl.fromLocalFile(self.video_paths)
media = QMediaContent(local)
self.mediaPlayer.setMedia(media)
self.play_video()
Here is a working example that I converted to Python from the C++ version available in this question: How to save a frame using QMediaPlayer?
import sys
import uuid
import PyQt5
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt, QObject, QUrl, QRect, pyqtSignal, QPoint
from PyQt5.QtGui import QPainter, QImage
from PyQt5.QtWidgets import QWidget, QApplication, QMainWindow, QGridLayout, QToolBar, QAction
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAbstractVideoBuffer, \
QVideoFrame, QVideoSurfaceFormat, QAbstractVideoSurface
from PyQt5.QtMultimediaWidgets import QVideoWidget
class VideoFrameGrabber(QAbstractVideoSurface):
frameAvailable = pyqtSignal(QImage)
def __init__(self, widget: QWidget, parent: QObject):
super().__init__(parent)
self.widget = widget
def supportedPixelFormats(self, handleType):
return [QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555, QVideoFrame.Format_ARGB8565_Premultiplied,
QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGR32,
QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGR555,
QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_AYUV444,
QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_YUV444,
QVideoFrame.Format_YUV420P, QVideoFrame.Format_YV12, QVideoFrame.Format_UYVY,
QVideoFrame.Format_YUYV, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21,
QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3,
QVideoFrame.Format_IMC4, QVideoFrame.Format_Y8, QVideoFrame.Format_Y16,
QVideoFrame.Format_Jpeg, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_AdobeDng]
def isFormatSupported(self, format):
imageFormat = QVideoFrame.imageFormatFromPixelFormat(format.pixelFormat())
size = format.frameSize()
return imageFormat != QImage.Format_Invalid and not size.isEmpty() and \
format.handleType() == QAbstractVideoBuffer.NoHandle
def start(self, format: QVideoSurfaceFormat):
imageFormat = QVideoFrame.imageFormatFromPixelFormat(format.pixelFormat())
size = format.frameSize()
if imageFormat != QImage.Format_Invalid and not size.isEmpty():
self.imageFormat = imageFormat
self.imageSize = size
self.sourceRect = format.viewport()
super().start(format)
self.widget.updateGeometry()
self.updateVideoRect()
return True
else:
return False
def stop(self):
self.currentFrame = QVideoFrame()
self.targetRect = QRect()
super().stop()
self.widget.update()
def present(self, frame):
if frame.isValid():
cloneFrame = QVideoFrame(frame)
cloneFrame.map(QAbstractVideoBuffer.ReadOnly)
image = QImage(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(),
QVideoFrame.imageFormatFromPixelFormat(cloneFrame.pixelFormat()))
self.frameAvailable.emit(image) # this is very important
cloneFrame.unmap()
if self.surfaceFormat().pixelFormat() != frame.pixelFormat() or \
self.surfaceFormat().frameSize() != frame.size():
self.setError(QAbstractVideoSurface.IncorrectFormatError)
self.stop()
return False
else:
self.currentFrame = frame
self.widget.repaint(self.targetRect)
return True
def updateVideoRect(self):
size = self.surfaceFormat().sizeHint()
size.scale(self.widget.size().boundedTo(size), Qt.KeepAspectRatio)
self.targetRect = QRect(QPoint(0, 0), size)
self.targetRect.moveCenter(self.widget.rect().center())
def paint(self, painter):
if self.currentFrame.map(QAbstractVideoBuffer.ReadOnly):
oldTransform = self.painter.transform()
if self.surfaceFormat().scanLineDirection() == QVideoSurfaceFormat.BottomToTop:
self.painter.scale(1, -1)
self.painter.translate(0, -self.widget.height())
image = QImage(self.currentFrame.bits(), self.currentFrame.width(), self.currentFrame.height(),
self.currentFrame.bytesPerLine(), self.imageFormat)
self.painter.drawImage(self.targetRect, image, self.sourceRect)
self.painter.setTransform(oldTransform)
self.currentFrame.unmap()
class App(QApplication):
def __init__(self, sys_argv):
super().__init__(sys_argv)
# Show main window
self.view = QMainWindow()
self.centralWidget = QWidget(self.view)
self.gridLayout = QGridLayout(self.centralWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.setSpacing(0)
self.video_item = QVideoWidget()
self.gridLayout.addWidget(self.video_item)
self.view.setCentralWidget(self.centralWidget)
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.grabber = VideoFrameGrabber(self.video_item, self)
self.mediaPlayer.setVideoOutput(self.grabber)
self.grabber.frameAvailable.connect(self.process_frame)
self.mediaPlayer.durationChanged.connect(self.update_duration)
self.mediaPlayer.positionChanged.connect(self.update_slider_position)
local = QUrl.fromLocalFile('c:/temp/lorem.mp4')
media = QMediaContent(local)
self.mediaPlayer.setMedia(media)
self.mediaPlayer.play()
self.view.show()
def process_frame(self, image):
# Save image here
image.save('c:/temp/{}.jpg'.format(str(uuid.uuid4())))
def update_duration(self):
pass
def update_slider_position(self):
pass
if __name__ == '__main__':
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
if hasattr(QtCore.Qt, 'AA_EnableHighDpiScaling'):
PyQt5.QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
if hasattr(QtCore.Qt, 'AA_UseHighDpiPixmaps'):
PyQt5.QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)
app = App(sys.argv)
app.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
sys.excepthook = except_hook
sys.exit(app.exec_())
Related
I need to make a new window for video output. It should have some buttons (which I'll add later), and a video output. I have the main window with audio playback capability, and I can make a video output on the main window, not the second one.
The video check using magic.python executes videopop, which opens a new window (VideoWindow).
Code:
file: main.py
import sys
import magic
# bude obsahovat menu (File, Edit, About,...), Queue, v menu bude historie
from PyQt5.QtCore import QUrl, Qt
from PyQt5.QtGui import QPalette, QColor, QIcon
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QFileDialog, QWidget, QLabel, QVBoxLayout, QHBoxLayout, \
QPushButton, QStyle, QSlider
from ui import UI
class QueueWin(QMainWindow, UI):
def __init__(self, *args, **kwargs):
super(QueueWin, self).__init__(*args, **kwargs)
self.ui(self)
self.setWindowTitle('MPx') # nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) # nastavi ikonku
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) # nastavi barvu okna
self.setPalette(p) # aplikuje barvu
self.open_file_act.triggered.connect(self.open_file)
#self.history_act.triggered.connect(self.open_history)
self.history_act.triggered.connect(self.videopop)
#self.open_act.triggered.connect(self.clr_history)
self.mediaPlayer.stateChanged.connect(self.mediastate_changed) #mení ikonku na tlačítku play
self.mediaPlayer.positionChanged.connect(self.update_position)
self.mediaPlayer.durationChanged.connect(self.duration_changed)
self.slider.valueChanged.connect(self.mediaPlayer.setPosition)
self.audioSlider.valueChanged.connect(self.mediaPlayer.setVolume)
def open_history (self):
h = History(self)
h.show()
def videopop(self):
v = VideoWindow(self)
v.show()
def credits(self):
w = Credits(self)
w.show()
def open_file(self):
filename, _ = QFileDialog.getOpenFileName(self, "Open Video")
if filename != '':
self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(filename)))
self.playBtn.setEnabled(True)
self.mediaPlayer.play()
mime = magic.Magic(mime=True) # check zda je soubor video
videocheck = mime.from_file(filename)
if videocheck.find('video') != -1:
self.videopop()
print('it is video')
def position_changed(self, position):
self.slider.setValue(position)
def duration_changed(self,duration):
self.slider.setRange(0, duration)
def update_position(self, position):
self.slider.blockSignals(True)
self.slider.setValue(position)
self.slider.blockSignals(False)
def play_video(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
def mediastate_changed(self, state):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
class VideoWindow(QMainWindow, UI):
def __init__(self, *args, **kwargs):
super(VideoWindow, self).__init__(*args, **kwargs)
self.videoui(self)
self.setWindowTitle('Video') #nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) #nastavi ikonku
# http://www.iconseeker.com/search-icon/isabi/media-player-5.html <-- odkaz na ikonku
self.setGeometry(710, 290, 500, 500) #xMistoOtevreni, yMistoOtevreni, xVelikost, yVelikost
self.setMinimumSize(400, 400) # xMinimalniVelikost, yMinimalniVelikost
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) #nastavi barvu okna
self.setPalette(p) #aplikuje barvu
class History(QMainWindow):
def __init__(self, parent=None):
super(History, self).__init__(parent)
self.setWindowTitle('Historie') # nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) # nastavi ikonku
# http://www.iconseeker.com/search-icon/isabi/media-player-5.html <-- odkaz na ikonku
self.setGeometry(710, 290, 500, 200) # xMistoOtevreni, yMistoOtevreni, xVelikost, yVelikost
self.setMinimumSize(200, 200) # xMinimalniVelikost, yMinimalniVelikost
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) # nastavi barvu okna
self.setPalette(p) # aplikuje barvu
class Credits(QMainWindow):
def __init__(self, parent=None):
super(Credits, self).__init__(parent)
self.setWindowTitle('O programu') # nastavi title
self.setWindowIcon(QIcon('media-player-5.ico')) # nastavi ikonku
# http://www.iconseeker.com/search-icon/isabi/media-player-5.html <-- odkaz na ikonku
self.setGeometry(710, 290, 500, 200) # xMistoOtevreni, yMistoOtevreni, xVelikost, yVelikost
self.setMinimumSize(200, 200) # xMinimalniVelikost, yMinimalniVelikost
p = self.palette()
p.setColor(QPalette.Window, QColor(52, 51, 51)) # nastavi barvu okna
self.setPalette(p) # aplikuje barvu
self.label = QLabel('Autor: Tomáš Gabriel, 3.B OAUH<br>Napsáno pomocí Pythonu a PyQt5', self)
self.label.setStyleSheet("color: yellow")
self.label.adjustSize()
if __name__ == '__main__':
app = QApplication(sys.argv)
mainWin = QueueWin()
mainWin.show()
sys.exit(app.exec_())
file: UI.py
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtMultimedia import QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import QPushButton, QSlider, QStyle, QHBoxLayout, QVBoxLayout, QMenuBar
class UI(object):
def ui(self, QueueWin):
QueueWin.setObjectName("QueueWin")
QueueWin.resize(600, 500)
_translate = QtCore.QCoreApplication.translate
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.centralWidget = QtWidgets.QWidget(QueueWin)
#sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
#sizePolicy.setHorizontalStretch(0)
#sizePolicy.setVerticalStretch(0)
#sizePolicy.setHeightForWidth(self.centralWidget.sizePolicy().hasHeightForWidth())
#self.centralWidget.setSizePolicy(sizePolicy)
self.openFileBtn = QPushButton("Otevrit soubor...", self.centralWidget)
self.openFileBtn.clicked.connect(self.open_file)
self.playBtn = QPushButton(self.centralWidget) # vytvori tlacitko "play"
self.playBtn.setEnabled(False)
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playBtn.clicked.connect(self.play_video)
self.slider = QSlider(Qt.Horizontal, self.centralWidget) # vytvori slider
self.slider.setRange(0, 0)
self.audioSlider = QSlider(Qt.Horizontal, self.centralWidget)
self.audioSlider.setMaximum(100)
self.audioSlider.setProperty("value", 100)
QueueWin.setCentralWidget(self.centralWidget)
# self.queue = QListView(self)
# self.queue.setAcceptDrops(True)
# self.queue.setAlternatingRowColors(True)
self.hbox = QHBoxLayout(self.centralWidget) # umisti tlacitka, slidery,... do UI
self.hbox.setContentsMargins(11, 11, 11, 11)
self.hbox.addWidget(self.openFileBtn)
self.hbox.addWidget(self.playBtn)
self.hbox.addWidget(self.slider)
self.hbox.addWidget(self.audioSlider)
self.vbox = QVBoxLayout(self.centralWidget)
#self.vbox.addWidget(videowidget)
# vbox.addWidget(self.queue)
self.vbox.addLayout(self.hbox)
#self.mediaPlayer.setVideoOutput(videowidget)
#self.mediaPlayer.positionChanged.connect(self.update_position)
#self.mediaPlayer.durationChanged.connect(self.duration_changed)
self.menuBar = QMenuBar(QueueWin)
QueueWin.setMenuBar(self.menuBar)
self.open = QtWidgets.QMenu(self.menuBar)
self.open_file_act = QtWidgets.QAction(QueueWin)
self.open.addAction(self.open_file_act)
self.open_file_act.setText(_translate("QueueWin", "Otevřít..."))
self.menuBar.addAction(self.open.menuAction())
self.open.setTitle(_translate("QueueWin", "Soubor"))
self.history = QtWidgets.QMenu(self.menuBar)
self.history_act = QtWidgets.QAction(QueueWin)
self.history.addAction(self.history_act)
self.history_act.setText(_translate("QueueWin", "Otevřít historii"))
self.menuBar.addAction(self.history.menuAction())
self.history.setTitle(_translate("QueueWin", "Historie"))
self.historyClr_act = QtWidgets.QAction(QueueWin)
self.history.addAction(self.historyClr_act)
self.historyClr_act.setText(_translate("QueueWin", "Vymazat historii"))
self.historyClr_act.setShortcut('ALT+H')
#about = self.menuBar.addMenu('Autor')
#about_act = QAction('O autorovi...', self)
#about_act.setShortcut('CTRL+A')
#about_act.triggered.connect(lambda: self.credits())
#about.addAction(about_act)
QtCore.QMetaObject.connectSlotsByName(QueueWin)
def videoui(self, VideoWindow):
VideoWindow.setObjectName("QueueWin")
VideoWindow.resize(600, 500)
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.centralWidget = QtWidgets.QWidget(VideoWindow)
self.videowidget = QVideoWidget(self.centralWidget)
VideoWindow.setCentralWidget(self.centralWidget)
self.hbox = QHBoxLayout(self.centralWidget) # umisti tlacitka, slidery,... do UI
self.hbox.setContentsMargins(11, 11, 11, 11)
self.hbox.addWidget(self.videowidget)
self.vbox = QVBoxLayout(self.centralWidget)
self.vbox.addLayout(self.hbox)
QtCore.QMetaObject.connectSlotsByName(VideoWindow)
You're not setting the video output, and the QMediaPlayer in VideoWindow is completely useless (so you can remove that), while you should use the one created for QueueWin and set the video output with the QVideoWidget of the other window.
The "basic" solution would be the following:
if videocheck.find('video') != -1:
videoWindow = VideoWindow(self)
self.mediaPlayer.setVideoOutput(videoWindow.videowidget)
videoWindow.show()
Be aware, though, that your code has some important issues that would require fixing.
First of all, files generated by the pyuic utility should never, ever be manually modified (read how to properly use those files in the official guidelines about using Designer).
Then, while technically working, creating a new QMainWindow with another one as parent is not suggested; you also continuously create a new window everytime the function is called, while you should reuse any existing window. A more proper approach would be to create the child windows when creating the main one, and show them when required, in this way you can already set up the video output for the separate video widget.
Also note that you don't need to use an external module to check if the media actually has video: just use the videoAvailableChanged signal and show the video window if the argument is True (you cannot just check isVideoAvailable() right after setMedia(), as that function is asynchronous).
Finally, if you're not using any of the QMainWindow features (menu, toolbars, dock widgets, status bar), just use a basic QWidget instead.
How can I set the audio output of a QMediaPlayer to a specific output in Windows 7 and later?
This was really easy in PySide (using Phonon) but I can't find a way to do it in PySide2.
There are some related questions already, like this old but still not solved one, or this one that asks exactly what I want.
They are both in c++ and its difficult to convert it to PySide2.
The second one is answered with this code:
QMediaService *svc = player->service();
if (svc != nullptr)
{
QAudioOutputSelectorControl *out = qobject_cast<QAudioOutputSelectorControl *>
(svc->requestControl(QAudioOutputSelectorControl_iid));
if (out != nullptr)
{
out->setActiveOutput(this->ui->comboBox->currentText());
svc->releaseControl(out);
}
}
Another one with an attempt to python conversion didn't work also.
I tried to convert them to Python code, but the result was not successful.
Here is my minimal attempt:
import sys
from PySide2 import QtMultimedia
from PySide2.QtCore import QUrl, Qt
from PySide2.QtMultimedia import QMediaPlayer, QMediaContent
from PySide2.QtWidgets import (QPushButton, QSlider, QHBoxLayout, QVBoxLayout,
QFileDialog, QStyle, QApplication, QDialog, QComboBox)
class Window(QDialog):
def __init__(self):
super().__init__()
self.out_combo = QComboBox()
mode = QtMultimedia.QAudio.AudioOutput
devices = QtMultimedia.QAudioDeviceInfo.availableDevices(mode)
for item in [(dev.deviceName(), dev) for dev in devices]:
self.out_combo.addItem(item[0], item[1])
self.out_combo.currentIndexChanged.connect(self.out_device_changed)
openBtn = QPushButton('Open file')
openBtn.clicked.connect(self.open_file)
self.playBtn = QPushButton()
self.playBtn.setEnabled(False)
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
self.playBtn.clicked.connect(self.play_file)
self.slider = QSlider(Qt.Horizontal)
self.slider.setRange(0, 0)
self.slider.sliderMoved.connect(self.set_position)
hor_layout = QHBoxLayout()
hor_layout.setContentsMargins(0, 0, 0, 0)
hor_layout.addWidget(openBtn)
hor_layout.addWidget(self.playBtn)
hor_layout.addWidget(self.slider)
ver_layout = QVBoxLayout()
ver_layout.addWidget(self.out_combo)
ver_layout.addLayout(hor_layout)
self.setLayout(ver_layout)
self.player = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.player.stateChanged.connect(self.mediastate_changed)
self.player.positionChanged.connect(self.position_changed)
self.player.durationChanged.connect(self.duration_changed)
self.show()
def open_file(self):
file_name, _ = QFileDialog.getOpenFileName(self, "Open file")
if file_name != '':
self.player.setMedia(QMediaContent(QUrl.fromLocalFile(file_name)))
# self.label.setText(basename(file_name))
self.playBtn.setEnabled(True)
def play_file(self):
if self.player.state() == QMediaPlayer.PlayingState:
self.player.pause()
else:
self.player.play()
def mediastate_changed(self, state):
if self.player.state() == QMediaPlayer.PlayingState:
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
else:
self.playBtn.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
def position_changed(self, position):
self.slider.setValue(position)
def duration_changed(self, duration):
self.slider.setRange(0, duration)
def set_position(self, position):
self.player.setPosition(position)
def out_device_changed(self, idx):
device = self.out_combo.itemData(idx)
service = self.player.service()
if service:
out = service.requestControl("org.qt-project.qt.mediastreamscontrol/5.0")
if out:
out.setActiveOutput(self.out_combo.currentText())
service.releaseControl(out)
else:
print("No output found!")
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
I'm trying to play an online video using python-vlc library.
First, I was using a simple version of the player to test it out:
import vlc
#example url
url="https://player.vimeo.com/external/363536021.hd.mp4?s=6f6e87d86a49149479ebdab6c8bc421aa89f327c&profile_id=175&oauth2_token_id=57447761"
player = vlc.Instance().media_player_new()
player.set_media(vlc.Instance().media_new(url))
player.play()
while str(player.get_state()) != "State.Ended":
pass
player.stop()
It plays the while video from URL without any problems!
Now I wanted to use the more complex version with pyqt5 GUI (original source-code here):
import sys
import os.path
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QPalette, QColor
from PyQt5.QtWidgets import QMainWindow, QWidget, QFrame, QSlider, QHBoxLayout, QPushButton, \
QVBoxLayout, QAction, QFileDialog, QApplication
import vlc
class Player(QMainWindow):
def __init__(self, videoUrl, master=None):
QMainWindow.__init__(self, master)
self.setWindowTitle("Media Player")
# creating a basic vlc instance
self.instance = vlc.Instance()
# creating an empty vlc media player
self.mediaplayer = self.instance.media_player_new()
self.playableVideo = videoUrl
self.createUI()
self.isPaused = False
def createUI(self):
"""Set up the user interface, signals & slots
"""
self.widget = QWidget(self)
self.setCentralWidget(self.widget)
# In this widget, the video will be drawn
if sys.platform == "darwin": # for MacOS
from PyQt5.QtWidgets import QMacCocoaViewContainer
self.videoframe = QMacCocoaViewContainer(0)
else:
self.videoframe = QFrame()
self.palette = self.videoframe.palette()
self.palette.setColor (QPalette.Window,
QColor(0,0,0))
self.videoframe.setPalette(self.palette)
self.videoframe.setAutoFillBackground(True)
self.positionslider = QSlider(Qt.Horizontal, self)
self.positionslider.setToolTip("Position")
self.positionslider.setMaximum(5000)
self.positionslider.sliderMoved.connect(self.setPosition)
self.hbuttonbox = QHBoxLayout()
self.playbutton = QPushButton("Play")
self.hbuttonbox.addWidget(self.playbutton)
self.playbutton.clicked.connect(self.PlayPause)
self.stopbutton = QPushButton("Exit")
self.hbuttonbox.addWidget(self.stopbutton)
self.stopbutton.clicked.connect(self.Stop)
self.hbuttonbox.setAlignment(Qt.AlignHCenter)
self.vboxlayout = QVBoxLayout()
self.vboxlayout.addWidget(self.videoframe)
self.vboxlayout.addWidget(self.positionslider)
self.vboxlayout.addLayout(self.hbuttonbox)
self.widget.setLayout(self.vboxlayout)
self.timer = QTimer(self)
self.timer.setInterval(200)
self.timer.timeout.connect(self.updateUI)
def PlayPause(self):
"""Toggle play/pause status
"""
if self.mediaplayer.is_playing():
self.mediaplayer.pause()
self.playbutton.setText("Play")
self.isPaused = True
else:
if self.mediaplayer.play() == -1:
self.OpenFile()
return
self.mediaplayer.play()
self.playbutton.setText("Pause")
self.timer.start()
self.isPaused = False
def Stop(self):
"""Stop player
"""
self.mediaplayer.stop()
self.playbutton.setText("Play")
def OpenFile(self, filename=None):
"""Open a media file in a MediaPlayer
"""
if filename is None:
filename = self.playableVideo
if not filename:
return
# create the media
if sys.version < '3':
filename = unicode(filename)
self.media = self.instance.media_new(filename)
# put the media in the media player
self.mediaplayer.set_media(self.media)
# parse the metadata of the file
self.media.parse()
# set the title of the track as window title
self.setWindowTitle(self.media.get_meta(0))
# the media player has to be 'connected' to the QFrame
# (otherwise a video would be displayed in it's own window)
# this is platform specific!
# you have to give the id of the QFrame (or similar object) to
# vlc, different platforms have different functions for this
if sys.platform.startswith('linux'): # for Linux using the X Server
self.mediaplayer.set_xwindow(self.videoframe.winId())
elif sys.platform == "win32": # for Windows
self.mediaplayer.set_hwnd(self.videoframe.winId())
elif sys.platform == "darwin": # for MacOS
self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
self.PlayPause()
def setPosition(self, position):
self.mediaplayer.set_position(position / 1000.0)
def updateUI(self):
self.positionslider.setValue(self.mediaplayer.get_position() * 1000)
if not self.mediaplayer.is_playing():
self.timer.stop()
if not self.isPaused:
self.Stop()
if __name__ == "__main__":
app = QApplication(sys.argv)
player = Player("https://player.vimeo.com/external/363536021.hd.mp4?s=6f6e87d86a49149479ebdab6c8bc421aa89f327c&profile_id=175&oauth2_token_id=57447761")
player.show()
player.resize(640, 480)
if sys.argv[1:]:
player.OpenFile(sys.argv[1])
sys.exit(app.exec_())
This code keeps returning an access stream error: HTTP connection failure.
The problem is that as soon as you start playing the video it is not available since the download is not immediate but with your code you try to update the position. The solution is to update is to get the position after it started playing:
def updateUI(self):
if not self.mediaplayer.is_playing():
return
self.positionslider.setValue(self.mediaplayer.get_position() * 1000)
if not self.mediaplayer.is_playing():
self.timer.stop()
if not self.isPaused:
self.Stop()
I have a problem with copy-pasting a QGraphicsitem in a scene.
I have tried the following code but it is not working properly.
If I tried to Paste the item, the first instance it is pasting correctly. For the second instance, it is removing the first instance item and pasting the second instance.
As of now, I have tried to get the path of the item in copy action and adding it to the scene in paste action.
#pos2 is my grid position
I just want to paste the copied item for n number of time until the new item is copied. Correct me if I am doing copy-paste in the wrong way.
from PyQt5.QtCore import (QByteArray, QDataStream, QIODevice, QMimeData, QPointF, QPoint, Qt, QRect,QTimer,QLineF, QEvent,QRectF)
from PyQt5.QtGui import QColor, QDrag, QPainter, QPixmap,QFont,QFontMetrics,QBrush, QLinearGradient, QIcon, QPen, QPainterPath, QTransform,QCursor,QMouseEvent,QClipboard
from PyQt5.QtWidgets import QApplication,QGraphicsTextItem,QGraphicsItemGroup, QSizePolicy, QScrollArea, QPushButton,QLineEdit, QMainWindow,QInputDialog, QGraphicsPathItem,QDialog, QVBoxLayout,QGraphicsItem,QStatusBar,QTextEdit, QAction,QMenu, qApp,QSplitter, QButtonGroup, QToolButton, QFrame, QHBoxLayout, QGraphicsView, QGraphicsItem, QGraphicsPixmapItem, QLabel, QGraphicsScene, QWidget
class GraphicsSceneClass(QGraphicsScene):
global selectedObjType
def __init__(self, parent=None):
super(GraphicsSceneClass, self).__init__(parent)
self.setSceneRect(0, 0, 1920, 1080)
self.setItemIndexMethod(QGraphicsScene.NoIndex)
self.setBackgroundBrush(QBrush(Qt.black))
def mousePressEvent(self, event):
sampleTransform = QTransform()
objectAtMouse = self.itemAt(event.scenePos(), sampleTransform)
if objectAtMouse and event.button()== Qt.LeftButton:
objectAtMouse.setSelected(True)
elif objectAtMouse==None and event.button()==Qt.RightButton:
self.grid = self.TargPosForLine(event.scenePos(), "ForLine")
def TargPosForLine(self, position, mode):
clicked_column = int((position.y() // 16)) * 16
clicked_row = int((position.x() // 16)) * 16
if clicked_column < 0:
clicked_column = 0
if clicked_row < 0:
clicked_row = 0
if(mode == "ForRect"):
return QRect(clicked_row, clicked_column,16,16)
elif(mode == "ForLine"):
return QPointF(clicked_row,clicked_column)
class MainWindow(QMainWindow):
global selectedObjType
# global item
def __init__(self,):
super(MainWindow, self).__init__()
self.scene = GraphicsSceneClass()
MainWindow.obj = self.scene
self.view = QGraphicsView(self.scene)
self.view.setMouseTracking(True)
self.view.setRenderHint(QPainter.HighQualityAntialiasing)
self.widg = QWidget()
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.addWidget(self.view)
self.widg.setMouseTracking(True)
self.widget = QWidget()
self.widget.setLayout(self.horizontalLayout)
self.setCentralWidget(self.widget)
self.obj=None
def contextMenuEvent(self, event):
contextMenu = QMenu(self)
Cutaction = contextMenu.addAction("Cut")
Coaction = contextMenu.addAction("Copy")
Paaction = contextMenu.addAction("Paste")
Propaction = contextMenu.addAction("draw")
quitAct = contextMenu.addAction("quit")
action = contextMenu.exec_(self.mapToGlobal(event.pos()))
if action == quitAct:
self.close()
elif action == Propaction:
painterPath = QPainterPath()
painterPath.moveTo(10, 50.0)
painterPath.lineTo(50,50)
painterPath.lineTo(50,55)
painterPath.lineTo(10,55)
gradient = QLinearGradient(1, 1, 1, 5)
gradient.setColorAt(0, QColor(Qt.gray))
gradient.setColorAt(0.5, QColor(192, 192, 192, 255))
gradient.setColorAt(1, QColor(Qt.darkGray))
painterPath.closeSubpath()
objectDrop = QGraphicsPathItem()
objectDrop.setPath(painterPath)
objectDrop.setBrush(QBrush(gradient))
self.scene.addItem(objectDrop)
objectDrop.setFlag(QGraphicsItem.ItemIsSelectable)
objectDrop._type1="line"
# self.scene.addPath(painterPath)
elif action == Coaction:
self.copy()
elif action == Paaction:
self.paste()
def copy(self):
item = self.selectedItem()
self.dragObject = item
x = str(self.dragObject._type1)
if item is None:
return
if 'text' in x:
self.object = QGraphicsPixmapItem(item.pixmap())
else:
self.object = QGraphicsPathItem(item.path())
gradient = QLinearGradient(1, 1, 1, 5)
gradient.setColorAt(0, QColor(Qt.gray))
gradient.setColorAt(0.5, QColor(192, 192, 192, 255))
gradient.setColorAt(1, QColor(Qt.darkGray))
self.object.setBrush(QBrush(gradient))
self.object.setPen(QPen(Qt.NoPen))
self.object._type1 = item._type1
def paste(self):
self.scene.addItem(self.object)
self.object.setPos(self.scene.grid.x(), self.scene.grid.y())
def selectedItem(self):
items = self.scene.selectedItems()
if len(items) == 1:
return items[0]
return None
if __name__=="__main__":
import sys
app=QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
Copying a QGraphicsItem is not a trivial task, in this case what can be done is to save the most important features and recreate another object using that information. In this case, a QDataStream can be used to serialize said information that can be easily transported to another application that knows how to decode it.
import importlib
import random
from PyQt5 import QtCore, QtGui, QtWidgets
custom_mimeType = "application/x-qgraphicsitems"
def item_to_ds(it, ds):
if not isinstance(it, QtWidgets.QGraphicsItem):
return
ds.writeQString(it.__class__.__module__)
ds.writeQString(it.__class__.__name__)
ds.writeInt(it.flags())
ds << it.pos()
ds.writeFloat(it.opacity())
ds.writeFloat(it.rotation())
ds.writeFloat(it.scale())
if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
ds << it.brush() << it.pen()
if isinstance(it, QtWidgets.QGraphicsPathItem):
ds << it.path()
def ds_to_item(ds):
module_name = ds.readQString()
class_name = ds.readQString()
mod = importlib.import_module(module_name)
it = getattr(mod, class_name)()
flags = QtWidgets.QGraphicsItem.GraphicsItemFlag(ds.readInt())
pos = QtCore.QPointF()
ds >> pos
it.setFlags(flags)
it.setPos(pos)
it.setOpacity(ds.readFloat())
it.setRotation(ds.readFloat())
it.setScale(ds.readFloat())
if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
pen, brush = QtGui.QPen(), QtGui.QBrush()
ds >> brush
ds >> pen
it.setPen(pen)
it.setBrush(brush)
if isinstance(it, QtWidgets.QGraphicsPathItem):
path = QtGui.QPainterPath()
ds >> path
it.setPath(path)
return it
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
self.setScene(
QtWidgets.QGraphicsScene(QtCore.QRectF(-200, -200, 400, 400), self)
)
for _ in range(4):
path = QtGui.QPainterPath()
poly = QtGui.QPolygonF(
[
QtCore.QPointF(0, -40),
QtCore.QPointF(-38, -12),
QtCore.QPointF(-24, 32),
QtCore.QPointF(24, 32),
QtCore.QPointF(38, -12),
QtCore.QPointF(0, -40),
]
)
path.addPolygon(poly)
it = QtWidgets.QGraphicsPathItem(path)
it.setBrush(QtGui.QColor(*random.sample(range(255), 3)))
it.setPen(QtGui.QColor(*random.sample(range(255), 3)))
self.scene().addItem(it)
it.setPos(QtCore.QPointF(*random.sample(range(-100, 100), 2)))
it.setFlags(
it.flags()
| QtWidgets.QGraphicsItem.ItemIsSelectable
| QtWidgets.QGraphicsItem.ItemIsMovable
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.Copy), self, activated=self.copy_items
)
QtWidgets.QShortcut(
QtGui.QKeySequence(QtGui.QKeySequence.Paste),
self,
activated=self.paste_items,
)
#QtCore.pyqtSlot()
def copy_items(self):
mimedata = QtCore.QMimeData()
ba = QtCore.QByteArray()
ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
for it in self.scene().selectedItems():
item_to_ds(it, ds)
mimedata.setData(custom_mimeType, ba)
clipboard = QtGui.QGuiApplication.clipboard()
clipboard.setMimeData(mimedata)
#QtCore.pyqtSlot()
def paste_items(self):
pos2 = QtCore.QPointF(40, 40)
clipboard = QtGui.QGuiApplication.clipboard()
mimedata = clipboard.mimeData()
if mimedata.hasFormat(custom_mimeType):
ba = mimedata.data(custom_mimeType)
ds = QtCore.QDataStream(ba)
while not ds.atEnd():
it = ds_to_item(ds)
self.scene().addItem(it)
it.setPos(pos2)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = GraphicsView()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
Update:
import importlib
from PyQt5 import QtCore, QtGui, QtWidgets
custom_mimeType = "application/x-qgraphicsitems"
def item_to_ds(it, ds):
if not isinstance(it, QtWidgets.QGraphicsItem):
return
ds.writeQString(it.__class__.__module__)
ds.writeQString(it.__class__.__name__)
ds.writeInt(it.flags())
ds << it.pos()
ds.writeFloat(it.opacity())
ds.writeFloat(it.rotation())
ds.writeFloat(it.scale())
if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
ds << it.brush() << it.pen()
if isinstance(it, QtWidgets.QGraphicsPathItem):
ds << it.path()
def ds_to_item(ds):
module_name = ds.readQString()
class_name = ds.readQString()
mod = importlib.import_module(module_name)
it = getattr(mod, class_name)()
flags = QtWidgets.QGraphicsItem.GraphicsItemFlag(ds.readInt())
pos = QtCore.QPointF()
ds >> pos
it.setFlags(flags)
it.setPos(pos)
it.setOpacity(ds.readFloat())
it.setRotation(ds.readFloat())
it.setScale(ds.readFloat())
if isinstance(it, QtWidgets.QAbstractGraphicsShapeItem):
pen, brush = QtGui.QPen(), QtGui.QBrush()
ds >> brush
ds >> pen
it.setPen(pen)
it.setBrush(brush)
if isinstance(it, QtWidgets.QGraphicsPathItem):
path = QtGui.QPainterPath()
ds >> path
it.setPath(path)
return it
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
self.setSceneRect(0, 0, 1920, 1080)
self.setItemIndexMethod(QtWidgets.QGraphicsScene.NoIndex)
self.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.black))
def mousePressEvent(self, event):
if event.button == QtCore.Qt.LeftButton:
it = self.itemAt(event.scenePos())
if it:
it.setSelected(True)
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super().__init__(parent)
self.setScene(GraphicsScene(self))
self.setRenderHint(QtGui.QPainter.HighQualityAntialiasing)
def contextMenuEvent(self, event):
menu = QtWidgets.QMenu(self)
cut_action = menu.addAction("Cut")
copy_action = menu.addAction("Copy")
paste_action = menu.addAction("Paste")
draw_action = menu.addAction("draw")
quit_action = menu.addAction("quit")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == quit_action:
self.window().close()
elif action == draw_action:
path = QtGui.QPainterPath()
path.moveTo(-20, -2.5)
path.lineTo(20, -2.5)
path.lineTo(20, 2.5)
path.lineTo(-20, 2.5)
path.closeSubpath()
gradient = QtGui.QLinearGradient(1, 1, 1, 5)
gradient.setColorAt(0, QtGui.QColor(QtCore.Qt.gray))
gradient.setColorAt(0.5, QtGui.QColor(192, 192, 192, 255))
gradient.setColorAt(1, QtGui.QColor(QtCore.Qt.darkGray))
item = QtWidgets.QGraphicsPathItem(path)
item.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable)
item.setBrush(QtGui.QBrush(gradient))
self.scene().addItem(item)
item.setPos(self.mapToScene(event.pos()))
elif action == copy_action:
self.copy_items()
elif action == paste_action:
self.paste_items(self.mapToScene(event.pos()))
def copy_items(self):
mimedata = QtCore.QMimeData()
ba = QtCore.QByteArray()
ds = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
for it in self.scene().selectedItems():
item_to_ds(it, ds)
mimedata.setData(custom_mimeType, ba)
clipboard = QtGui.QGuiApplication.clipboard()
clipboard.setMimeData(mimedata)
def paste_items(self, pos):
clipboard = QtGui.QGuiApplication.clipboard()
mimedata = clipboard.mimeData()
if mimedata.hasFormat(custom_mimeType):
ba = mimedata.data(custom_mimeType)
ds = QtCore.QDataStream(ba)
while not ds.atEnd():
it = ds_to_item(ds)
self.scene().addItem(it)
it.setPos(pos)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self._view = GraphicsView()
self.setCentralWidget(self._view)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I'm new to PyQt and I'm trying to create a videoplayer that screenshots the current frame it retrieves. I implemented this method for grabbing the videoframes in order to take screenshots and added it to a videoplayer GUI. Here's how the GUI essentially looks like:
I created a trigger for the screenshot action in the VideoWindow class with the following code:
screenshotAction = QAction(QIcon('screenshot.png'), '&Screenshot', self)
screenshotAction.setShortcut('Ctrl+S')
screenshotAction.setStatusTip('Screenshot scenes')
screenshotAction.triggered.connect(self.screenshotCall)
I then took the implementation of the VideoFrameGrabber class and called it in the screenshot function:
def screenshotCall(self):
#Call video frame grabber
self.grabber = VideoFrameGrabber(self.videoWidget, self)
self.mediaPlayer.setVideoOutput(self.grabber)
self.grabber.frameAvailable.connect(self.process_frame)
self.mediaPlayer.setVideoOutput(self.videoWidget)
Complete code:
# PyQt5 Video player
#!/usr/bin/env python
from PyQt5.QtCore import QDir, Qt, QUrl, pyqtSignal, QPoint, QRect, QObject
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer, QVideoFrame, QAbstractVideoSurface, QAbstractVideoBuffer, QVideoSurfaceFormat
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, QPainter, QImage
import sys
import os
import os.path as osp
class VideoFrameGrabber(QAbstractVideoSurface):
frameAvailable = pyqtSignal(QImage)
def __init__(self, widget: QWidget, parent: QObject):
super().__init__(parent)
self.widget = widget
def supportedPixelFormats(self, handleType):
return [QVideoFrame.Format_ARGB32, QVideoFrame.Format_ARGB32_Premultiplied,
QVideoFrame.Format_RGB32, QVideoFrame.Format_RGB24, QVideoFrame.Format_RGB565,
QVideoFrame.Format_RGB555, QVideoFrame.Format_ARGB8565_Premultiplied,
QVideoFrame.Format_BGRA32, QVideoFrame.Format_BGRA32_Premultiplied, QVideoFrame.Format_BGR32,
QVideoFrame.Format_BGR24, QVideoFrame.Format_BGR565, QVideoFrame.Format_BGR555,
QVideoFrame.Format_BGRA5658_Premultiplied, QVideoFrame.Format_AYUV444,
QVideoFrame.Format_AYUV444_Premultiplied, QVideoFrame.Format_YUV444,
QVideoFrame.Format_YUV420P, QVideoFrame.Format_YV12, QVideoFrame.Format_UYVY,
QVideoFrame.Format_YUYV, QVideoFrame.Format_NV12, QVideoFrame.Format_NV21,
QVideoFrame.Format_IMC1, QVideoFrame.Format_IMC2, QVideoFrame.Format_IMC3,
QVideoFrame.Format_IMC4, QVideoFrame.Format_Y8, QVideoFrame.Format_Y16,
QVideoFrame.Format_Jpeg, QVideoFrame.Format_CameraRaw, QVideoFrame.Format_AdobeDng]
def isFormatSupported(self, format):
imageFormat = QVideoFrame.imageFormatFromPixelFormat(format.pixelFormat())
size = format.frameSize()
return imageFormat != QImage.Format_Invalid and not size.isEmpty() and \
format.handleType() == QAbstractVideoBuffer.NoHandle
def start(self, format: QVideoSurfaceFormat):
imageFormat = QVideoFrame.imageFormatFromPixelFormat(format.pixelFormat())
size = format.frameSize()
if imageFormat != QImage.Format_Invalid and not size.isEmpty():
self.imageFormat = imageFormat
self.imageSize = size
self.sourceRect = format.viewport()
super().start(format)
self.widget.updateGeometry()
self.updateVideoRect()
return True
else:
return False
def stop(self):
self.currentFrame = QVideoFrame()
self.targetRect = QRect()
super().stop()
self.widget.update()
def present(self, frame):
if frame.isValid():
cloneFrame = QVideoFrame(frame)
cloneFrame.map(QAbstractVideoBuffer.ReadOnly)
image = QImage(cloneFrame.bits(), cloneFrame.width(), cloneFrame.height(),
QVideoFrame.imageFormatFromPixelFormat(cloneFrame.pixelFormat()))
self.frameAvailable.emit(image) # this is very important
cloneFrame.unmap()
if self.surfaceFormat().pixelFormat() != frame.pixelFormat() or \
self.surfaceFormat().frameSize() != frame.size():
self.setError(QAbstractVideoSurface.IncorrectFormatError)
self.stop()
return False
else:
self.currentFrame = frame
self.widget.repaint(self.targetRect)
return True
def updateVideoRect(self):
size = self.surfaceFormat().sizeHint()
size.scale(self.widget.size().boundedTo(size), Qt.KeepAspectRatio)
self.targetRect = QRect(QPoint(0, 0), size)
self.targetRect.moveCenter(self.widget.rect().center())
def paint(self, painter):
if self.currentFrame.map(QAbstractVideoBuffer.ReadOnly):
oldTransform = self.painter.transform()
if self.surfaceFormat().scanLineDirection() == QVideoSurfaceFormat.BottomToTop:
self.painter.scale(1, -1)
self.painter.translate(0, -self.widget.height())
image = QImage(self.currentFrame.bits(), self.currentFrame.width(), self.currentFrame.height(),
self.currentFrame.bytesPerLine(), self.imageFormat)
self.painter.drawImage(self.targetRect, image, self.sourceRect)
self.painter.setTransform(oldTransform)
self.currentFrame.unmap()
class VideoWindow(QMainWindow):
def __init__(self, parent=None):
super(VideoWindow, self).__init__(parent)
self.setWindowTitle("PyQt Video Player Widget")
self.counter = 0
self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
self.videoWidget = QVideoWidget()
self.videoFrame = QVideoFrame()
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 video')
openAction.triggered.connect(self.openFile)
# Create exit action
exitAction = QAction(QIcon('quit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.exitCall)
screenshotAction = QAction(QIcon('screenshot.png'), '&Screenshot', self)
screenshotAction.setShortcut('Ctrl+S')
screenshotAction.setStatusTip('Screenshot scenes')
screenshotAction.triggered.connect(self.screenshotCall)
# Create menu bar and add action
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
#fileMenu.addAction(newAction)
fileMenu.addAction(openAction)
fileMenu.addAction(screenshotAction)
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(self.videoWidget)
layout.addLayout(controlLayout)
layout.addWidget(self.errorLabel)
# Set widget to contain window contents
wid.setLayout(layout)
self.mediaPlayer.setVideoOutput(self.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())
self.path = osp.dirname(str(fileName))
if fileName != '':
self.mediaPlayer.setMedia(
QMediaContent(QUrl.fromLocalFile(fileName)))
self.playButton.setEnabled(True)
def exitCall(self):
sys.exit(app.exec_())
def screenshotCall(self):
#Call video frame grabber
self.grabber = VideoFrameGrabber(self.videoWidget, self)
self.mediaPlayer.setVideoOutput(self.grabber)
self.mediaPlayer.pause()
self.grabber.frameAvailable.connect(self.process_frame)
self.errorLabel.setText("Taking a screenshot of image "+str(self.counter)+" ....")
self.mediaPlayer.play()
self.mediaPlayer.setVideoOutput(self.videoWidget)
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 process_frame(self, image):
# Save image here
filename = "screenshot" + str(self.counter).zfill(6)
self.path = 'C:/Desktop/temp'
image.save(self.path+'/{}.png'.format(str(filename)))
self.counter = self.counter+1
if __name__ == '__main__':
app = QApplication(sys.argv)
player = VideoWindow()
player.resize(720, 480)
player.show()
sys.exit(app.exec_())
However, whenever I try taking a screenshot, the video display turns black. Based from the audio, the video is still playing, but the current display remains black all throughout the duration of the video from the moment the screenshot is taken. This happens for both when the player is paused and while it is still playing. I think it's because of the line self.mediaPlayer.setVideoOutput(self.grabber), since it overwrites the current display on the screen with the grabber.
I've tried calling the videoWidget back and set it as the video output after the frame was grabbed, but it seems to overwrite the grabber and does not produce any screenshots at all.
Is there a way for me to simultaneously take screenshots and still display the video on the GUI?
solved this by calling the pause function every time the video plays, and then playing the part of the video where it was paused when the play button is pressed.
here's the code:
def screenshotCall(self):
#Call video frame grabber
self.grabber = VideoFrameGrabber(self.videoWidget, self)
self.mediaPlayer.setVideoOutput(self.grabber)
self.grabber.frameAvailable.connect(self.process_frame)
self.errorLabel.setText("Taking a screenshot of image "+str(self.counter)+" ....")
self.mediaPlayer.pause()
def play(self):
if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
self.mediaPlayer.pause()
else:
self.mediaPlayer.play()
self.mediaPlayer.setVideoOutput(self.videoWidget)