Как перенаправить ввод/вывод из терминала в pyqt5 виджет?

Пишу миниатюрную IDE для паскаля. Вот очень упрощенный кусок кода:

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        wid = QWidget()
        self.lay = QVBoxLayout()
        wid.setLayout(self.lay)
        self.setCentralWidget(wid)

        self.initUI()

    def initUI(self):

        self.inp = QTextEdit()
        self.out = QTextEdit()
        self.out.setReadOnly(True)

        self.lay.addWidget(self.inp)
        self.lay.addWidget(self.out)
   ...
   ...
       def run(self):
        try:
            with open('cached/run.pas', 'w') as f:
                f.write(self.inp.toPlainText())

            config = cfg .ConfigParser()
            config.read('settings.ini')
            compiler = config.get('settings-n-preference', 'engine')

            compiling_logs = os.popen(compiler + ' cached/run.pas').read()
            self.out.setText(os.popen('cached/run').read())

            os.remove('cached/run.pas')
            os.remove('cached/run')


        except:
            self.out.setText(compiling_logs)

Все пашет хорошо, если прога на паскале не требует инпута. Ну например:

program Hello;
begin
  writeln ('Hello, world.');
end.

Ну а если это уже что-то такое (то есть где надо что-то вводить), то прога нот-респондиться:

program Hello;
begin
  writeln ('Hello, world.');
  readln;
end.

Как быть?


Ответы (2 шт):

Автор решения: Alexander Chernin

Думаю, надо копать в сторону QProcess (докреф) и его каналов чтения/записи. Вот пример на Qt/C++ (скопитырен отсюда):

mplayerProcess->start("mplayer2 --slave --quiet input.mp4");
...
QByteArray command("pause 1\n");
mplayerProcess->write( command );

На питоне это должно быть, видимо, так:

mplayerProcess.start("mplayer2 --slave --quiet input.mp4")
...
command = "pause 1\n"
mplayerProcess.write(command)

В вашей задаче с вводом на readln, нужно создать что-то типа QTextEdit, в который вводить команды, которые передавать через канал записи QProcess (я так думаю, хотя и не пробовал). Неплохо было бы создать виджет и для канала чтения (для консольного вывода)

→ Ссылка
Автор решения: eri

Вот неблокирующий вариант. Команды укладывай в список что выполнить последовательно.

from PyQt5.QtCore import QProcess, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QWidget , QVBoxLayout, QTextEdit,QApplication, QLineEdit, QPushButton
import sys

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.__batch = []
        wid = QWidget()
        self.lay = QVBoxLayout()
        wid.setLayout(self.lay)
        self.setCentralWidget(wid)
        self.process =  QProcess()
        self.process.readyReadStandardOutput.connect(self.readyRead)
        self.process.readyReadStandardError.connect(self.readyError)
        self.process.finished.connect(self.runBatch)
        self.initUI()

    def initUI(self):
        self.inp = QTextEdit()
        self.out = QTextEdit()
        self.out.setReadOnly(True)
        self.line = QLineEdit()
        self.btnRun = QPushButton()

        self.lay.addWidget(self.inp)
        self.lay.addWidget(self.btnRun) # кнопка запуска
        self.lay.addWidget(self.out)
        self.lay.addWidget(self.line) # поле для ввода ... ввода )

        self.btnRun.pressed.connect(self.run)
        self.line.returnPressed.connect(self.readyWrite)

    def readyWrite(self):
        self.process.write(self.line.text().encode())
        self.process.write(b'\n')
        self.line.setText('')

    def readyError(self):
        self.out.append(bytes(self.process.readAllStandardError()).decode())

    def readyRead(self, *a):
        self.out.append(bytes(self.process.readAllStandardOutput()).decode())


    def runBatch(self):
        if self.__batch:
            cmd = self.__batch.pop(0)
            self.process.start(cmd, QProcess.Unbuffered | QProcess.ReadWrite)


    def run(self):
        with open('run.pas', 'w') as f:
            f.write(self.inp.toPlainText())
        self.out.setText('')
        self.__batch.append('fpc run.pas')
        self.__batch.append('./run')
        self.runBatch()


app = QApplication([])

win = MainWindow()

win.show()
sys.exit(app.exec())

Только есть проблемка с кодом в примере.

writeln ('Hello, world.');
readln;

Вывод буферизуется и не выводит 'Hello, world.' пока не нажмешь энтер...

→ Ссылка