среда, 22 июня 2011 г.

Графический интерфейс для консольного приложения.

Так сложилось, что пользователям нужны графические интерфейсы. Виноваты в этом Microsoft, Apple или природа человека, которому удобней манипулировать визуальными объектами, может быть, и что-либо ещё, но факт остается фактом - программистам удобно и правильно использовать консольные приложения, а пользователям, будьте добры, предоставьте окошечки с кнопочками.

И так, ситуация: имеется уже написанное и отлаженное консольное приложение, необходимо предоставить к нему графический интерфейс. Один из вариантов на python+PyQt под катом.

Создадим простейшее приложение с графическим интерфейсом, которое позволит запускать произвольное консольное приложение с выводом его стандартных потоков stdout и stderr в QTextEdit.

В Qt Disigner создадим простую форму с полем для имени программы, опций и преобразуем в python-исходник с помощью pyuic4.

Далее, в конец метода инициализации формы setupUi добавляем:

self.process = QProcess()
self.process.readyReadStandardOutput.connect(self.readOutput)
self.process.readyReadStandardError.connect(self.readErrors)
self.process.finished.connect(self.finishedProcess)
self.pushButtonStart.clicked.connect(self.start)
self.pushButtonStop.clicked.connect(self.stop)

Здесь мы создаем экземпляр процесса и соединяем его сигналы readyReadStandardOutput, readyReadStandardError и finished с методами, которые опишем позже. Также зададим обработчики для нажатий кнопок. Кнопка Stop позволит нам принудительно прервать выполнение процесса.

Сигналы readyReadStandardOutput и readyReadStandardError позволяют нам узнать, когда приложение, запущенное в созданном процессе QProcess, записывает данные в стандартные потоки вывода типа stdout и stderr. Сигнал finished позволит нам узнать о завершении работы приложения и прочитать возвращаемое значение. Принято, что значение статуса "0" соответствует успешному завершению работы приложения, статус, отличный от нуля, - коду ошибки. Хотя, конечно, это зависит от автора этого приложения.

Функции запуска и останова выглядят следующим образом:

def start(self):
    self.process.start(self.lineEditCommand.text(), \
                       self.lineEditOptions.text().split(" "))
    self.process.waitForStarted()
        
def stop(self):
    self.process.close()
    self.process.waitForFinished()

Имхо, всё очень просто, пояснений не требуется.

Функции вывода данных потока также очень просты.

def outLogLine(self, line, color = Qt.black):
    self.textEditLog.moveCursor(QTextCursor.End)
    self.textEditLog.setTextColor(color)
    self.textEditLog.insertPlainText(line)
    c = self.textEditLog.textCursor();
    c.movePosition(QTextCursor.End);
    self.textEditLog.setTextCursor(c);
        
def readOutput(self):
    output = self.process.readAllStandardOutput()
    output = QTextCodec.codecForName('cp866').toUnicode(output)
    self.outLogLine(output, Qt.darkGreen)

def readErrors(self):
    output = self.process.readAllStandardError()
    output = QTextCodec.codecForName('cp866').toUnicode(output)
    self.outLogLine(output, Qt.red)

Нюанса два: во-первых, поскольку в винде, например, кодировка вывода консольных приложений cp-866, то необходима соответствующая перекодировка в unicode, во-вторых, курсор в QTextEdit устанавливаем с помощью setTextCursor, чтобы была автоматическая прокрутка лога на конец.

В общем, вот и всё. База для GUI-обертки любого консольного приложения готова. Исходник здесь. Всем удачи.
Хорошая статья здесь: Capturing Output from a Process.

Комментариев нет:

Отправить комментарий