17.1.3.  Qonsole with Keyboard Events

[ fromfile: qprocess.xml id: qonsole2 ]

In the preceding example, Qonsole had a separate widget for user input. For a more authentic console experience, the user should be able to type commands directly in the command output window. To accomplish this Qonsole needs to capture keyboard events. The first step is to override the the QObject base class eventFilter() method, as shown in Example 17.10, the revised class definition.

Example 17.10. src/qonsole/keyevents/qonsole.h

[ . . . . ]
class Qonsole : public QMainWindow {
    Q_OBJECT
 public:
    Qonsole();
 public slots:
    void execute();
    void showOutput();
    bool eventFilter(QObject *o, QEvent *e)  ;
 protected:
    void updateCursor();
 private:
    QString m_UserInput;
    QTextEdit* m_Logw;
    QProcess* m_Shell;
};
[ . . . . ]

As discussed in Section 8.3, an event is an object derived from QEvent. Within the context of an application, such a QEvent is associated with a QObject that is its intended receiver. The receiving object has a handler to process the event. An eventFilter examines a QEvent and determines whether to permit it to be processed by the intended receiver. We have provided our revised Qonsole application with an eventFilter() function that can be used to filter keyboard events from m_Logw, an extended QTextEdit. Qonsole, an extended QMainWindow, is the intended recipient of those events. The implementation of this function is shown in Example 17.11

Example 17.11. src/qonsole/keyevents/qonsole.cpp

[ . . . . ]

bool Qonsole::eventFilter(QObject* o, QEvent* e) {
    if (e->type() == QEvent::KeyPress) {
        QKeyEvent* k = static_cast<QKeyEvent*> (e); 
        int key = k->key();
        QString str = k->text();
        m_UserInput.append(str);
        updateCursor();
        if ((key == Qt::Key_Return) || (key == Qt::Key_Enter) ) {
#ifdef Q_WS_WIN                                     1
            m_UserInput.append(QChar(0x000A));
#endif
            execute();
            return true;                            2           
        }
        else {
            m_Logw->insertPlainText(str);
            return true;
        }
    }
    return false;                                   3
}

1

Windows processes need a Carriage Return + Line Feed, not just a CR.

2

We processed the event. This prevents other widgets from seeing it.

3

Don't touch the event.


When the Enter key is pressed, the member function execute() is called so that the command string can be sent to the shell and then reset. Example 17.12 shows the implementation of these two functions.

Example 17.12. src/qonsole/keyevents/qonsole.cpp

[ . . . . ]

void Qonsole::updateCursor() {
    QTextCursor cur = m_Logw->textCursor();
    cur.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
    m_Logw->setTextCursor(cur);
}

void Qonsole::execute() {
    QByteArray bytes = m_UserInput.toUtf8();
    m_Shell->write(bytes);
    m_UserInput = "";    
}

All that remains to be done is to call the base class function installEventFilter() on m_Logw, the widget whose events you want to capture. This is done in the constructor, as shown in Example 17.13.

Example 17.13. src/qonsole/keyevents/qonsole.cpp

[ . . . . ]

Qonsole::Qonsole() {
   m_Logw = new QTextEdit;
   setCentralWidget(m_Logw); 
   m_Logw->installEventFilter(this);                1
   m_Logw->setLineWrapMode(QTextEdit::WidgetWidth);
   m_Shell = new QProcess();
   m_Shell->setReadChannelMode(QProcess::MergedChannels);
   connect (m_Shell, SIGNAL(readyReadStandardOutput()),
             this, SLOT(showOutput()));
#ifdef Q_WS_WIN
   m_Shell->start("cmd", QStringList(), QIODevice::ReadWrite);
#else
   m_Shell->start("bash", QStringList("-i"), QIODevice::ReadWrite);
#endif   
}

1

Intercept events going to the QTextEdit.