QProcess останавливает связь после ввода определенной команды

Что я пытаюсь сделать?

Я пытаюсь построить Python Qt Gui, который позволит мне легко запускать / отлаживать встроенные программы для микроконтроллеров. В качестве компилятора я использую sdcc. Sdcc также содержит эмулятор.

В коде я запускаю дочерний QProcess, который будет запускать эмулятор микроконтроллера (s51).

В эмуляторе симуляция начинается с ввода r + RETURN ("r\n").

Симуляция может быть остановлена, нажав RETURN ("\n").

Моя проблема

Моя проблема в том, что мой код не может попасть на второй ВОЗВРАТ. Другими словами, симуляция продолжается до бесконечности. Так или иначе, дочерний процесс никогда не получает второе ВОЗВРАТ.

Действия по воспроизведению

  1. Установите Python, PyQt и sdcc.
  2. Скопируйте приведенный ниже код в файл emtpy и запустите его.
  3. В конце концов измените путь к исполняемому файлу s51.
  4. Нажмите на кнопку "Пуск".
  5. Войти r в поле ввода ниже и нажмите клавишу возврата. Симуляция должна начаться.
  6. Симуляция должна прекратиться при повторном нажатии клавиши 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_()

0 ответов

Другие вопросы по тегам