Как перенаправить ввод/вывод из терминала в 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 шт):
Думаю, надо копать в сторону 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 (я так думаю, хотя и не пробовал). Неплохо было бы создать виджет и для канала чтения (для консольного вывода)
Вот неблокирующий вариант. Команды укладывай в список что выполнить последовательно.
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.' пока не нажмешь энтер...