Inter-Process Communication
The QProcess class allows us to execute and interact with external programs. The class works asynchronously, doing its work in the background so that the user interface remains responsive. QProcess emits signals to notify us when the external process has data or has finished.
We will develop a small application that provides a user interface for an external image conversion program. For this example, we make use of the ImageMagick convert program, which is freely available for all major platforms.
Figure 10.1. The Image Converter application
The Image Converter's user interface was created in Qt Designer. The .ui file is on the CD that accompanies this book. Here, we will focus on the .ui.h file that contains the code. Note that the process and fileFilters variables were declared in Qt Designer's Members tab as follows:
QProcess *process; QString fileFilters;
The uic tool includes these variables as part of the generated ConvertDialog class.
void ConvertDialog::init() { process = 0; QStringList imageFormats = QImage::outputFormatList(); targetFormatComboBox->insertStringList(imageFormats); fileFilters = tr("Images") + " (*." + imageFormats.join(" *.").lower() + ")"; }
A file filter consists of a descriptive text and one or more wildcard patterns(for example, "Text files(*.txt)"). The QImage::outputFormatList() function returns a list of the image output formats that are supported by Qt. This list can vary depending on the options that were selected when Qt was installed.
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); } }
The dialog's Browse button is connected to the browse() slot. If the user has previously selected a file, we initialize the file dialog with that file's path; otherwise, we use the user's home directory.
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(); }
The dialog's Convert button is connected to the convert() slot. We copy the source file's name and change its suffix to match the target file format.
We then create a QProcess object. The first argument given to a QProcess object using addArgument() is the name of the external program to execute. Subsequent arguments become this program's arguments.
We connect the Qprocess's readyReadStderr() to the dialog's updateOutputTextEdit() slot to display error messages from the external program in the dialog's QTextEdit as they are generated. We also connect the Qprocess's processExited() signal to the dialog's slot of the same name.
void ConvertDialog::updateOutputTextEdit() { QByteArray data = process->readStderr(); QString text = outputTextEdit->text() + QString(data); outputTextEdit->setText(text); }
Whenever the external process writes to stderr, our updateOutputTextEdit() slot is called. We read the error text and append it to the 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); }
When the process has finished, we let the user know the outcome and then delete the process.
Wrapping a console application in this way can be useful because it allows us to make use of preexisting functionality rather than having to implement that functionality ourselves. Another use of QProcess is to launch other GUI applications, such as a web browser or an email client.