PySide2关闭时终止QProcess问题:如何实现PyQt4同款子进程保留行为?
PySide2 QProcess子进程随主程序关闭的问题解决
我之前用PyQt4写了个测试程序,在Linux上用QProcess启动外部程序并捕获它的标准输出。关闭主应用后,子进程还能继续跑,这正是我想要的效果。但把代码改成PySide2版本(切换到QtWidgets这些模块)之后,关闭主应用时子进程也跟着退出了。我想知道这是PyQt4或者PySide2的Bug吗?有没有办法让PySide2实现和PyQt4一样的行为?另外我知道startDetached方法,但用它就没法获取标准输出了,所以这个方案行不通。
附原PySide2示例代码:
import sys from PySide2 import QtWidgets,QtCore, QtWidgets class gui(QtWidgets.QMainWindow): def __init__(self): super(gui, self).__init__() self.initUI() def stdoutReady(self, process): data = str(self.process.readAllStandardOutput()) print('STDOUT: {}'.format(data)) def stderrorReady(self, process): data = str(self.process.readAllStandardError()) print('STDERROR: {}'.format(data)) def processFinished(self, exitCode, exitStatus): print('EXIT CODE: {}, EXIT STATUS: {}'.format(exitCode, exitStatus)) def initUI(self): layout = QtWidgets.QHBoxLayout() centralWidget = QtWidgets.QWidget() centralWidget.setLayout(layout) self.setCentralWidget(centralWidget) self.process = QtCore.QProcess(self) self.process.readyReadStandardOutput.connect(lambda: self.stdoutReady(self.process)) self.process.readyReadStandardError.connect(lambda: self.stderrorReady(self.process)) self.process.start('dolphin') def main(): app = QtWidgets.QApplication(sys.argv) ui=gui() ui.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
这不是Bug,是默认行为差异!
PyQt4和PySide2在QProcess的父对象处理逻辑上默认不一样:
- 在PyQt4中,即使给QProcess设置了父对象(比如代码里的
QProcess(self)),主进程退出时也不会自动终止子进程; - 但PySide2中,当QProcess的父对象(这里是主窗口)被销毁时,会自动触发子进程的终止操作,导致子进程跟着退出。
解决方案:两种方法实现PyQt4相同行为
你不需要用startDetached,可以通过以下两种方式保留标准输出捕获的同时,让子进程在主应用关闭后继续运行:
方法1:不给QProcess设置父对象
创建QProcess时不要传入self作为父参数,这样它就不属于主窗口的对象树,主窗口销毁时不会触发它的终止逻辑:
self.process = QtCore.QProcess()
方法2:保留父对象,设置DetachOnExit属性
如果必须给QProcess设置父对象,可以手动开启DetachOnExit属性,告诉Qt在主进程退出时分离子进程:
self.process = QtCore.QProcess(self) self.process.setProcessAttribute(QtCore.QProcess.DetachOnExit, True)
修改后的完整示例代码
这里还优化了一下标准输出的编码处理,避免出现不必要的字符:
import sys from PySide2 import QtWidgets, QtCore class gui(QtWidgets.QMainWindow): def __init__(self): super(gui, self).__init__() self.initUI() def stdoutReady(self): # 处理编码,避免乱码和多余的转义字符 data = str(self.process.readAllStandardOutput(), encoding='utf-8').strip() print('STDOUT: {}'.format(data)) def stderrorReady(self): data = str(self.process.readAllStandardError(), encoding='utf-8').strip() print('STDERROR: {}'.format(data)) def processFinished(self, exitCode, exitStatus): print('EXIT CODE: {}, EXIT STATUS: {}'.format(exitCode, exitStatus)) def initUI(self): layout = QtWidgets.QHBoxLayout() centralWidget = QtWidgets.QWidget() centralWidget.setLayout(layout) self.setCentralWidget(centralWidget) # 选其中一种方案即可 # 方案1:无父对象 self.process = QtCore.QProcess() # 方案2:带父对象并设置DetachOnExit # self.process = QtCore.QProcess(self) # self.process.setProcessAttribute(QtCore.QProcess.DetachOnExit, True) # 连接信号,去掉多余的lambda表达式 self.process.readyReadStandardOutput.connect(self.stdoutReady) self.process.readyReadStandardError.connect(self.stderrorReady) self.process.finished.connect(self.processFinished) self.process.start('dolphin') def main(): app = QtWidgets.QApplication(sys.argv) ui = gui() ui.show() sys.exit(app.exec_()) if __name__ == '__main__': main()
这样修改后,你关闭PySide2主应用时,dolphin进程会继续运行,同时你依然能正常捕获它的标准输出和错误输出,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Michael B
相关产品推荐
相关产品推荐

