1
votes

I'm using QT 5.7. My main.qml file's root element is 'ApplicationWindow'(which btw always underlines visible, width and height as invalid properties even though they work. I'd like to know a fix for it or the proper way to do this since I can't edit it in designer). This is its code:

ApplicationWindow {
visible: true
width: 640
height: 480
    Rectangle{
    signal mSend()
    anchors.fill: parent
        Button{
            id: bSend
                onClicked: {
                    parent.mSend()
                }
        }
    }
}

Now I'm trying to bind the mSend signal to a CPP slot this way:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
#include <QObject>
#include <QQuickView>
#include <QQuickItem>

class Chat: public QObject{
    Q_OBJECT

    public Q_SLOTS:
    void sendMessage(){
        qDebug() << "CPP SLOT sendMessage called";
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));

    QQuickView view(&engine, Q_NULLPTR);
    QObject *item = view.rootObject();
    Chat chat;
    if(item){
        qDebug() << "Item found";
        QObject::connect(item, SIGNAL(mSend()), &chat, SLOT(sendMessage()));
    }else{
        qDebug() << "item is null";
    }

    return app.exec();
}
#include "main.moc"

But the result is Item is null. I've been following this guide but I'm unable to tinker with QMLApplicationEngine and ApplicationWindow in QML.

TIA

NOTE: I'm using Q_SLOTS because I'm also using Boost to incorporate Socket IO library.

2

2 Answers

3
votes

You should move the signal from the Rectangle to the root object:

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

ApplicationWindow {
  id: appWindow
  visible: true
  width: 640
  height: 480
  signal mSend()
  Rectangle{
    anchors.fill: parent
    Button{
      id: bSend
      onClicked: {
        appWindow.mSend()
      }
    }
  }
}

The root object is directly available through the QQmlApplicationEngine

QGuiApplication app(argc, argv);

QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));

QObject *item = engine.rootObjects().first();
Chat chat;
if(item){
    qDebug() << "Item found";
    QObject::connect(item, SIGNAL(mSend()), &chat, SLOT(sendMessage()));
}else{
    qDebug() << "item is null";
}

return app.exec();
8
votes

Instead of digging out QML objects and messing with them from C++, I would highly recommend exposing your Chat object to QML. This is the way the QML and C++ integration was designed to work. You have several options to choose from. Either way, you can call Chat::sendMessage() directly from the Button::onClicked signal handler.

QML Type

You can register Chat as a QML type using qmlRegisterType(), so you can create the instance in QML.

  • main.cpp:

    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<Chat>("foo.bar", 1, 0, "Chat"); // <==
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
        return app.exec();
    }
    
  • main.qml:

    import QtQuick 2.0
    import QtQuick.Controls 2.0
    import foo.bar 1.0
    
    ApplicationWindow {
        Chat {
          id: chat
        } 
    
        Button {
            onClicked: chat.sendMessage(...)
        }
    }
    

QML Singleton Type

You can register Chat as a QML singleton type using qmlRegisterSingletonType(), so you can conveniently access the same instance anywhere in QML.

  • main.cpp

    static QObject *chatInstance(QQmlEngine *engine, QJSEngine *)
    {
        return new Chat(engine);
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        qmlRegisterSingletonType<Chat>("foo.bar", 1, 0, "Chat", chatInstance); // <==
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
        return app.exec();
    }
    
  • main.qml

    import QtQuick 2.0
    import QtQuick.Controls 2.0
    import foo.bar 1.0
    
    ApplicationWindow {
        Button {
            onClicked: Chat.sendMessage(...)
        }
    }
    

Context Property

If you must create the Chat instance in C++, you may set it as a context property using QQmlContext::setContextProperty() for the QML engine's root context. Notice that you must set the property before loading QML that references the context property.

  • main.cpp

    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        Chat chat;
        engine.rootContext()->setContextProperty("chat", &chat); // <==
    
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    
        return app.exec();
    }
    
  • main.qml

    import QtQuick 2.0
    import QtQuick.Controls 2.0
    
    ApplicationWindow {
        Button {
            onClicked: chat.sendMessage(...)
        }
    }