问题描述
我正在尝试从Tkinter gui中显示的python多处理过程中获取输出。
我可以通过gui将进程的输出发送到命令shell,例如在shell提示符下运行fllowing tiny脚本:
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中注释掉stdout来修改上面的内容
# 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
但是,我无法弄清楚如何将进程的输出发送到文本框。 我错过了一些简单的事吗?
1楼
您可以将stdout / stderr重定向到myfunc()中的StringIO,然后将写入StringIO的任何内容发送回父级(如unutbu所示)。 有关执行此重定向的一种方法,请参阅答案。
由于该示例比您需要的更多,这里的版本更符合您的既定目标:
#!/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()
2楼
您可以使用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()
有关详细信息,请参阅 multiprocessing
。
3楼
假设使用进程的输出调用myfunc
,只需编写myfunc,如:
def myfunc(text):
textwidget.insert("end", text)
textwidget是文本小部件的句柄