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,各种插槽也无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章