我有一个调用子程序的程序。当子程序与Popen一起运行时,我需要禁用运行按钮并启用停止按钮。但是,由于Popen打开了一个新进程,因此在程序完成后打算打印的内容会立即打印出来。我尝试在self.p.communicate()
之后添加Popen
,但GUI将冻结,直到子程序完成运行,因此停止按钮将不起作用。
这是我的计划:
def gui(QtGui.QWidget):
...
self.runButton = QtGui.QPushButton('Run')
self.stopButton = QtGui.QPushButton('Stop')
self.runButton.clicked.connect(self.run)
self.stopButton.clicked.connect(self.kill)
self.runButton.setEnabled(True)
self.stopButton.setEnabled(False)
def run(self):
self.runButton.setEnabled(False)
self.p = subprocess.Popen(sample.exe)
#self.p.communicate()
self.stopButton.setEnabled(True)
print "Finish running" #without communicate() it will print out before .exe finish running
def kill(self):
self.p.kill()
self.stopButton.setEnabled(False)
print 'You have stop the program'
self.runButton.setEnabled(True)
我使用的是Window7,Python 2.7,pyqt4。我不必使用子进程,任何打开并能够杀死子程序的东西都可以。
提前致谢。
修改:使用QProcess
作为dano建议尝试。我已将以下代码添加到我的程序中:
def gui(QtCore.Widget):
self.process = QtCore.QProcess(self)
def run(self):
self.process.start(exe_path)
self.process.started.connect(self.onstart)
self.process.finished.connect(self.onfinish)
self.runButton.setEnabled(False)
#self.p = subprocess.Popen(sample.exe) #removed
#self.p.communicate() #removed
def onstart (self):
self.stopButton.setEnabled(True)
self.runButton.setEnabled(False)
print "Started\n"
def onfinish (self):
self.stopButton.setEnabled(False)
self.runButton.setEnabled(True)
print "Finish running\n"
def kill(self):
self.process.kill()
#self.p.kill() #removed
这是输出:
点击"运行" (子程序完成运行时输出)
Finish running
点击"运行"第二次
Started
点击"停止"
Finish running
Finish running
点击"运行"第三次
Started
Started
点击"停止"
Finish running
Finish running
Finish running
这里发生了一些事情。
答案 0 :(得分:0)
p = subprocess.Popen(exe)
不等待exe
完成 - 只要exe
启动就会返回。
p.communicate()
确实等待子进程结束,因此它会阻止你的GUI线程(GUI冻结直到子进程退出)。
为避免阻止GUI,您可以检查p.poll()
是否返回None
以确定p
进程是否仍在运行。通常,GUI框架提供了一种设置定期回调的方法(例如,Qt的QTimer) - 您可以在那里调用p.poll()
。
或者将阻塞调用(例如p.wait()
)放入后台线程并在完成时通知GUI线程(发出信号,生成事件)。
QProcess suggested by @dano已经内置了通知机制(finished
信号)。
作为@three_pineapples said:如果您不希望多次调用回调,请不要多次连接相同的信号。在您的情况下,不要多次在同一个流程实例上调用.start()
:
def run(self):
self.runButton.setEnabled(False) # call it first because
# processes are not started instantly
self.process = QtCore.QProcess(self)
self.process.started.connect(self.onstart) # connect "start" signal
self.process.finished.connect(self.onfinish)
self.process.start(exe_path, args) # then start