How can I send python multiprocessing Process output to a Tkinter gui How can I send python multiprocessing Process output to a Tkinter gui tkinter tkinter

How can I send python multiprocessing Process output to a Tkinter gui


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 pythonimport sysfrom cStringIO import StringIOfrom code import InteractiveConsolefrom contextlib import contextmanagerfrom multiprocessing import Process, Pipe@contextmanagerdef 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_fdsclass 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, resultdef 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)fugaziprint "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).

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


You can pass (picklable) data between processes using a multiprocessing.Pipe.For example:

import Tkinterimport multiprocessing as mpclass 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()

See Doug Hellman's tutorial on multiprocessing for more information.


Assuming that myfunc is called with the output of the process, just write myfunc like:

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

where textwidget is a handle to the text widget