Reputation: 2478
when the button clicked ,the following code begin to run (called from the main (GUI) thread ),then the user interface become freezed
def on_pushButton_clicked(self):
subprocess.call([r'C:\Program Files\ffmpeg-20131122-git-fb7d70c-win32-static\bin\ffmpeg', '-f', 'concat', '-i','mylist.txt', '-c', 'copy', 'output.mp4'])
anyone can explain why ? subprocess() itself can not run in an asynchronous way ?
Upvotes: 2
Views: 3700
Reputation: 369074
subprocess.call
waits until the subprocess terminate. Use subprocess.Popen
instead.
def on_pushButton_clicked(self):
subprocess.Popen([r'C:\Program Files\ffmpeg-20131122-git-fb7d70c-win32-static\bin\ffmpeg', '-f', 'concat', '-i','mylist.txt', '-c', 'copy', 'output.mp4'])
Upvotes: 2
Reputation: 120608
As others have said, subprocess.call
will wait until the command complete.
But given that you're using PyQt, it's might be better to use QProcess, since that will allow you to use signals and events to keep the GUI responsive.
There are a few issues to think about.
Firstly, the example ffmpeg
command will hang if the output file already exists, because, by deafult, it will prompt the user for permission to overwrite. So it would be a good idea to add the -y
or -n
flag to deal with that.
Secondly, the command could also hang for some unexpected reason. So you should probably give the user a way to forcibly kill the process.
Finally, what should happen if the user attempts to close the application before the command completes? Probably you will need to handle the closeEvent of the main window to deal with that.
The demo script below shows some possible ways to handle the above issues:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.label = QtGui.QLabel('Elapsed: 0', self)
layout.addWidget(self.label)
self.buttonStart = QtGui.QPushButton('Start', self)
self.buttonStart.clicked.connect(self.handleButtonStart)
layout.addWidget(self.buttonStart)
self.buttonStop = QtGui.QPushButton('Stop', self)
self.buttonStop.setDisabled(True)
self.buttonStop.clicked.connect(self.handleButtonStop)
layout.addWidget(self.buttonStop)
self._process = QtCore.QProcess(self)
self._process.started.connect(self.handleStarted)
self._process.finished.connect(self.handleFinished)
self._process.error.connect(self.handleError)
self._time = QtCore.QTime()
self._timer = QtCore.QTimer(self)
self._timer.timeout.connect(self.handleTimeout)
def closeEvent(self, event):
if self._timer.isActive():
event.ignore()
else:
QtGui.QWidget.closeEvent(self, event)
def handleButtonStart(self):
self._running = True
self._process.start('ffmpeg', [
'-f', 'concat', '-i', 'input.txt',
'-c', 'copy', '-y', 'output.mp4',
], QtCore.QIODevice.ReadOnly)
def handleTimeout(self):
self.label.setText(
'Elapsed: %.*f' % (2, self._time.elapsed() / 1000.0))
def handleButtonStop(self):
if self._timer.isActive():
self._process.close()
def handleStarted(self):
self.buttonStart.setDisabled(True)
self.buttonStop.setDisabled(False)
self._time.start()
self._timer.start(50)
def handleFinished(self):
self._timer.stop()
self.buttonStart.setDisabled(False)
self.buttonStop.setDisabled(True)
def handleError(self, error):
if error == QtCore.QProcess.CrashExit:
print('Process killed')
else:
print(self._process.errorString())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 200, 100)
window.show()
sys.exit(app.exec_())
Upvotes: 0