Я разрабатываю графический интерфейс для запуска внешней долгосрочной фоновой программы.Этой фоновой программе можно дать команду ввода через stdin и использовать stdout и stderr для продолжения распечатки выходных сообщений и сообщений об ошибках.Я использую объект wx.TextCtrl внутри графического интерфейса для ввода и вывода на печать.Мой текущий код выглядит следующим образом, что в основном вдохновлено постом «Как реализовать окно графического интерфейса оболочки»: 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 или базового графического элемента управления GTK + есть некоторые проблемы с многопоточностью. Иногда я не набираю никаких команд ввода, это также вылетает. Я ищу из определенного поста в Интернете и ищукак опасно вызывать элемент управления GUI из вторичного потока.
Я нашел свою ошибку.Как указано в wxpython - потоки и события окна или в главе 18 книги «WxPython в действии» Ноэля и Робина:
Наиболее важным моментом является то, что GUIоперации должны выполняться в главном потоке или в том цикле, в котором выполняется цикл приложения. Выполнение операций с графическим интерфейсом в отдельном потоке является хорошим способом сбоя приложения непредсказуемым и трудным для отладки способом ...
Моя ошибка в том, что я пытался передать объект wx.TextCtrl
в другой поток.Это неправильно.Я пересмотрю свой дизайн.