[ fromfile: qprocess.xml id: qprocess ]
QProcess is a convenient (and cross-platform) class for starting and controlling other processes. It is derived from QObject and takes full advantage of signals and slots to make it easier to "hook up" with other Qt classes.
Now consider a simple example that starts a process and views its continually running output.[98] Example 17.1 shows the definition of a simple class derived from QProcess.
Example 17.1. src/logtail/logtail.h
[ . . . . ] #include <QObject> #include <QProcess> class LogTail : public QProcess { Q_OBJECT public: LogTail(QString fn = QString()); ~LogTail(); signals: void logString(const QString &str); public slots: void logOutput(); }; [ . . . . ]
<include src="src/logtail/logtail.h" href="src/logtail/logtail.h" id="logtailh" mode="cpp"/>
A QProcess can launch[99] another process using the start()
function.
The new process is a child process that terminates when the parent process does.[100]
Example 17.2 shows the implementation of the constructor and destructor of the LogTail
class.
Example 17.2. src/logtail/logtail.cpp
[ . . . . ] LogTail::LogTail(QString fn) { connect (this, SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput())); QStringList argv; argv << "-f" << fn; start("tail", argv); } LogTail::~LogTail() { terminate(); }
<include src="src/logtail/logtail.cpp" mode="cpp" href="src/logtail/logtail.cpp" id="logtailctor" segid="constructor"/>
The child process can be treated as a sequential I/O device with two predefined output channels that represent two separate streams of data: stdout
and stderr.
The parent process can select an output channel with setReadChannel()
(default is stdout).
The signal readyReadStandardOutput()
is emitted when data is available on the selected channel of the child process.
The parent process can then read its output by calling read()
, readLine()
, or getChar()
.
If the child process has standard input enabled, the parent can use write()
to send data to it.
Example 17.3 shows the implementation of the slot logOutput()
which is connected to the signal readyReadStandardOutput()
and uses readAllStandardOutut()
so that it pays attention only to stdout
.
Example 17.3. src/logtail/logtail.cpp
[ . . . . ] // tail sends its output to stdout. void LogTail::logOutput() { QByteArray bytes = readAllStandardOutput(); QStringList lines = QString(bytes).split("\n"); foreach (QString line, lines) { emit logString(line); } }
<include src="src/logtail/logtail.cpp" mode="cpp" href="src/logtail/logtail.cpp" id="logoutput" segid="logOutput"/>
The use of signals eliminates the need for a read loop. When there is no more input to be read, the slot will no longer be called. Signals and slots make concurrent code much simpler to read, because they hide the event-handling and dispatching code. Some client code is given in Example 17.4.
Example 17.4. src/logtail/logtail.cpp
[ . . . . ] int main (int argc, char* argv[]) { QApplication app(argc, argv); QStringList al = app.arguments(); QTextEdit textEdit; textEdit.setWindowTitle("Debug"); textEdit.setWindowTitle("logtail demo"); QString filename; if (al.size() > 1) filename = al[1]; LogTail tail(filename); tail.connect (&tail, SIGNAL(logString(const QString&)), &textEdit, SLOT(append(const QString&))); textEdit.show(); return app.exec(); }
<include src="src/logtail/logtail.cpp" mode="cpp" href="src/logtail/logtail.cpp" id="logtailclient" segid="main"/>
This application appends lines to the QTextEdit whenever they appear in the specified log file.
To demonstrate the LogTail
application you need a text file that constantly grows as lines are added to it; for example, some kind of active log file.
If you can't find one, you can create one using a tool such as top
, a utility available on a typical *nix host.
Normally top
, with no command-line arguments, produces a plain-text, formatted screen that lists, in descending order of resource usage, the 25 running processes that use the most system resources at the moment.
The display starts with a summary of system usage specs and the entire display is updated every few seconds.
top
continues until it is terminated by the user.
In this example, we launch top
with command-line arguments:
-b
, which puts it into batch mode so that we can redirect its output
-d 1.0
, which specifies the number of seconds between updates
> toplog
, which redirects output to the file toplog
&, which runs top
as a background process
After that, run the logtail
example on the result file:
top -b -d 1.0 > toplog & ./logtail toplog
Figure 17.1 is a screenshot of the running program.
The demo runs until terminated, after which you must also kill the top
job, whose [job number] and process id were displayed just after it was launched.
Using bash
, you need only the job number (%1
) to kill the job.
src/logtail> top -b -d 1.0 > toplog & [1] 24209 src/logtail> ./logtail toplog [[ logtail was terminated here. ]] QProcess: Destroyed while process is still running. src/logtail> kill 24209 src/logtail>
[98] tail -f
runs forever showing whatever is appended to a file and is useful for showing the contents of a log file of a running process.
[99] Underscoring the value of the cross-platform QProcess API is the fact that the mechanism for one process to launch another differs considerably in the two leading operating system families. For more information about how it is handled in *nix systems, see the Wikipedia article Fork. The Microsoft Windows approach is described in Spawn.
[100] It is also possible to use startDetached()
to start a process that can live after the parent process dies.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |