2014-07-11 3 views
0

Je programme un logiciel GUI avec une fenêtre de terminal (wxCtrl) pour afficher la sortie du programme externe en temps réel pendant son exécution.pexpect run commande long bash hang wxpython GUI

J'ai essayé subprocess.Popen, cela ne fonctionne pas comme prévu, car il va bloquer mon interface graphique pendant son exécution, et ne donne que la sortie après l'exécution terminée.

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 

Maintenant, je suis en train d'utiliser pexpect, je lis ce post, how to use pexpect to get spontaneous output of subprocess in python

Je codé quelque chose comme,

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 

Mais encore l'interface graphique gèlera pendant que je une longue temps qui passe cmd.

Donc, je demande une solution pexpect simple me permettant d'utiliser mon interface graphique et de voir la sortie du cmd en même temps.

J'ai lu le document pexpect, il semble que pexpect.spawn() devrait démarrer un thread séparé pour la commande, maintenant je suis confus de mettre pexpect.spawn() dans un nouveau thread.

Répondre

0

Enfin, j'ai élaboré une solution pour mon problème, le gel de l'interface graphique et la communication de threads dans wxPython.

On aurait dû lire cet article http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads, c'est une combinaison de threading, wx.CallAfter(), PubSub pour résoudre le problème de communication de thread. Donc, dans mon cas, il suffit d'ajouter le pexpect pour indiquer ce qu'il faut communiquer.

Voici mon exemple run(). vous aurez besoin de voir l'exemple dans le lien ci-dessus.

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) 
1

Votre fenêtre de l'interface graphique va se figer, peu importe la méthode que vous utilisez pour exécuter les scripts. Vous devez exécuter les commandes en tant que thread séparé afin que l'interface graphique ne soit pas bloquée. Cela aurait aidé si vous aviez fourni un exemple minimal de votre code, mais de toute façon essayer quelque chose comme ceci:

import thread 

def miExecuteCmd(self, cmd): 
    #bunch of codes... 

def on_execute(self, cmd): 
    thread.start_new_thread(self.miExecutecmd,()) 

Bind votre gestionnaire d'événements pour appeler self.on_execute qui à son tour exécuter la commande dans un nouveau thread

+0

merci pour le code, mais ce n'est pas complet, j'ai besoin de la nouvelle discussion envoyer des informations au fil principal dans les deux sens. Je l'ai trouvé en utilisant PubSub dans wxpython, le mettra à jour bientôt. –

+0

Si vous aviez mentionné plus tôt, j'aurais pu vous montrer cela aussi! – user2963623

Questions connexes