【问题标题】:Redirect stdout from multiprocessing to Tkinter text widget将标准输出从多处理重定向到 Tkinter 文本小部件
【发布时间】:2020-12-13 04:58:29
【问题描述】:

我有点卡在这里。我已经阅读了很多堆栈溢出线程,但没有进一步了解该主题。

我的目标是拥有一个 tinter GUI,它可以在某个时候在新进程中启动一个函数,并将该函数中的每个打印重定向到 Guis Text 小部件。有一个管道和队列,但我不熟悉如何正确使用它们。我找到了一个可行的解决方案here,但这仅适用于 Python 3。不幸的是,我必须使用 Python 2.7...

有人可以帮忙吗?

我的示例代码:

from Tkinter import *
import multiprocessing as mp
import time
import sys

class Gui(object):

    def __init__(self):
        self.a=Tk()
        b1=Button(self.a, text="Process 1", command=self.func)
        b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE)

        self.messages=Text(
            self.a, height=2.5, width=30, bg="light cyan", state=NORMAL)
        self.messages.grid(row=1, column=0, columnspan=3)


        sys.stdout = self.StdoutRedirector(self.messages)
        sys.stderr = self.StdoutRedirector(self.messages)
        
        self.a.mainloop()
        
    class StdoutRedirector(object):
        def __init__(self, text_widget):
            self.output = text_widget

        def write(self, string):
            self.output.config(state=NORMAL)
            self.output.update_idletasks()
            self.output.insert('end', string)
            self.output.see('end')
            self.output.config(state=DISABLED)

        def flush(self):
            pass

    def func(self):
        print("test")
        proc=mp.Process(target=go)
        proc.start()


def go():
    for i in range(0,10):
        time.sleep((1))
        print(i)

if __name__ == "__main__":
    Gui()

【问题讨论】:

    标签: python-2.7 redirect tkinter multiprocessing stdout


    【解决方案1】:

    您的代码中的问题是您正在更改主循环之外的 tkinter 小部件的内容,这是不可能的。我发现的一种解决方案是将标准输出和标准错误重定向到队列,并定期在主循环中读取这些队列的内容以更新文本小部件。定期阅读使用方法.after(<delay in ms>, <callback>)完成。

    from Tkinter import *
    import multiprocessing as mp
    import time
    import sys
    
    class StdoutRedirector(object):
        def __init__(self, queue):
            self.output = queue
    
        def write(self, string):
            self.output.put(string)
    
        def flush(self):
            pass
    
    class Gui(object):
    
        def __init__(self):
            self.a=Tk()
            b1 = Button(self.a, text="Process 1", command=self.func)
            b1.grid(row=0, column=0, pady=10, padx=10, sticky=SE)
    
            self.messages=Text(self.a, height=2.5, width=30, bg="light cyan",
                               state=NORMAL)
            self.messages.grid(row=1, column=0, columnspan=3)
    
            self.messages.tag_configure('error', foreground='red')
            # queues to get the stdout and stderr
            self.stdout = mp.Queue()
            self.stderr = mp.Queue()
            self.proc = None  # process
            self.a.mainloop()
    
        def stdout_update(self):
            self.messages.configure(state=NORMAL)
            while not self.stdout.empty():  # display stdout
                self.messages.insert('end', self.stdout.get(False))
            while not self.stderr.empty():  # display stderr
                self.messages.insert('end', self.stderr.get(False), 'error')
            self.messages.configure(state=DISABLED)
            if self.proc.is_alive(): # the process is not finished, schedule the next display update
                self.a.after(100, self.stdout_update)
    
        def func(self):
            self.proc = mp.Process(target=go, args=(self.stdout, self.stderr))
            self.proc.start()
            self.stdout_update()  # launch display update
    
    
    def go(queue_out, queue_err):
        # redirect stdout and stderr to queues
        sys.stdout = StdoutRedirector(queue_out)
        sys.stderr = StdoutRedirector(queue_err)
        for i in range(0, 2):
            time.sleep(1)
            print(i)
        1/0 # to test the stderr
    
    if __name__ == "__main__":
        Gui()
    

    【讨论】:

    • 谢谢!这正是我一直在寻找的! :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-02
    • 2013-12-16
    • 2015-08-31
    相关资源
    最近更新 更多