Connecting C++ slots to QML signals
The separation of the user interface and backend allows us to connect C++ slots to the QML signals. Although it's possible to write processing functions in QML and manipulate interface items in C++, it violates the principle of the separation. Therefore, you may want to know how to connect a C++ slot to a QML signal at first. As for connecting a QML slot to a C++ signal, I'll introduce that later in this book.
In order to demonstrate this, we need to create a C++ class in the first place by right-clicking on the project in the Projects panel and selecting Add New…. Then, click on C++ Class in the pop-up window. The newly created class should at least inherit from QObject
by choosing QObject
as its base class. This is because a plain C++ class can't include Qt's slots or signals. The header file's content is displayed as follows:
#ifndef PROCESSOR_H #define PROCESSOR_H #include <QObject> class Processor : public QObject { Q_OBJECT public: explicit Processor(QObject *parent = 0); public slots: void onMenuClicked(const QString &); }; #endif // PROCESSOR_H
Here's the content of the source file:
#include <QDebug> #include "processor.h" Processor::Processor(QObject *parent) : QObject(parent) { } void Processor::onMenuClicked(const QString &str) { qDebug() << str; }
The C++ file is the same as the one we dealt with in the previous topics. The onMenuClicked
slot I defined is simply to output the string that passes through the signal. Note that you have to include QDebug
if you want to use the built-in functions of qDebug
, qWarning
, qCritical
, and so on.
The slot is prepared, so we need to add a signal to the QML file. The QML file is changed to the following code:
import QtQuick 2.3 import QtQuick.Controls 1.2 ApplicationWindow { id: window visible: true width: 640 height: 480 title: qsTr("Hello QML") signal menuClicked(string str) menuBar: MenuBar { Menu { title: qsTr("File") MenuItem { text: qsTr("Exit") shortcut: "Ctrl+Q" onTriggered: Qt.quit() } MenuItem { text: qsTr("Click Me") onTriggered: window.menuClicked(text) } } } Text { id: hw text: qsTr("Hello World") font.capitalization: Font.AllUppercase anchors.centerIn: parent } Label { anchors { bottom: hw.top; bottomMargin: 5; horizontalCenter: hw.horizontalCenter } text: qsTr("Hello Qt Quick") } }
As you can see, I specified the ID of the root ApplicationWindow
item to window and declared a signal named menuClicked
. In addition to this, there is another MenuItem
in the menu file. It emits the menuClicked
signal of window, using its text as the parameter.
Now, let's connect the slot in the C++ file to this newly created QML signal. Edit the main.cpp
file.
#include <QApplication> #include <QQmlApplicationEngine> #include "processor.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); QObject *firstRootItem = engine.rootObjects().first(); Processor myProcessor; QObject::connect(firstRootItem, SIGNAL(menuClicked(QString)), &myProcessor, SLOT(onMenuClicked(QString))); return app.exec(); }
The item in the QML file is accessed as QObject
in C++ and it could be cast to QQuickItem
. For now, we only need to connect its signal, so QObject
will do.
You may notice that I used the old-style syntax of the connect
statement. This is because QML is dynamic and the C++ compiler can't detect the existence of the signal in the QML file. Since things in QML are checked at runtime, it doesn't make sense to use the old syntax here.
When you run this application and navigate to File | Click Me in the menu bar, you'll see Application Output in Qt Creator:
"Click Me"
Let's review this process again. Triggering the Click Me
menu item resulted in the emission of the window's signal menuClicked
. This signal passed the text of MenuItem
, which is Click Me
, to the slot in C++ class Processor
, and the processor myProcessor
slot onMenuClicked
printed the string to the Application Output panel.