pexpect 执行长时间运行的 bash 命令时挂起 wxpython GUI
我正在编写一个图形界面软件,这个软件有一个终端窗口(wxCtrl),可以实时显示外部程序的输出。
我尝试过使用subprocess.Popen,但效果不如预期,因为它会在运行时让我的图形界面卡住,只有在执行完成后才会显示输出。
def miExecuteCmd(self, cmd):
self.terminal.addText("\n###\n\n")
self.terminal.addText("Executing: %s\n" % cmd)
args = shlex.split(cmd)
p = subprocess.Popen(args, stdout = subprocess.PIPE)
output = p.stdout.readlines()
output = "".join(output)
self.terminal.addText(output)
if (p.returncode != None and p.returncode != 0 ):
self.terminal.addText("Command Execution Problem, return code is %d\n" % p.returncode)
return output
现在我在尝试使用pexpect,我看了这篇文章,如何使用pexpect获取子进程的即时输出
所以我写了类似这样的代码,
def miExecuteCmd(self, cmd):
self.terminal.addText("\n###\n\n")
self.terminal.addText("Executing: %s\n" % cmd)
output = []
child = pexpect.spawn(cmd)
while True:
try:
child.expect('\n')
line = child.before
output.append(line)
self.terminal.addText(line)
except pexpect.EOF:
break
if child.exitstatus != None and child.exitstatus != 0:
line = "Command Execution Problem, return code is %d\n" % child.exitstatus
self.terminal.addText(line)
output.append(line)
output = "".join(output)
return output
但即使这样,当我使用一个运行时间很长的命令时,图形界面还是会卡住。
所以我想找一个简单的pexpect解决方案,能让我在操作图形界面的同时看到命令的输出。
我看了pexpect的文档,似乎pexpect.spawn()应该会为命令启动一个独立的线程,现在我有点困惑,不知道是否应该把pexpect.spawn()放在一个新线程里。
2 个回答
1
无论你用什么方法来运行脚本,你的图形界面窗口都会卡住。为了避免这种情况,你需要把命令放在一个单独的线程中执行,这样图形界面就不会被阻塞。如果你能提供一个简单的代码示例,那就更好了。不过,试试下面的做法:
import thread
def miExecuteCmd(self, cmd):
#bunch of codes...
def on_execute(self, cmd):
thread.start_new_thread(self.miExecutecmd, ())
把你的事件处理器绑定到调用 self.on_execute
,这样它就会在一个新线程中执行命令。
0
最后,我找到了解决我问题的好办法,解决了在wxPython中界面卡顿和线程之间的通信。
大家应该看看这篇文章 http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads,它结合了线程、wx.CallAfter()和PubSub来解决线程通信的问题。所以在我的情况下,只需要添加pexpect来指示需要沟通的内容。
下面是我的run()示例,你需要查看上面链接中的示例。
def run(self):
wx.CallAfter(self.sendToMainThread, "\n###\n\n")
text = "Executing: %s\n" % (self.cmd)
wx.CallAfter(self.sendToMainThread, text)
child = pexpect.spawn(self.cmd)
while True:
try:
if self.stopFlag:
line = "Stop Buttont Clicked, Stopping External Command... \n"
wx.CallAfter(self.sendToMainThread, line)
child.terminate(True)
child.close()
break
child.expect('\n')
line = child.before
wx.CallAfter(self.sendToMainThread, line)
except pexpect.EOF:
child.close()
break
if child.exitstatus != None and child.exitstatus != 0:
line = "Command Execution Problem, return code is %d\n" % child.exitstatus
wx.CallAfter(self.sendToMainThread, line)
#it looks like next line never happens because of the exception handling above.
#child.close() make sure child return a code.
elif child.exitstatus == None:
line = "Command Execution was interrupted.\n"
wx.CallAfter(self.sendToMainThread, line)
#sending an end signal to main thread. command is finished.
wx.CallAfter(Publisher().sendMessage, "endthread", True)