I have a PyQt Window with widgets that change. I want to make a video of it. I found this Answer to be very useful, however it is does not seem to be possible to use subprocess PIPE as a target in QtGui.QPixmap's save-method. I have the feeling that I should use the native QtProcess for this kind of work, but I don't know how I can PIPE the images and I can't see the errors because I can't see the standard outpur/error either. What I want to do is something like this:
from PyQt4 import QtGui, QtCore
import random
app = QtGui.QApplication([])
win = QtGui.QWidget()
layout = QtGui.QGridLayout()
win.setLayout(layout)
#picture frame
scene = QtGui.QGraphicsScene()
canvas = QtGui.QGraphicsView(scene)
layout.addWidget(canvas,0,0)
# start button
def run():
# set pen
pen = QtGui.QPen(QtCore.Qt.red)
size = canvas.size()
# start seperate process
process = QtCore.QProcess(app)
process.start('ffmpeg',['-y', '-f', 'image2pipe', '-vcodec', 'mjpeg', '-r', '24', '-i', '-', '-vcodec', 'mpeg4', '-qscale', '5', 'video.avi'])
for i in range(100):
x = random.randint(1, size.width()-1)
y = random.randint(1, size.height()-1)
scene.addLine(x,y,x,y, pen=pen)
QtGui.QPixmap.grabWidget(win).save(process, "jpeg")
but_run = QtGui.QPushButton("Go!")
but_run.clicked.connect(run)
layout.addWidget(but_run,1,0)
win.show()
app.exec_()
So I figured it out by myself. There are only a few commands to add:
process.setProcessChannelMode(process.ForwardedChannels)
will redirect error and normal output of the child process to the main process. This allows you to see what ffmpeg does or not does.
EDIT: This is actually not needed.
process.setOpenMode(process.WriteOnly)
this will Open the Input channel of the process.
I changed the file format to png because it looks better. JPG compression is not suited for large mono colored areas.
process.closeWriteChannel()
Will Close the Input Channel. This is how ffmpeg knows there is no more data coming in.
And at last, call process.terminate()
to close the process.
Here is the fully functioning code:
from PyQt4 import QtGui, QtCore
import random
app = QtGui.QApplication([])
win = QtGui.QWidget()
layout = QtGui.QGridLayout()
win.setLayout(layout)
#picture frame
scene = QtGui.QGraphicsScene()
canvas = QtGui.QGraphicsView(scene)
layout.addWidget(canvas,0,0)
# start button
def run():
# set pen
pen = QtGui.QPen(QtCore.Qt.red)
size = canvas.size()
# start seperate process
process = QtCore.QProcess(app)
process.setProcessChannelMode(process.ForwardedChannels)
#process.setOpenMode(process.WriteOnly)
process.start('ffmpeg',['-y', '-vcodec', 'png', '-i', '-', '-vcodec', 'mpeg4', '-qscale', '5', '-r', '24', 'video.avi',"-loglevel", "debug"])
for i in range(100):
x = random.randint(1, size.width()-1)
y = random.randint(1, size.height()-1)
scene.addLine(x,y,x,y, pen=pen)
QtGui.QPixmap.grabWidget(win).save(process, "png")
process.closeWriteChannel()
process.terminate()
but_run = QtGui.QPushButton("Go!")
but_run.clicked.connect(run)
layout.addWidget(but_run,1,0)
win.show()
app.exec_()