子进程导致用户界面冻结

时间:2013-12-09 13:17:05

标签: python pyqt subprocess pyside

单击该按钮时,以下代码开始运行(从主(GUI)线程调用),然后用户界面变为冻结

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'])
谁能解释为什么? subprocess()本身不能以异步方式运行吗?

2 个答案:

答案 0 :(得分:0)

subprocess.call等待子进程终止。请改用subprocess.Popen

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'])

答案 1 :(得分:0)

正如其他人所说,subprocess.call将等到命令完成。

但鉴于您使用的是PyQt,使用QProcess可能会更好,因为这样可以使用信号和事件来保持GUI的响应。

要考虑一些问题。

首先,如果输出文件已经存在,示例ffmpeg命令将挂起,因为,通过deafult,它将提示用户允许覆盖。因此,最好添加-y-n标志来处理。

其次,该命令也可能因某些意外原因而挂起。所以你应该给用户一种强制杀死进程的方法。

最后,如果用户在命令完成之前尝试关闭应用程序,会发生什么?可能你需要处理主窗口的closeEvent来处理它。

下面的演示脚本显示了处理上述问题的一些可能方法:

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_())