2011-04-29 40 views
1

長時間実行しているシェルスクリプトは、stdoutとstderrを生成します。これはGUIのtextctrlに表示します。これは、GUIスレッドをシェルスクリプトのスレッドから分離して分離することによって可能です。しかし、私がマルチプロセッシングを実装するとき、私はロードブロッキングを打ちました。ここに私のダウンストリッピングされたコード:wxPython TextCtrlの別のプロセスで実行されているシェルスクリプトの出力をキャプチャするにはどうすればよいですか?

#!/usr/bin/env python 

import wx 
import sys, subprocess 
from multiprocessing import Process, Queue 
from Queue import Empty 

class MyFrame(wx.Frame): 
    def __init__(self, *args, **kwds): 
     wx.Frame.__init__(self, *args, **kwds) 

     self.button = wx.Button(self, -1 , "Run") 
     self.output = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE|\ 
                 wx.TE_READONLY|wx.HSCROLL) 

     self.Bind(wx.EVT_BUTTON, self.OnButton, self.button) 

     sizer = wx.BoxSizer(wx.VERTICAL) 
     sizer.Add(self.output, -1, wx.EXPAND, 0) 
     sizer.Add(self.button) 
     self.SetSizerAndFit(sizer) 
     self.Centre() 

    def OnButton(self, event): 
     numtasks = 4 # total number of tasks to run 
     numprocs = 2 # number of processors = number of parallel tasks 
     work_queue = Queue() 
     for i in xrange(numtasks): 
      work_queue.put(i) 
     processes = [Process(target=self.doWork, args=(work_queue,)) 
        for i in range(numprocs)] 
     for p in processes: 
      p.daemon = True 
      p.start() 

    def doWork(self, work_queue): 
     while True: 
      try: 
       x = work_queue.get(block=False) 
       self.runScript(x) 
      except Empty: 
       print "Queue Empty" 
       break 

    def runScript(self, taskno): 
     print '## Now Running: ', taskno 
     command = ['./script.sh'] 
     proc = subprocess.Popen(command, shell=True, 
             stdout=subprocess.PIPE, 
             stderr=subprocess.STDOUT) 
     while True: 
      stdout = proc.stdout.readline() 
      if not stdout: 
       break 
      #sys.stdout.flush() #no need for flush, apparently it is embedded in the multiprocessing module 
      self.output.AppendText(stdout.rstrip()) #this is the part that doesn't work. 

if __name__ == "__main__": 
    app = wx.App(0) 
    frame = MyFrame(None, title="shell2textctrl", size=(500,500)) 
    frame.Show(True) 
    app.MainLoop() 

私は他人の提案に多くの解決策を試した。その中には、GUIを応答させるためにwx.CallAfterまたはwx.lib.delayedresultを使用する方法、いくつかのスレッドレシピ(例えば、wxApplication Development Cookbook、threadpool、others ...)と、別のスレッドから実行されるmultiprocess() text.rlに書き込むためにsys.stdout.writeを再定義します。それ以外のものはありません。いずれも成功しなかった。誰でも作業コードと一緒にソリューションを提供してもらえますか?

#!/bin/tcsh -f 
@ i = 1 
while ($i <= 20) 
echo $i 
@ i += 1 
sleep 0.2 
end 

答えて

1

私はロジャー・スタッキーのSimpler wxPython Multiprocessing Exampleフレームワークの上に変更された出力キューを実装して解決策を見つけました。以下のコードは、彼よりも簡単です。それは少しでも清掃することができます。他の人に役立つことを願っています。私はまだこれをやるより簡単な方法があるはずだという気持ちが残っている。

import getopt, math, random, sys, time, types, wx, subprocess 

from multiprocessing import Process, Queue, cpu_count, current_process, freeze_support 
from Queue import Empty 

class MyFrame(wx.Frame): 
    def __init__(self, parent, id, title): 
     wx.Frame.__init__(self, parent, id, title, wx.Point(700, 500), wx.Size(300, 200)) 

     self.panel = wx.Panel(self, wx.ID_ANY) 

     #widgets 
     self.start_bt = wx.Button(self.panel, wx.ID_ANY, "Start") 
     self.Bind(wx.EVT_BUTTON, self.OnButton, self.start_bt) 

     self.output_tc = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_MULTILINE|wx.TE_READONLY) 

     # sizer 
     self.sizer = wx.GridBagSizer(5, 5) 
     self.sizer.Add(self.start_bt, (0, 0), flag=wx.ALIGN_CENTER|wx.LEFT|wx.TOP|wx.RIGHT, border=5) 
     self.sizer.Add(self.output_tc, (1, 0), flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=5) 
     self.sizer.AddGrowableCol(0) 
     self.sizer.AddGrowableRow(1) 
     self.panel.SetSizer(self.sizer) 

     # Set some program flags 
     self.keepgoing = True 
     self.i = 0 
     self.j = 0 


    def OnButton(self, event): 
     self.start_bt.Enable(False) 

     self.numtasks = 4 
     self.numproc = 2 
     #self.numproc = cpu_count() 
     self.output_tc.AppendText('Number of processes = %d\n' % self.numproc) 

     # Create the queues 
     self.taskQueue = Queue() 
     self.outputQueue = Queue() 

     # Create the task list 
     self.Tasks = range(self.numtasks) 

     # The worker processes... 
     for n in range(self.numproc): 
      process = Process(target=self.worker, args=(self.taskQueue, self.outputQueue)) 
      process.start() 

     # Start processing tasks 
     self.processTasks(self.update) 

     if (self.keepgoing): 
      self.start_bt.Enable(True) 

    def processTasks(self, resfunc=None): 
     self.keepgoing = True 

     # Submit first set of tasks 
     numprocstart = min(self.numproc, self.numtasks) 
     for self.i in range(numprocstart): 
      self.taskQueue.put(self.Tasks[self.i]) 

     self.j = -1 # done queue index 
     self.i = numprocstart - 1 # task queue index 
     while (self.j < self.i): 
      # Get and print results 
      self.j += 1 
      output = None 
      while output != 'STOP!': 
       try: 
        output = self.outputQueue.get() 
        if output != 'STOP!': 
         resfunc(output) 
       except Empty: 
        break 

      if ((self.keepgoing) and (self.i + 1 < self.numtasks)): 
       # Submit another task 
       self.i += 1 
       self.taskQueue.put(self.Tasks[self.i]) 

    def update(self, output): 
     self.output_tc.AppendText('%s PID=%d Task=%d : %s\n' % output) 
     wx.YieldIfNeeded() 

    def worker(self, inputq, outputq): 
     while True: 
      try: 
       tasknum = inputq.get() 
       print '## Now Running: ', tasknum #this goes to terminal/console. Add it to outputq if you'd like it on the TextCtrl. 
       command = ['./script.sh'] 
       p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
       while True: 
        r = p.stdout.readline() 
        if not r: 
         outputq.put('STOP!') 
         break 
        outputq.put((current_process().name, current_process().pid, tasknum, r.rstrip())) 
      except Empty: 
       break 

    # The worker must not require any existing object for execution! 
    worker = classmethod(worker) 

class MyApp(wx.App): 
    def OnInit(self): 
     self.frame = MyFrame(None, -1, 'stdout to GUI using multiprocessing') 
     self.frame.Show(True) 
     self.frame.Center() 
     return True 

if __name__ == '__main__': 
    freeze_support() 
    app = MyApp(0) 
    app.MainLoop() 
1

これはあなたを助けたりしません場合、私は知らないが、私はここに、サブプロセスを使用して、このようなものの例があります:

をあなたは私によってどこかにここに書かれたこのスクリプトは誰を忘れてしまった使用することができますhttp://www.blog.pythonlibrary.org/2010/06/05/python-running-ping-traceroute-and-more/

pingIPおよびtracertIPメソッドを参照してください。あなたのシェルスクリプトがPythonで書かれている場合は、おそらく私はその記事でGUIにポストバックするために使用しているもののようなジェネレータを追加することができます。あなたはおそらくすでにLongRunningProcessのwikiの記事を読んで、私はそれを取り、ここに私自身のチュートリアルを作った:

http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/

+0

確かに、私はLongRunningProcessの記事とあなたのチュートリアル(非常に便利です!)を見ました。私は_threading_で動作するように自分のコードを取得できますが、_multiprocessing_では動作しません。マルチスレッドはスレッド化に基づいているため、愚かです。そして、これらの2つのモジュールがどのように機能するかについての私の理解は、厳しく制限されています。残念ながら、私は本当にマルチプロセッシングを使用する必要があります。 –

+0

私はまだマルチプロセッシングを使用していないので、その時点ではコメントできません。私が時間を見つけたら、それを掘り下げます。 –

関連する問題