wx.TextCtrl(或基础GTK +)的多线程问题 [英] multithreading issue with wx.TextCtrl (or underlying GTK+)

查看:77
本文介绍了wx.TextCtrl(或基础GTK +)的多线程问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发GUI以启动外部长期运行的后台程序.可以通过stdin给该后台程序输入命令,并使用stdout和stderr保持打印输出和错误消息.我在GUI内使用wx.TextCtrl对象提供输入和打印输出.我当前的代码如下,其主要灵感来自如何实现Shell GUI窗口"帖子:

I am developing a GUI to launch an external long-term running background program. This background program can be given input command via stdin and use stdout and stderr to keep printing out output and error messages. I use a wx.TextCtrl object inside the GUI to give input and print output. My current code is as follows, which is mainly inspired by the "how to implemente a shell GUI window" post: wxPython: how to create a bash shell window?

但是,我的以下代码使用缓冲先前的输出"方法,即,我使用线程来缓冲输出.只有当我发出下一个输入命令并按返回"按钮时,才可以呈现缓冲的事务输出.现在,我想及时看到输出消息,因此,我希望具有这样的功能:始终可以从后台子进程中自发打印输出(直接冲洗掉),而且我还可以通过stdin和打印输出.

However, my following code uses "buffer previous output" approach, i.e., I use a thread to buffer the output. The buffered transaction output could be only rendered when I give next input command and press "return" button. Now, I want to see the output messages timely, therefore I want to have the feature that "the output can always be spontaneously printed out (directly flushed out) from the background subprocess and I could also interfere to type some input command via stdin and have the output printed.

class BashProcessThread(threading.Thread):
    def __init__(self, readlineFunc):
        threading.Thread.__init__(self)
        self.readlineFunc = readlineFunc
        self.lines = []
        self.outputQueue = Queue.Queue()
        self.setDaemon(True)

    def run(self):
        while True:
           line = self.readlineFunc()
           self.outputQueue.put(line)
           if (line==""):
            break
        return ''.join(self.lines)

    def getOutput(self):
        """ called from other thread """            
        while True:
            try:
                line = self.outputQueue.get_nowait()
                lines.append(line)
            except Queue.Empty:
                break
        return ''.join(self.lines)

class myFrame(wx.Frame):
    def __init__(self, parent, externapp):
        wx.Window.__init__(self, parent, -1, pos=wx.DefaultPosition)
        self.textctrl = wx.TextCtrl(self, style=wx.TE_PROCESS_ENTER|wx.TE_MULTILINE)
        launchcmd=["EXTERNAL_PROGRAM_EXE"]
        p = subprocess.Popen(launchcmd, stdin=subprocess.PIPE, 
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        self.outputThread = BashProcessThread(p.stdout.readline)
        self.outputThread.start()
        self.__bind_events()         
        self.Fit()

    def __bind_events(self):
        self.Bind(wx.EVT_TEXT_ENTER, self.__enter)

    def __enter(self, e):
        nl=self.textctrl.GetNumberOfLines()
        ln =  self.textctrl.GetLineText(nl-1)
        ln = ln[len(self.prompt):]     
        self.externapp.sub_process.stdin.write(ln+"\n")
        time.sleep(.3)
        self.textctrl.AppendText(self.outputThread.getOutput())

我应该如何修改以上代码来实现此目的?我还需要使用线程吗?我可以对线程进行如下编码吗?

How should I modify the above code to achieve this? Do I still need to use thread? Can I code a thread as follows?

class PrintThread(threading.Thread):
    def __init__(self, readlineFunc, tc):
        threading.Thread.__init__(self)
        self.readlineFunc = readlineFunc
        self.textctrl=tc
        self.setDaemon(True)

    def run(self):
        while True:
            line = self.readlineFunc()
            self.textctrl.AppendText(line)

但是,当我尝试使用上面的代码时,它崩溃了.

However, when I tried with the above code, it crashes.

我来自Gtk的错误如下.

I have errors from Gtk, as follows.

(python:13688): Gtk-CRITICAL **: gtk_text_layout_real_invalidate: assertion `layout->wrap_loop_count == 0' failed
Segmentation fault

有时甚至是错误

 (python:20766): Gtk-CRITICAL **: gtk_text_buffer_get_iter_at_mark: assertion `GTK_IS_TEXT_MARK (mark)' failed
Segmentation fault

有时甚至是错误

(python:21257): Gtk-WARNING **: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position across buffer modifications.
You can apply tags and insert marks without invalidating your iterators,
but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset)
will invalidate all outstanding iterators
Segmentation fault

有时甚至是错误

Gtk-ERROR **: file gtktextlayout.c: line 1113 (get_style): assertion failed: (layout->one_style_cache == NULL)
aborting...
Aborted

或其他错误消息,但是每次都有不同的错误消息,真的很奇怪!

or other error message, but different error message each time, really weird!

似乎wx.TextCtrl或GTK +的基础gui控件在多线程方面存在一些问题.有时我不输入任何输入命令,它也会崩溃.我从互联网上的某个帖子中搜索,看起来从辅助线程调用GUI控件似乎很危险.

It seems the wx.TextCtrl or the underlying gui control of GTK+ has some problem with multi-threading. sometimes I do not type any input commands, it also crashes. I search from the certain post on the internet, and looks like it is dangerous to call a GUI control from a secondary thread.

我发现了我的错误.正如 wxpython-线程和窗口事件或本书第18章所指出的那样Noel和Robin的"WxPython实际应用":

I found my mistake. As pointed out in wxpython -- threads and window events or in chapter 18 of the book "WxPython in action" by Noel and Robin:

最重要的一点是,GUI操作必须在主线程中或在应用程序循环所运行的线程中进行.在单独的线程中运行GUI操作是使应用程序崩溃的原因之一调试方式...

The most important point is that GUI operations must take place in the main thread, or the one that the application loop is running in. Running GUI operations in a separate thread is a good way for your application to crash in unpredictable and hard-to-debug ways...

我的错误是我试图将wx.TextCtrl对象传递给另一个线程.这个不对.我将重新考虑我的设计.

My mistake is I tried to pass the wx.TextCtrl object to another thread. This is not right. I will reconsider my design.

推荐答案

最大的问题是,您不能强制子进程不缓冲其输出,并且大多数程序的标准I/O库将在以下情况下缓冲输出: stdout是一个管道(更准确地说,它们将从行缓冲变为块缓冲).诸如Expect之类的工具通过在伪tty中运行子流程来解决此问题,这基本上可以欺骗子流程,使其认为其输出将到达终端.

The biggest problem is that you can't force the subprocess not to buffer its output, and the standard I/O library of most programs will buffer the output when stdout is a pipe (more accurately, they will go from line-buffering to block buffering). Tools like Expect fix this by running the subprocess in a pseudo-tty, which basically tricks the subprocess into thinking its output is going to a terminal.

有一个名为 Pexpect 的Python模块,该模块可以通过相同的方式解决此问题期望的方式.我从未使用过它,所以腔体清空器.

There is a Python module called Pexpect, which solves this in the same way that Expect does. I've never used it, so caveat emptor.

这篇关于wx.TextCtrl(或基础GTK +)的多线程问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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