2010-11-19 151 views
7

我想從python多進程輸出獲取輸出顯示在Tkinter GUI中。如何發送python多進程輸出到Tkinter gui

我可以通過在殼提示符下運行fllowing微小腳本經由GUI發送從過程輸出的命令外殼,例如:

from multiprocessing import Process 
import sys 

def myfunc(text):  
    print text 
    sys.stdout.flush() 

def f1(): 
    p1 = Process(target = myfunc, args = ("Surprise",)) 
    p1.start() 

def f2(): 
    p2 = Process(target = myfunc, args = ("Fear",)) 
    p2.start() 

def fp(): 
    myfunc("... and an almost fanatical devotion to the Pope") 

a = Tk() 

b1 = Button(a, text="Process 1", command=f1) 
b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE) 
b2 = Button(a, text="Process 2", command=f2) 
b2.grid(row=0, column=1, pady=10, padx=10, sticky=SE) 
b3 = Button(a, text="Parent", command=fp) 
b3.grid(row=0, column=2, pady=10, padx=10, sticky=SE) 

if __name__ == "__main__": 
    a.mainloop() 

我還可以發送輸出從到文本框,例如通過註釋掉標準輸出的沖洗中MYFUNC

# sys.stdout.flush() 

和後立即加入「b3.grid ......」修改上述行:

class STDText(Text): 
    def __init__(self, parent, cnf={}, **kw): 
     Text.__init__(self, parent, cnf, **kw) 
    def write(self, stuff): 
     self.config(state=NORMAL) 
     self.insert(END, stuff) 
     self.yview_pickplace("end") 
     self.config(state=DISABLED) 

messages = STDText(a, height=2.5, width=30, bg="light cyan", state=DISABLED) 
messages.grid(row=1, column=0, columnspan=3) 
sys.stdout = messages 

但是我不知道如何將輸出從進程發送到文本框。我錯過了一些簡單的東西嗎

回答

8

你可以重定向標準輸出/標準錯誤,以一個在myfunc()中的StringIO,然後將寫入該StringIO的任何東西發送回父節點(如unutbu建議的那樣)。查看我對this question的回答,瞭解執行此重定向的一種方法。

由於這個例子確實有點超過你所需要的,這裏是一個的多與你的既定目標對準的版本:

#!/usr/bin/env python 
import sys 
from cStringIO import StringIO 
from code import InteractiveConsole 
from contextlib import contextmanager 
from multiprocessing import Process, Pipe 

@contextmanager 
def std_redirector(stdin=sys.stdin, stdout=sys.stdin, stderr=sys.stderr): 
    tmp_fds = stdin, stdout, stderr 
    orig_fds = sys.stdin, sys.stdout, sys.stderr 
    sys.stdin, sys.stdout, sys.stderr = tmp_fds 
    yield 
    sys.stdin, sys.stdout, sys.stderr = orig_fds 

class Interpreter(InteractiveConsole): 
    def __init__(self, locals=None): 
     InteractiveConsole.__init__(self, locals=locals) 
     self.output = StringIO() 
     self.output = StringIO() 

    def push(self, command): 
     self.output.reset() 
     self.output.truncate() 
     with std_redirector(stdout=self.output, stderr=self.output): 
      try: 
       more = InteractiveConsole.push(self, command) 
       result = self.output.getvalue() 
      except (SyntaxError, OverflowError): 
       pass 
      return more, result 

def myfunc(conn, commands): 
    output = StringIO() 
    py = Interpreter() 
    results = "" 

    for line in commands.split('\n'): 
     if line and len(line) > 0: 
      more, result = py.push(line + '\n') 
      if result and len(result) > 0: 
       results += result 

    conn.send(results) 
    conn.close() 

if __name__ == '__main__': 
    parent_conn, child_conn = Pipe() 

    commands = """ 
print "[42, None, 'hello']" 

def greet(name, count): 
    for i in range(count): 
     print "Hello, " + name + "!" 

greet("Beth Cooper", 5) 
fugazi 
print "Still going..." 
""" 
    p = Process(target=myfunc, args=(child_conn, commands)) 
    p.start() 
    print parent_conn.recv() 
    p.join() 

有關安全性的應用在這裏(即,不這樣做,除非你通常的注意事項可以相信這些代碼片段的發件人不會做任何愚蠢/惡意的事情)。

另請注意,如果您不需要解釋python表達式語句的任意組合,則可以簡化很多操作。如果你只需要調用一個產生一些輸出的頂級函數,像這樣的東西可能更合適:

def dosomething(): 
    print "Doing something..." 

def myfunc(conn, command): 
    output = StringIO() 
    result = "" 
    with std_redirector(stdout=output, stderr=output): 
     try: 
      eval(command) 
      result = output.getvalue() 
     except Exception, err: 
      result = repr(err) 

    conn.send(result) 
    conn.close() 

if __name__ == '__main__': 
    parent_conn, child_conn = Pipe() 
    command = "dosomething()" 
    p = Process(target=myfunc, args=(child_conn, command)) 
    p.start() 
    print parent_conn.recv() 
    p.join() 
3

您可以在使用multiprocessing.Pipe的進程之間傳遞(可分揀)數據。 例如:

import Tkinter 
import multiprocessing as mp 

class STDText(Tkinter.Text): 
    def __init__(self, parent, cnf={}, **kw): 
     Tkinter.Text.__init__(self, parent, cnf, **kw) 
    def write(self, stuff): 
     self.config(state=Tkinter.NORMAL) 
     self.insert(Tkinter.END, stuff) 
     self.yview_pickplace("end") 
     self.config(state=Tkinter.DISABLED) 

def myfunc(conn,text):  
    conn.send(text) 
    conn.close() 

class Gui(object): 
    def __init__(self): 
     self.a=Tkinter.Tk() 
     b1=Tkinter.Button(self.a, text="Process 1", command=self.foo) 
     b1.grid(row=0, column=0, pady=10, padx=10, sticky=Tkinter.SE) 
     b2=Tkinter.Button(self.a, text="Process 2", command=self.bar) 
     b2.grid(row=0, column=1, pady=10, padx=10, sticky=Tkinter.SE) 
     b3=Tkinter.Button(self.a, text="Parent", command=self.baz) 
     b3.grid(row=0, column=2, pady=10, padx=10, sticky=Tkinter.SE) 
     self.messages=STDText(
      self.a, height=2.5, width=30, bg="light cyan", state=Tkinter.DISABLED) 
     self.messages.grid(row=1, column=0, columnspan=3) 
     self.a.mainloop()   
    def call_myfunc(self,text): 
     parent_conn, child_conn=mp.Pipe() 
     proc=mp.Process(target=myfunc, args=(child_conn,text,)) 
     proc.start() 
     self.messages.write(parent_conn.recv()) 
     proc.join()  
    def foo(self): 
     self.call_myfunc('Foo\n') 
    def bar(self): 
     self.call_myfunc('Bar\n')   
    def baz(self): 
     parent_conn, child_conn=mp.Pipe() 
     myfunc(child_conn,'Baz\n') 
     self.messages.write(parent_conn.recv()) 

if __name__ == "__main__": 
    Gui() 

有關詳細信息,請參見第multiprocessingDoug Hellman's tutorial

+0

Unutbu - 感謝非常有幫助的答案。它當然可以解決我玩具的例子。 (在實際的應用程序中,這些進程將運行不同的函數並自動生成輸出消息(即不會打印某些給定的文本作爲參數)。我想知道是否有可能找到一個'cheapo'來避免進入管道,但我想這可能是要走的路線 – tchaz 2010-11-20 15:22:34

0

假設myfunc時調用過程的輸出,只寫MYFUNC,如:

def myfunc(text):  
    textwidget.insert("end", text) 

其中textwidget是一個句柄文本組件

+0

謝謝你的建議,這肯定會對我的玩具例子有所幫助,但是我可能會簡化一些我需要的東西 - 在實際的應用程序中,進程將運行不同的功能併產生輸出消息自發地(即不打印給他們的特定文本作爲參數)。 – tchaz 2010-11-20 15:15:56