Моя проблема немного отличается, так как я хотел собрать и stdout, и stderr из запущенного процесса, но, в конечном счете, то же самое, поскольку я хотел визуализировать вывод в виджете как сгенерированный.
Я не хотел прибегать ко многим из предложенных обходных путей с использованием очередей или дополнительных потоков, поскольку они не должны быть необходимы для выполнения такой распространенной задачи, как запуск другого сценария и сбор его выходных данных.
Прочитав предложенные решения и документы по Python, я решил свою проблему с помощью реализации ниже. Да, это работает только для POSIX, так как я использую select
вызов функции.
Я согласен с тем, что документы сбивают с толку, и реализация такой распространенной задачи сценариев неудобна. Я полагаю, что более старые версии python имеют разные значения по умолчанию Popen
и разные объяснения, что создавало много путаницы. Кажется, это хорошо работает как для Python 2.7.12, так и для 3.5.2.
Ключ должен был установить bufsize=1
буферизацию строки, а затем universal_newlines=True
обработать как текстовый файл вместо двоичного файла, который, по-видимому, становится настройкой по умолчанию bufsize=1
.
class workerThread(QThread):
def __init__(self, cmd):
QThread.__init__(self)
self.cmd = cmd
self.result = None ## return code
self.error = None ## flag indicates an error
self.errorstr = "" ## info message about the error
def __del__(self):
self.wait()
DEBUG("Thread removed")
def run(self):
cmd_list = self.cmd.split(" ")
try:
cmd = subprocess.Popen(cmd_list, bufsize=1, stdin=None
, universal_newlines=True
, stderr=subprocess.PIPE
, stdout=subprocess.PIPE)
except OSError:
self.error = 1
self.errorstr = "Failed to execute " + self.cmd
ERROR(self.errorstr)
finally:
VERBOSE("task started...")
import select
while True:
try:
r,w,x = select.select([cmd.stdout, cmd.stderr],[],[])
if cmd.stderr in r:
line = cmd.stderr.readline()
if line != "":
line = line.strip()
self.emit(SIGNAL("update_error(QString)"), line)
if cmd.stdout in r:
line = cmd.stdout.readline()
if line == "":
break
line = line.strip()
self.emit(SIGNAL("update_output(QString)"), line)
except IOError:
pass
cmd.wait()
self.result = cmd.returncode
if self.result < 0:
self.error = 1
self.errorstr = "Task terminated by signal " + str(self.result)
ERROR(self.errorstr)
return
if self.result:
self.error = 1
self.errorstr = "exit code " + str(self.result)
ERROR(self.errorstr)
return
return
ERROR, DEBUG и VERBOSE - это просто макросы, которые печатают вывод на терминал.
Это решение ИМХО эффективно на 99,99%, так как оно все еще использует readline
функцию блокировки , поэтому мы предполагаем, что подпроцесс хорош и выводит полные строки.
Я приветствую отзывы, чтобы улучшить решение, так как я все еще новичок в Python.