QProcess останавливает связь после ввода определенной команды
Что я пытаюсь сделать?
Я пытаюсь построить Python Qt Gui, который позволит мне легко запускать / отлаживать встроенные программы для микроконтроллеров. В качестве компилятора я использую sdcc. Sdcc также содержит эмулятор.
В коде я запускаю дочерний QProcess, который будет запускать эмулятор микроконтроллера (s51
).
В эмуляторе симуляция начинается с ввода r + RETURN ("r\n"
).
Симуляция может быть остановлена, нажав RETURN ("\n"
).
Моя проблема
Моя проблема в том, что мой код не может попасть на второй ВОЗВРАТ. Другими словами, симуляция продолжается до бесконечности. Так или иначе, дочерний процесс никогда не получает второе ВОЗВРАТ.
Действия по воспроизведению
- Установите Python, PyQt и sdcc.
- Скопируйте приведенный ниже код в файл emtpy и запустите его.
- В конце концов измените путь к исполняемому файлу s51.
- Нажмите на кнопку "Пуск".
- Войти
r
в поле ввода ниже и нажмите клавишу возврата. Симуляция должна начаться. - Симуляция должна прекратиться при повторном нажатии клавиши Return. Но это не так.
Заметки:
Я написал код для Fedora Linux. Если вы работаете в Ubuntu, вам, возможно, придется изменить путь к исполняемому файлу на
/usr/bin/s51
(Я не уверен в точном пути).Код отлично работает на некоторых других консольных приложениях, таких как bash, python, ifconfig, echo, gcc,...
Я считаю, что проблема как-то связана с тем, что sdcc выполняет ioct сразу после прочтения команды "r\n" (найдена с использованием strace):
ioctl(0, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
EDIT1: я бегу Qt 4.8.3
EDIT2: упрощен и исправлен код + добавлен скриншот
Скриншот
Симуляция должна была остановиться после второго ВОЗВРАТА.
(Я вошел h run\n
, run\n
, \n
, \n
, \help
) Снимок экрана можно найти здесь (не хватает репутации)
SimpleConsole.py:
#!/usr/bin/env python3
import sys
from PyQt4 import Qt, QtGui, QtCore
class SimpleConsole(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setup()
self.process = None
def setup(self):
l = QtGui.QHBoxLayout()
lbl = QtGui.QLabel('Command:')
self.processExecutable = QtGui.QLineEdit()
self.processExecutable.setEnabled(True)
self.processExecutable.returnPressed.connect(self.startStopProcess)
self.processExecutable.setText('/usr/libexec/sdcc/s51')
lbl.setBuddy(self.processExecutable)
l.addWidget(lbl)
l.addWidget(self.processExecutable)
self.processOutputWidget = QtGui.QTextEdit()
self.processOutputWidget.setReadOnly(True)
self.processInputWidget = QtGui.QLineEdit()
self.processInputWidget.setEnabled(False)
self.processInputWidget.returnPressed.connect(self.inputForProcess)
main = QtGui.QVBoxLayout()
main.addLayout(l)
main.addWidget(self.processOutputWidget)
main.addWidget(self.processInputWidget)
widget = QtGui.QWidget()
widget.setLayout(main)
self.setCentralWidget(widget)
self.setStatusBar(QtGui.QStatusBar())
self.statusBarLabel = QtGui.QLabel()
self.statusBar().addWidget(self.statusBarLabel)
self.toolbar = QtGui.QToolBar()
self.addToolBar(self.toolbar)
self.startStopAction = self.toolbar.addAction('Start')
self.startStopAction.triggered[bool].connect(self.startStopProcess)
def closeEvent(self, event):
self.stopProcess()
@QtCore.pyqtSlot(QtCore.QProcess.ProcessState)
def processStateChanged(self, newState):
self.processInputWidget.setEnabled(newState == QtCore.QProcess.Running)
pid = self.process.pid()
if newState == QtCore.QProcess.Running:
self.startStopAction.setText('Stop')
self.startStopAction.setEnabled(True)
self.processExecutable.setEnabled(False)
elif newState == QtCore.QProcess.NotRunning:
self.processInputWidget.setEnabled(False)
self.processInputWidget.clear()
self.startStopAction.setText('Start')
self.startStopAction.setEnabled(True)
self.processExecutable.setEnabled(True)
self.process = None
status = {QtCore.QProcess.NotRunning:'not running',
QtCore.QProcess.Starting:'starting',
QtCore.QProcess.Running:'running'}
self.statusBarLabel.setText('Process state change: {newState}. (pid={pid})'.format(newState=status[newState], pid=pid))
@QtCore.pyqtSlot()
def startStopProcess(self):
if self.process:
self.stopProcess()
else:
self.processOutputWidget.clear()
self.process = QtCore.QProcess(self)
self.process.stateChanged.connect(self.processStateChanged)
self.process.readyReadStandardOutput.connect(self.processOutputRead)
self.process.readyReadStandardError.connect(self.processErrorRead)
exe = self.processExecutable.text()
self.process.start(exe, QtCore.QIODevice.ReadWrite)
def moveCursorToEnd(self):
self.processOutputWidget.moveCursor(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
@QtCore.pyqtSlot()
def inputForProcess(self):
cmd = self.processInputWidget.text() + '\n'
self.moveCursorToEnd()
self.processOutputWidget.insertPlainText(cmd)
self.process.writeData(bytearray(cmd, encoding='utf-8'))
self.process.waitForBytesWritten()
self.processInputWidget.clear()
@QtCore.pyqtSlot()
def processOutputRead(self):
bytesOut = bytes(self.process.readAllStandardOutput()).decode('utf-8')
self.moveCursorToEnd()
self.processOutputWidget.insertPlainText(bytesOut)
@QtCore.pyqtSlot()
def processErrorRead(self):
bytesError = bytes(self.process.readAllStandardError()).decode('utf-8')
self.moveCursorToEnd()
self.processOutputWidget.insertPlainText(bytesError)
def stopProcess(self):
if self.process:
self.process.closeWriteChannel()
if not self.process.waitForFinished(500):
self.process.terminate()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = SimpleConsole()
w.show()
app.exec_()