如何在子进程期间和之后做事

How to do stuff during and after a child process

我有一个调用子程序的程序。当子程序是 运行 Popen 时,我需要禁用 运行 按钮并启用停止按钮。但是,因为 Popen 开启了一个新的进程,程序结束后应该打印的东西会立即打印出来。我尝试在 Popen 之后添加 self.p.communicate(),但是 GUI 将冻结直到子程序完成 运行ning,因此停止按钮将不起作用。 这是我的程序:

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。我不必使用子进程,任何打开并能够杀死子程序的东西都可以。

提前致谢。

编辑: 尝试按照 dano 的建议使用 QProcess。我已将以下代码添加到我的程序中:

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

这是输出:

点击"Run"(子程序结束后输出运行ning)

Finish running

第二次点击"Run"

Started

点击"Stop"

Finish running
Finish running

第三次点击"Run"

Started
Started

点击"Stop"

Finish running
Finish running
Finish running

这里发生了一些事情。

p = subprocess.Popen(exe) 不会等待 exe 完成 - 它 returns 一旦 exe 开始。

p.communicate() 会等待子进程结束,因此它会阻塞您的 GUI 线程(GUI 冻结直到子进程退出)。

为避免阻塞 GUI,您可以检查是否 p.poll() returns None 以确定 p 进程是否仍在 运行。通常 GUI 框架提供了一种设置周期性回调的方法(例如,Qt 的 QTimer)——您可以在那里调用 p.poll()。 或者将阻塞调用(例如p.wait())放入后台线程并在完成时通知GUI线程(发出信号,生成事件)。

QProcess 已经内置了通知机制(finished 信号)。

As :如果您不想多次调用回调,请不要多次连接同一个信号。在您的案例中,多次调用 .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