0

我有四个文本框和最多四个进程,我将开始使用多处理模块。我可以让进程正确执行,但我真的很想将每个进程的所有输出重定向到不同的 wx.TextCtrl,这样我就可以看到整个解决方案过程中发生了什么。我已经用一个线程和 sys.stdout 重定向成功地做到了这一点,如

http://www.velocityreviews.com/forums/t515815-wxpython-redirect-the-stdout-to-a-textctrl.html

但是类似的想法不适用于流程。有人可以为我破解这个问题的简单解决方案吗?我无法想象我是唯一遇到过这个问题的人。

4

2 回答 2

1

对于那些感兴趣的人,这里是一个工作代码片段,它可以满足我最初的问题。这是一种享受。

此代码创建一个包装进程的线程。在进程的 run() 函数中,stderr 和 stdout 使用重定向类重定向到管道。可以腌制管道,这是在流程的 run() 函数中使用它们的先决条件。

只要有输出等待,线程就会坐下来从管道中提取输出。使用 wx.CallAfter 函数将从管道中提取的文本写入 wx.TextCtrl。请注意,这是一个非阻塞调用,实际上这里的所有代码都是非阻塞的,这使得 GUI 具有响应性。请注意重定向类中的 flush() 函数也可以重定向 stderr。

注意:需要注意的一件事是,如果您尝试使用管道以过高的吞吐量进行读写,则 GUI 将锁定。但只要你的输出速度相当慢,就没有问题

import wx
import sys
import time
from multiprocessing import Pipe, Process
from threading import Thread

class RedirectText2Pipe(object):
    def __init__(self, pipe_inlet):
        self.pipe_inlet = pipe_inlet
    def write(self, string):
        self.pipe_inlet.send(string)
    def flush(self):
        return None

class Run1(Process):
    def __init__(self, pipe_inlet):
        Process.__init__(self)
        self.pipe_std = pipe_inlet

    def run(self):
        redir = RedirectText2Pipe(self.pipe_std)
        sys.stdout = redir
        sys.stderr = redir

        for i in range(100):
            time.sleep(0.01)
            print i,'Hi'

class RedirectedWorkerThread(Thread):
    """Worker Thread Class."""
    def __init__(self, stdout_target):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.stdout_target_ = stdout_target

    def run(self):
        """
        In this function, actually run the process and pull any output from the 
        pipes while the process runs
        """
        pipe_outlet, pipe_inlet = Pipe(duplex = False)
        p = Run1(pipe_inlet)
        p.daemon = True
        p.start()

        while p.is_alive():
            #Collect all display output from process
            while pipe_outlet.poll():
                wx.CallAfter(self.stdout_target_.WriteText, pipe_outlet.recv())

class MainFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,None)

        self.txt1 = wx.TextCtrl(self, style = wx.TE_MULTILINE|wx.TE_READONLY)
        self.txt2 = wx.TextCtrl(self, style = wx.TE_MULTILINE|wx.TE_READONLY)
        self.txt3 = wx.TextCtrl(self, style = wx.TE_MULTILINE|wx.TE_READONLY)
        self.btn = wx.Button(self, label='Run')
        self.btn.Bind(wx.EVT_BUTTON, self.OnStart)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.AddMany([(self.txt1,1,wx.EXPAND),(self.txt2,1,wx.EXPAND),(self.txt3,1,wx.EXPAND),self.btn])
        self.SetSizer(sizer)

    def OnStart(self, event):
        t1 = RedirectedWorkerThread(self.txt1)
        t1.daemon = True
        t1.start()

        t2 = RedirectedWorkerThread(self.txt2)
        t2.daemon = True
        t2.start()

        t3 = RedirectedWorkerThread(self.txt3)
        t3.daemon = True
        t3.start()

if __name__ == '__main__':
    app = wx.App(False)
    frame = MainFrame() 
    frame.Show(True) 
    app.MainLoop()
于 2012-06-30T15:24:35.143 回答
1

据我了解,在 wxPython 领域,您通常希望从线程运行进程。因此,您从进程到线程以及从线程返回到 wxPython 进行通信。我会使用某种命名方案将每个潜在进程与文本控件(可能是 1-4?)相关联,并将其传递回将使用 wx.CallAfter 或 wx.PostEvent 告诉 wx 更新的线程。

您也可以使用简单的 Python 套接字服务器来完成此操作。向服务器发布一条带有标头的消息,该标头说明它属于哪个文本控件。在 wx 部分,您可以让 wx.Timer 检查套接字服务器是否有新消息并根据需要进行更新。

于 2012-06-21T13:40:26.600 回答