PyQt4 QProcess 状态始终为 0,各种插槽也无法正常工作

Posted

技术标签:

【中文标题】PyQt4 QProcess 状态始终为 0,各种插槽也无法正常工作【英文标题】:PyQt4 QProcess state always 0, various slots not working too 【发布时间】:2015-07-16 13:06:56 【问题描述】:

我正在尝试弄清楚 QProcess (Linux!) 的工作方式,因为我的项目需要它(注意:不使用 suprocess 或多线程!进程也必须与主应用程序分离!)。这是一个小代码来演示一些基本功能:

#!/usr/bin/python

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QProcess


class Example(QtGui.QWidget):


    def __init__(self):
        super(Example, self).__init__()
        self.myProcess = QProcess(self)
        self.myProcess.finished.connect(self.onFinished) # NEVER called
        self.myProcess.stateChanged.connect(self.onStateChanged) # NEVER called
        self.myProcess.started.connect(self.onStarted) # NEVER called
        self.command = "./testCommand.py"
        self.args = [""]
        self.initUI()

    def initUI(self):               
        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)

        qbtn = QtGui.QPushButton('Start', self)
        qbtn.clicked.connect(self.toggleProcess)
        qbtn.resize(qbtn.sizeHint())
        hbox.addWidget(qbtn)

        # This button is for testing the responsiveness of the GUI after the QProcess has been started
        qbtn2 = QtGui.QPushButton('Click me', self)
        qbtn2.setCheckable(True)
        qbtn2.toggled.connect(self.toggleButton)
        qbtn2.resize(qbtn2.sizeHint())
        hbox.addWidget(qbtn2)

        self.setLayout(hbox)
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('QProcess controlled by a button')    
        self.show()

    def toggleProcess(self):
        # process states (based on Qt docs):
        # 0 - not running
        # 1 - starting
        # 2 - running

        # For some reason state is ALWAYS 0
        if self.myProcess.state() == 0:
            self.myProcess.startDetached(self.command, self.args)
            print "Starting process"
            print "Process state", str(self.myProcess.state())
        elif self.myProcess.state() == 1:
            print "Process is starting"
            return
        else:
            print "Stopping process"
            self.myProcess.close()   

    def toggleButton(self, value):
        if value == True:
            print "Lalalala!"
        else:
            print "Didadida!"

    def onStarted(self):
        print "Process started"

    def onFinished(self):
        print "Process stopped"

    def onStateChanged(self):
        print "Process has changed state"

    def __del__(self):
        if self.myProcess.state() == 1 or self.myProcess.state() == 2:
            self.myProcess.close()
        else:
            pass

def main(): 
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

testCommand.py如下:

版本 1:

#!/usr/bin/env python

count = 0
while count < 10:
    print "c"
    count = count + 1

VERSION 2:这里我尝试了一个无限进程来查看 GUI 是否被阻塞。

#!/usr/bin/env python

while True:
    print "c"

在这里我遇到了多个问题。首先,QProcess.state() 函数总是返回 0,因此我永远不会在 toggleProcess() 函数中遇到我的 IF 语句的其他两种情况。由于进程状态没有变化,因此没有任何信号被发出……永远。无论我选择我的 testCommand.py 的版本 1,它只运行一个循环 10 次或版本 2,它无限运行直到进程关闭,结果总是状态 0,即使我可以看到该进程正在运行(在版本 2 中,您将获得无限行的“c”字符)。在我相应地使用 VERSION 2 的情况下,我无法停止进程(因为它的状态不会改变,因此永远不会调用 QProcess.close())所以如果我关闭我的主应用程序,我会创建一个必须通过 htop 或类似进程管理器杀死的孤立进程。我知道 QProcess.startDetached() 创建了一个分离的进程,但我仍然希望通过“开始”按钮对该进程的执行进行一些控制。顺便说一句,当我使用 QProcess.execute() 时,我得到了相同的行为,它创建了一个子进程并相应地冻结了 GUI 运行所需的时间(在版本 2 中这是无限期的)。

谁能告诉我为什么会这样?我想要的只是能够启动一个分离的进程,但仍然能够通过同一个按钮终止它。我也尝试过使用可检查按钮(例如“单击我”按钮)和布尔标志,但缺少状态更改的问题仍然存在。

谢谢!

【问题讨论】:

显然这是startDetached() 的预期行为,请参阅this question 等。如果你想使用QProcess的信号,你必须使用start() 是的,我已经看到了这个问题,但是我未来的目标是创建一个 GUI 防崩溃进程管理器(我正在使用 ROS),即使 GUI 崩溃,启动的进程也会继续存在并且一旦 GUI 重新启动,它将使用存储在某个文件中的 PID 重新连接到这些进程。所以这里的 GUI 只是控制多个 QProcess-es 的一种便捷方式,仅此而已。这就是为什么 startDetached() 是我唯一的选择,因为如果我的 GUI 终止,start() 将导致创建的进程也终止。 【参考方案1】:

startDetached 函数是静态的。分离的进程是由 Qt 在内部启动的,因此从来没有一个 QProcess 与之对应。这就是信号不起作用的原因。在您的示例脚本中,myProcess 完全是多余的。

根据定义,一个分离的进程与启动它的进程没有直接的通信方式。你得到的只是一个pid,就是这样。在某些平台上,可以使用该 pid 来终止进程 - 例如,请参阅 os.kill。

出于与上述相同的原因,无法重新附加到现有进程。你所拥有的只是那个 pid,如果你想以后再使用它,它需要以某种方式存储在外部(例如,在一个文件中)。

一般来说,您正在处理的问题是Inter-process communication (IPC)。有许多不同的解决方案可用,因此您需要更清楚地了解应用程序的结构,然后再决定哪一个最合适。

【讨论】:

以上是关于PyQt4 QProcess 状态始终为 0,各种插槽也无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

pyqt4正确连接QCheckbox状态

带有 adb 的 QProcess 输出始终为空

QProcess正常退出

仅当 QProcess Stdout 包含子字符串时才打印它

QProcess精加工的可靠检测

QProcess.start 不返回退出状态或退出代码