10.4. Взаимодействия между процессами.

Класс QProcess позволяет запускать и взаимодействовать с другими программами. Экземпляры класса работают асинхронно, выполняя всю работу в фоновом режиме, что не приводит к "замораживанию" пользовательского интерфейса. QProcess может известить приложение о завершении запущенной им программы или о наличии данных, полученных от нее, выдавая соответствующие сигналы.

В демонстрационных целях напишем небольшое приложение, которое предоставит пользователю интерфейс с внешней программой преобразования графических файлов. Для данного примера будет использоваться программа convert из пакета ImageMagick, которая доступна для большинства платформ.

Рисунок 10.1. Внешний вид приложения Image Converter.


Форма приложения была разработана в среде визуального построителя интерфейсов Qt Designer. Соответствующий .ui находится на CD, сопровождающем книгу. Здесь же мы сконцентрируем все свое внимание на содержимом файла .ui.h, который содержит исходный код. Обратите внимание: переменные process и fileFilters были объявлены в Qt Designer, на вкладке Members следующим образом: QProcess *process; QString fileFilters; Утилита uic добавляет эти переменные в класс ConvertDialog. void ConvertDialog::init() { process = 0; QStringList imageFormats = QImage::outputFormatList(); targetFormatComboBox->insertStringList(imageFormats); fileFilters = tr("Images") + " (*." + imageFormats.join(" *.").lower() + ")"; } Переменная fileFilters содержит текст описания и один, или более, шаблонов имен файлов (например, "Text files (*.txt)"). Функция QImage::outputFormatList() возвращает список форматов изображений, поддерживаемых Qt. Этот список тесно связан с опциями, выбранными при установке библиотки. void ConvertDialog::browse() { QString initialName = sourceFileEdit->text(); if (initialName.isEmpty()) initialName = QDir::homeDirPath(); QString fileName = QFileDialog::getOpenFileName(initialName, fileFilters, this); fileName = QDir::convertSeparators(fileName); if (!fileName.isEmpty()) { sourceFileEdit->setText(fileName); convertButton->setEnabled(true); } } Кнопка Browse связана со слотом browse(). Если ранее пользователь уже выбирал файл, то путь поиска, для диалога выбора файла, назначается исходя из полного имени предыдущего файла, в противном случае, открывается домашний каталог. void ConvertDialog::convert() { QString sourceFile = sourceFileEdit->text(); targetFile = QFileInfo(sourceFile).dirPath() + QDir::separator() + QFileInfo(sourceFile).baseName(); targetFile += "."; targetFile += targetFormatComboBox->currentText().lower(); convertButton->setEnabled(false); outputTextEdit->clear(); process = new QProcess(this); process->addArgument("convert"); if (enhanceCheckBox->isChecked()) process->addArgument("-enhance"); if (monochromeCheckBox->isChecked()) process->addArgument("-monochrome"); process->addArgument(sourceFile); process->addArgument(targetFile); connect(process, SIGNAL(readyReadStderr()), this, SLOT(updateOutputTextEdit())); connect(process, SIGNAL(processExited()), this, SLOT(processExited())); process->start(); } Кнопка Convert связана со слотом convert(). По сигналу от кнопки собирается имя целевого файла, из имени исходного файла и расширения, соответствующего заданному формату.

Затем создается экземпляр класса QProcess. После этого собирается список аргументов командной строки, с помощью функции addArgument(). Первым идет имя файла внешней программы. Далее следуют аргументы, которые будут ей передаваться.

После создания списка аргументов производится соединение сигнала readyReadStderr(), класса QProcess, со слотом updateOutputTextEdit() диалогового окна, чтобы выводить в QTextEdit сообщения от внешней программы, по мере их поступления. И затем соединяется сигнал processExited(), класса QProcess, со слотом processExited() диалогового окна.

void ConvertDialog::updateOutputTextEdit() { QByteArray data = process->readStderr(); QString text = outputTextEdit->text() + QString(data); outputTextEdit->setText(text); } Как только внешняя программа выдаст что нибудь на stderr, будет вызван слот updateOutputTextEdit(). Сообщение будет прочитано и записано в QTextEdit. void ConvertDialog::processExited() { if (process->normalExit()) { outputTextEdit->append(tr("File %1 created") .arg(targetFile)); } else { outputTextEdit->append(tr("Conversion failed")); } delete process; process = 0; convertButton->setEnabled(true); } По завершении внешнего процесса перед пользователем выводится соответствующее сообщение, после чего процесс удаляется.

Создание графического интерфейса, для консольных приложений, подобным образом, может оказаться очень полезным, потому что позволяет использовать функциональность, заложенную в уже существующие программы и нам не нужно ломать голову над собственной реализацией.