17.1.2.  Qonsole: Writing an Xterm in Qt

[ fromfile: qprocess.xml id: qonsole ]

A command-line shell reads commands from the user and prints the output. In this example, we have a QTextEdit providing a view of the output of another running process, in this case, bash[99], the default command-line shell interpreter for most systems. The QProcess is a model, representing a running process. Figure 17.4 shows a screenshot of Qonsole, our first attempt at a GUI for a command shell.[100]

Figure 17.4. Qonsole1

Qonsole1

Because it connects signals to slots and handles user interactions, Qonsole is considered a controller. Because it derives from QMainWindow, it also contains some view code. The UML diagram in Figure 17.5 shows the relationships between the classes in this application.

Figure 17.5.  Qonsole UML: Model and View

Qonsole UML: Model and View

Example 17.6 shows the class definition for Qonsole.

Example 17.6. src/qonsole/qonsole1/qonsole.h

[ . . . . ]
class Qonsole : public QMainWindow {
    Q_OBJECT
 public:
    Qonsole();
 public slots:
    void execute();
    void showOutput();
 private:
    QTextEdit* m_Logw;
    QLineEdit* m_InputArea;
    QProcess* m_Shell;
};
[ . . . . ]

Example 17.7 shows the how constructor establishes the structure of Qonsole and the important connections between its components.

Example 17.7. src/qonsole/qonsole1/qonsole.cpp

[ . . . . ]

Qonsole::Qonsole() {
   m_Logw = new QTextEdit();
   m_Logw->setReadOnly(true);
   setCentralWidget(m_Logw);
   m_InputArea = new  QLineEdit();
   QDockWidget* qdw = new QDockWidget("Type commands here");
   qdw->setWidget(m_InputArea);
   addDockWidget(Qt::BottomDockWidgetArea, qdw);
   connect (m_InputArea, SIGNAL(returnPressed()),
			this, SLOT(execute()));

   m_Shell = new QProcess(this);
   m_Shell->setReadChannelMode(QProcess::MergedChannels);           1
   connect (m_Shell, SIGNAL(readyReadStandardOutput()),
			 this, SLOT(showOutput()));
#ifdef Q_OS_WIN
   m_Shell->start("cmd", QStringList(), QIODevice::ReadWrite);
#else
   m_Shell->start("bash", QStringList("-i"), QIODevice::ReadWrite); 2
#endif

}

1

Merge stdout and stderr.

2

Run bash in interactive mode.


Whenever the shell outputs anything, Qonsole sends it to the QTextEdit. Whenever the user presses the return key, Qonsole grabs any text that is in the QLineEdit and sends it to the shell, which interprets it as a command, as shown in Example 17.8.

Example 17.8. src/qonsole/qonsole1/qonsole.cpp

[ . . . . ]

void Qonsole::showOutput() {            1
    QByteArray bytes = m_Shell->readAllStandardOutput();
    QStringList lines = QString(bytes).split("\n");
    foreach (QString line, lines) {
        m_Logw->append(line);
    }
}

void Qonsole::execute() {
    QString cmdStr = m_InputArea->text() + "\n";
    m_InputArea->setText("");
    m_Logw->append(cmdStr);
    QByteArray bytes = cmdStr.toUtf8(); 2
    m_Shell->write(bytes);               3
}

1

A slot that gets called whenever input is ready

2

8-bit Unicode Transformation Format

3

Send the data into the stdin stream of the bash child process


Example 17.9 shows the client code that launches this application.

Example 17.9. src/qonsole/qonsole1/qonsole.cpp

[ . . . . ]

#include <QApplication>

int main(int argc, char* argv[]) {
   QApplication app(argc, argv);
   Qonsole qon;
   qon.show();
   return app.exec();
}



[99] Or cmd on Windows

[100] There is a small disagreement between the authors about the pronunciation of Qonsole. The two choices seem to be "Chonsole" (using the standard Chinese transliteration convention, as in Qing) and "Khonsole" (using the standard Arabic transliteration, as in Qatar).