如何将 python 多处理进程输出发送到 Tkinter gui [英] How can I send python multiprocessing Process output to a Tkinter gui

查看:24
本文介绍了如何将 python 多处理进程输出发送到 Tkinter gui的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从 Tkinter gui 中显示的 python 多处理进程获取输出.

I'm trying to get output from a python multiprocessing Process displayed in a Tkinter gui.

我可以通过 gui 将进程的输出发送到命令 shell,例如通过在 shell 提示符下运行流动的小脚本:

I can send output from Processes via a gui to a command shell, for example by running the fllowing tiny script at a shell prompt:

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

我还可以将输出从 parent 发送到文本框,例如通过注释掉 myfunc 中 stdout 的刷新来修改上述内容

I can also send output from the parent to a Text box, for example by modifying the above by commenting out the flushing of stdout in myfunc

#    sys.stdout.flush()

并在b3.grid..."行之后立即添加以下内容:

and adding immediately after the "b3.grid..." line the following:

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

但是我不知道如何将输出从进程发送到文本框.我错过了一些简单的东西吗?

However I can't figure out how to send output from the Processes to the text box. Am I missing something simple?

推荐答案

您可以将 stdout/stderr 重定向到 myfunc() 中的 StringIO,然后将写入该 StringIO 的任何内容发送回父级(如 unutbu 所建议的).查看我对这个问题的回答进行这种重定向的一种方式.

You could redirect stdout/stderr to a StringIO in myfunc(), then send whatever gets written into that StringIO back to the parent (as suggested by unutbu). See my answer to this question for one way of doing this redirection.

由于该示例的作用超出了您的需要,因此这里有一个更符合您既定目标的版本:

Since that example does a bit more than you need, here's a version that's more aligned with your stated goals:

#!/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('
'):
        if line and len(line) > 0:
            more, result = py.push(line + '
')
            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()

此处适用有关安全性的常见警告(即,除非您可以相信这些代码片段的发件人不会做任何愚蠢/恶意的事情,否则不要这样做).

The usual caveats about security apply here (i.e., don't do this unless you can trust the sender of these code snippets to not do anything stupid/malicious).

另请注意,如果您不需要解释任意混合的 Python 表达式 语句,您可以将其简化很多.如果你只需要调用一个生成一些输出的顶级函数,这样的事情可能更合适:

Also note that you can simplify this a lot if you don't need to interpret an arbitrary mix of python expressions and statements. If you only need to call a top-level function that generates some outputs, something like this may be more appropriate:

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

这篇关于如何将 python 多处理进程输出发送到 Tkinter gui的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆