Access to an After Loaded View From Qml To C++



  • Hello ! I'm new in Qt Devlopment and I have a short question that certainly won't take long time to resolve for such specialists ;)

    I made a Loader to display a splashscreen that will load the main.qml thereafter like this:

    @
    QDeclarativeView view;
    view.setSource(QUrl("MainAppLoader.qml"));
    view.setResizeMode(QDeclarativeView::SizeRootObjectToView);
    view.showFullScreen();@

    I would like to connect some stuff to a child of my PageStackWindows in main.qml : mainMenu.qml

    The problem if I do that :

    @ QObject rootObject = dynamic_cast<QObject>(view.rootObject());
    QObject mainMenu = rootObject->findChild<QObject>("mainMenu");
    QObject::connect(&myClass, SIGNAL(data(QVariant)), mainMenu, SLOT(updateData(QVariant)));@

    I get this error: QObject::connect: Cannot connect <MyClass>::data(QVariant) to (null)::updateData(QVariant)

    Yes mainMenu is null. Of course I put: objectName: "mainMenu"

    In my MainAppLoader.qml I have this that load the main.qml :

    @Loader {
    id: mainLoader
    width: parent.width
    height: parent.height
    }

        Timer {
            id: firstPhaseTimer
            interval: 700
            running: true
            repeat: false
    
            onTriggered: {
                if (!mainLoader.Loading) {
                    // Start to load the main application
                    mainLoader.source = Qt.resolvedUrl("main.qml");
                    secondPhaseTimer.start();
                }
            }
        }@
    

    Do you know how I could get / use the reference of my main.qml / MainMenu.qml freshly created to connect what I want from cpp ?

    If I do the same without my splashScreenLoader, it works.

    Thanks !


  • Moderators

    You are probably asking for connection too early. Either emit a signal to C++ once loading is ready, or make the connection in QML.



  • You say it's possible to connect a freshly created QML View with CPP in QML ?

    In other words, I just need to warn Cpp with a signal when my main.qml is created mainLoader.source = Qt.resolvedUrl("main.qml");
    to tell him to make the rootObject->findChild<QObject*>("mainMenu"); and connect it ? that's all ?

    I'l try tomorow, thank you for the quick anwser !


  • Moderators

    I think so. To be honest I did not take time to read through and fully understand your post, I just gave a first impression.

    The definite point is that object needs to be ready before meta objects can be connected.

    BTW. you don't need the dynamic_cast line, root object should have ::findChild() available.



  • As I see it, you have the following options:

    1.) Call a C++ slot after the Loader finished, as sierdzio suggested
    You can call a C++ function from QML by registering the C++ object as a context property with @QDeclarativeContext::setContextProperty()@ and then call the slot function of this object. This is useful if the object you are calling only exists once, i.e. a Singleton.

    2.) By registering the C++ item as a QML item. You can do that with @qmlRegisterType<CppClassName>("NamespaceForImport", 1, 0 "QMLItemName")@

    In that way, you could also directly access the signal from QML, e.g. if the signal is called @dataChanged(QVariant newData)@

    , you can access it in QML with
    @
    import NamespaceForImport 1.0

    QMLItemName {
    onDataChanged: { ... "newData" is available as parameter ... }
    }
    @



  • Hello again, I think I'm doing it wrong

    I made a call to my C++ object when QML completed the launched of MainMenu.qml like this:

    in main.cpp:

    @QDeclarativeView view;
    view.rootContext()->setContextProperty("MyClass",myClass);@

    in MainMenu.qml

    @Component.onCompleted: {
    MyClass.mainMenuCreated();
    }@

    in MyClass c++

    @void MyClass::mainMenuCreated()
    {
    qDebug() << "Hello I'm created";

    }@

    This is working but now, How can I have access to QDeclarativeView / QDeclarativeContext from MyClass to connect my freshly created qml file with c++ signal ? I only have access in main.cpp and tried to pass it in MyClass constructor, but it seems private. I think I'm doing something wrong :D



  • You should be able to pass it with the constructor - what do you mean that it is private? You have control over MyClass implementation, so you can just do something like the following:
    @
    //in main.cpp:
    QMLApplicationViewer viewer;

    MyClass myClass = new MyClass(viewer);

    //and in MyClass constructor you can access the QDeclarativeEngine from the QMLApplicationViewer with:

    MyClass::MyClass(QMLApplicationViewer &viewer) {
    QDeclarativeEngine *engine = viewer.engine();
    QDeclarativeContext *context = engine->rootContext;
    }
    @

    From the QDeclarativeView you can call the method rootContext() to get the context in your MyClass.

    Alternatively, to receive the context from a QDeclarativeItem which you instantiate from QML, you can use the following:
    @QDeclarativeContext *currentContext = QDeclarativeEngine::contextForObject(this);
    QDeclarativeEngine *declarativeEngine = currentContext->engine();
    QDeclarativeContext *rootContext = declarativeEngine->rootContext();@



  • I surely made a mistake somewhere :(

    In main.cpp

    @QDeclarativeView view;
    MyClass *myClass = new MyClass(view);
    @

    MyClass.h

    @class MyClass : public QObject
    {
    Q_OBJECT
    public:
    explicit MyClass();
    explicit MyClass(QDeclarativeView &view);

    signals:

    public slots:

    private:
    QDeclarativeView mQDeclarativeView;
    

    };@

    MyClass.cpp

    @MyClass::MyClass() {
    }

    MyClass::MyClass(QDeclarativeView &view) {
    mQDeclarativeView = view;
    }@

    Got this error:

    @ error : 'QDeclarativeView& QDeclarativeView::operator=(const QDeclarativeView&)' is private
    @


  • Moderators

    Use a pointer. You are trying to use an assignment operator while all QObject children cannot be copied.



  • It was what I did first but you gave me an example with references :P

    It's working now :)

    The final code for visitors:

    @void MyClass::mainMenuCreated()
    {
    qDebug() << "Hello I'm created";

    QObject *menu = (mQDeclarativeView->rootObject())->findChild<QObject*>("mainMenu");
    QObject::connect(this, SIGNAL(data(QVariant)), menu, SLOT(updateData(QVariant)));
    

    }@

    And now my function from MainMenu.qml is called :)

    @updateData(text) { console.log("I'm here"); }
    @

    If you see any improvment that could be made or other better technique, don't hesitate :)

    Thank you so much for this lesson :P



  • No worries :)

    As a tiny improvement, it's good practice to name context properties with a small first letter to be able to distinguish them from classes/QML components. So instead of "MyClass", name it "myClass" - you could access enums for instance from QML with "MyClass.enumName" then, and so don't mix it up with a context property access.

    Also, the data-signal could be renamed to dataChanged, as it is probably reflects more what it does. But that's syntactic sugar, just a suggestion..


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.