如何在子进程期间和之后进行处理

时间:2015-01-29 16:52:39

标签: python qt user-interface pyqt subprocess

我有一个调用子程序的程序。当子程序与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

这里发生了一些事情。

1 个答案:

答案 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