=> Главная База Знаний Qt Связь между процессами


Связь между процессами

Связь между процессами

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

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

Рис. 12.2. Приложение Image Converter.

Интерфейс пользователя приложения Image Converter (конвертор изображений) был создан при помощи Qt Designer. Файл .ui находится на компакт-диске, который входит в состав данной книги. Здесь мы основное внимание уделим подклассу, который является наследником сгенерированного компилятором uic класса Ui::ConvertDialog, и начнем с заголовочного файла:

01 #ifndef CONVERTDIALOG_H

02 #define CONVERTDIALOG_H

03 #include <QDialog>

04 #include <QProcess>

05 #include "ui_convertdialog.h"

06 class ConvertDialog : public QDialog,

07 public Ui::ConvertDialog

08 {

09 Q_OBJECT

10 public:

11 ConvertDialog(QWidget *parent = 0);

12 private slots:

13 void on_browseButton_clicked();

14 void on_convertButton_clicked();

15 void updateOutputTextEdit();

16 void processFinished(int exitCode, QProcess::ExitStatus exitStatus);

17 void processError(QProcess::ProcessError error);

18 private:

19 QProcess process;

20 QString targetFile;

21 };

22 #endif

Этот заголовочный файл создается по тому знакомому образцу, который используется в подклассах форм Qt Designer. Благодаря механизму автоматического связывания QtDesigner слоты on_browseButton_clicked() и on_convertButton_clicked() автоматически связываются с сигналом clicked() кнопок Browse (просмотреть) и Convert (преобразовать).

01 ConvertDialog::ConvertDialog(QWidget *parent)

02 : QDialog(parent)

03 {

04 setupUi(this);

05 connect(&process, SIGNAL(readyReadStandardError()),

06 this, SLOT(updateOutputTextEdit()));

07 connect(&process, SIGNAL(finished(int, QProcess::ExitStatus)),

08 this, SLOT(processFinished(int, QProcess::ExitStatus)));

09 connect(&process, SIGNAL(error(QProcess::ProcessError)),

10 this, SLOT(processError(QProcess::ProcessError)));

11 }

Вызов setupUi() создает и компонует все виджеты форм, устанавливает соединения сигнал—слот для слотов on_objectName_signalName() и связывает кнопку Quit (выйти) с функцией QDialog::accept(). После этого мы вручную связываем три сигнала объекта QProcess с тремя закрытыми слотами. Любые сообщения внешнего процесса для потока cerr мы будем обрабатывать в функции updateOutputTextEdit().

01 void ConvertDialog::on_browseButton_clicked()

02 {

03 QString initialName = sourceFileEdit->text();

04 if (initialName.isEmpty())

05 initialName = QDir::homePath();

06 QString fileName = QFileDialog::getOpenFileName(this,

07 tr("Choose File"), initialName);

08 fileName = QDir::convertSeparators(fileName);

09 if (!fileName.isEmpty()) {

10 sourceFileEdit->setText(fileName);

11 convertButton->setEnabled(true);

12 }

13 }

Сигнал clicked() кнопки Browse (просмотреть) автоматически связывается в функции setupUi() со слотом on_browseButton_clicked(). Если пользователь ранее выбирал какой-нибудь файл, мы инициализируем диалоговое окно выбора файла именем этого файла; в противном случае мы используем домашний каталог пользователя.

01 void ConvertDialog::on_convertButton_clicked()

02 {

03 QString sourceFile = sourceFileEdit->text();

04 targetFile = QFileInfo(sourceFile).path()

05 + QDir::separator() + QFileInfo(sourceFile).baseName()

06 + "." + targetFormatComboBox->currentText().toLower();

07 convertButton->setEnabled(false);

08 outputTextEdit->clear();

09 QStringList args;

10 if (enhanceCheckBox->isChecked())

11 args << "-enhance";

12 if (monochromeCheckBox->isChecked())

13 args << "-monochrome";

14 args << sourceFile << targetFile;

15 process.start("convert", args);

16 }

Когда пользователь нажимает кнопку Convert (преобразовать), мы копируем имя исходного файла и изменяем его расширение в соответствии с новым форматом файла. Мы используем зависимый от платформы разделитель каталогов ('/' или '\' возвращается функцией QDir::separator()) вместо жесткого кодирования этих символов, поскольку пользователь будет видеть имя файла.

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

Для инициирования внешнего процесса мы вызываем функцию QProcess::start() с именем программы, которая должна выполняться (convert), и всеми ее аргументами. В данном случае мы передаем флажки -enhance и -monochrome, если пользователь выбрал соответствующие опции, и затем имена исходного и целевого файлов. Тип выполняемого преобразования программа convert определяет по расширениям файлов.

01 void ConvertDialog::updateOutputTextEdit()

02 {

03 QByteArray newData = process.readAllStandardError();

04 QString text = outputTextEdit->toPlainText()

05 + QString::fromLocal8Bit(newData);

06 outputTextEdit->setPlainText(text);

07 }

При всякой записи внешним процессом в поток cerr вызывается слот updateOutputTextEdit(). Мы считываем текст сообщения об ошибке и добавляем его в существующий текст QTextEdit.

01 void ConvertDialog::processFinished(int exitCode,

02 QProcess::ExitStatus exitStatus)

03 {

04 if (exitStatus == QProcess::CrashExit) {

05 outputTextEdit->append(tr("Conversion program crashed"));

06 } else if (exitCode != 0) {

07 outputTextEdit->append(tr("Conversion failed"));

08 } else {

09 outputTextEdit->append(tr("File %1 created").arg(targetFile));

10 }

11 convertButton->setEnabled(true);

12 }

По окончании процесса мы уведомляем пользователя о результате и включаем кнопку Convert.

01 void ConvertDialog::processError(QProcess::ProcessError error)

02 {

03 if (error == QProcess::FailedToStart) {

04 outputTextEdit->append(tr("Conversion program not found"));

05 convertButton->setEnabled(true);

06 }

07 }

Если процесс не удается запустить, QProcess генерирует сигнал error() вместо finished(). Мы выдаем сообщение об ошибке и включаем кнопку Convert.

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

Одним из распространенных примеров, где желателен синхронный режим работы, является приложение, обеспечивающее редактирование простых текстов с применением текстового редактора, предпочитаемого пользователем. Такое приложение реализуется достаточно просто с помощью QProcess. Например, пусть в QTextEdit содержится простой текст и имеется кнопка Edit, при нажатии на которую выполняется слот edit().

01 void ExternalEditor::edit()

02 {

03 QTemporaryFile outFile;

04 if (!outFile.open())

05 return;

06 QString fileName = outFile.fileName();

07 QTextStream out(&outFile);

08 out << textEdit->toPlainText();

09 outFile.close();

10 QProcess::execute(editor, QStringList() << options << fileName);

11 QFile inFile(fileName);

12 if (!inFile.open(QIODevice::ReadOnly))

13 return;

14 QTextStream in(&inFile);

15 textEdit->setPlainText(in.readAll());

16 }

Мы используем QTemporaryFile для создания пустого файла с уникальным именем. Мы не задаем аргументы функции QTemporaryFile::open(), поскольку для нас подходит ее режим по умолчанию, по которому файл открывается для чтения и записи. Мы записываем содержимое поля редактирования во временный файл и затем закрываем файл, потому что некоторые текстовые редакторы не могут работать с уже открытыми файлами.

Статическая функция QProcess::execute() запускает внешний процесс и блокирует работу приложения до завершения процесса. Аргумент editor в строке типа QString содержит имя исполняемого модуля редактора (например, «gvim»). Аргумент options является списком QStringList (который содержит один элемент, «—f», если мы используем gvim).

После закрытия пользователем текстового редактора процесс завершает свою работу и функция execute() возвращает управление. Затем мы открываем временный файл и считываем его содержимое в QTextEdit. QTemporaryFile автоматически удаляет временный файл, когда объект выходит из области видимости.

При синхронной работе QProcess нет необходимости устанавливать соединения сигнал—слот. Если требуется более тонкое управление, чем то, которое обеспечивает статическая функция execute(), мы можем использовать альтернативный подход. Это означает создание объекта QProcess и вызов для него функции start() с последующей установкой блокировки путем вызова функции QProcess::waitForStarted(), после успешного завершения которой вызывается функция QProcess::waitForFinished(). Пример применения этого подхода можно найти в справочной документации по классу QProcess.

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

QProcess может также применяться для запуска других приложений с графическим пользовательским интерфейсом, например веб—браузера или почтового клиента. Однако если нашей целью является связь между приложениями, а не просто запуск одного из другого, то лучше установить прямую связь между приложениями, используя Qt—классы, предназначенные для работы с сетью, или расширение ActiveQt для Windows.