многопоточность с помощью wx.TextCtrl (или базового GTK +)

Я разрабатываю GUI для запуска внешней долгосрочной фоновой программы. Эта фоновая программа может быть введена командой ввода через stdin и использовать stdout и stderr для сохранения вывода выходных и сообщений об ошибках. Я использую объект wx.TextCtrl внутри GUI для ввода ввода и вывода. Мой текущий код выглядит следующим образом, который в основном основан на «как реализовать окно оболочки GUI»: wxPython: как создать окно оболочки bash?

Однако в моем следующем коде используется подход «предыдущий вывод буфера», т. Е. Я использую поток для буферизации вывода. Вывод буферизованной транзакции может отображаться только тогда, когда я даю следующую команду ввода и нажимаю кнопку «возврат». Теперь я хочу, чтобы выходные сообщения были своевременными, поэтому я хочу иметь эту функцию: «вывод всегда можно распечатать автоматически (непосредственно вымытый) из подпроцесса фона, и я также могу вмешаться, чтобы ввести некоторую команду ввода через stdin и напечатать выход.

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

Как мне изменить приведенный выше код для достижения этого? Должен ли я использовать поток? Могу ли я закодировать поток следующим образом?

 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) 

Однако, когда я попытался с вышеуказанным кодом, он сработает.

У меня есть ошибки от Gtk, следующим образом.

 (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 

или другое сообщение об ошибке, но разное сообщение об ошибке каждый раз, действительно странно!

Кажется, что wx.TextCtrl или базовый gui-контроль GTK + имеет некоторые проблемы с многопоточными. иногда я не ввожу никаких команд ввода, это также приводит к сбоям. Я выполняю поиск с определенного поста в Интернете и выглядит опасным для вызова элемента управления GUI из вторичного потока.

Я нашел свою ошибку. Как указано в wxpython – потоках и событиях окна или в главе 18 книги «WxPython in action» Ноэля и Робин:

Наиболее важным моментом является то, что операции с графическим интерфейсом должны выполняться в основном потоке или в том, в котором выполняется цикл приложения. Выполнение операций с графическим интерфейсом в отдельном потоке – хороший способ для вашего приложения сбой в непредсказуемых и труднодоступных ситуациях, способы отладки …

Моя ошибка в том, что я пытался передать объект wx.TextCtrl в другой поток. Это неправильно. Я пересмотрю свой дизайн.

    Самая большая проблема заключается в том, что вы не можете заставить подпроцесс не буферизовать свой вывод, а стандартная библиотека ввода-вывода большинства программ буферизует вывод, когда stdout является каналом (точнее, они будут переходить от буферизации строк к блоку буферизация). Такие инструменты, как Expect, исправляют это, запустив подпроцесс в псевдо-tty, что в основном подталкивает подпроцесс к мысли, что его выход идет на терминал.

    Существует модуль Python под названием Pexpect , который решает это так же, как и Expect. Я никогда не использовал его, так что предостерегаю .

    pty.spawn () может быть полезна. Также вы можете вручную создать PTY с помощью pty.openpty () и передать их в popen как stdin / stdout.

    Если у вас есть доступ к текстовому режиму, вы также можете отключить буферизацию.

    Не можете ли вы передать объект TextCtrl на ваш OutputThread и напрямую ли добавить текст в вывод, не связывая его с методом __enter?

    Вызывающий контракт для чего-либо в gtk (на котором построено wxpython и приложение, которое не соответствует утверждению) заключается в том, что вы должны только изменять элементы gui из потока Main Gui. Поэтому ответ для меня прост:

    В C # мне нужно было сделать следующее (это анонимная функция лямбда, и почти наверняка есть аналогичный вызов библиотеки в wxpython / gtk)

     Gtk.Application.Invoke((_,__) => { //code which can safely modify the gui goes here }); 

    И это должно решить ваши проблемы с утверждением … это было для меня, по крайней мере.