Strange behaviour when open QML Popup Window from C++ code.



  • Hi everyone !
    I'm implement M-V-VM Pattern and have this Strange Behaviour

    First, I open a main window like normal. then set the Context Object like this:

    //Set Data Context to the Root Context
    QObject* mainViewModel = new MainWindowViewModel(window);
    QQmlContext* ctx = engine.rootContext();
    ctx->setContextObject(mainViewModel);
    

    Then I'm try to open Popup in MainWindowViewModel like this:

    QQmlApplicationEngine engine;
    
    QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/FrmAuthenticate.qml")));
    QObject *childItem = qobject_cast<QObject*>(component.create());
    QQuickWindow *dialog = qobject_cast<QQuickWindow *>(childItem);
    QQmlContext* ctx = engine.contextForObject(dialog);
    FrmAuthenticateViewModel *viewModel = new FrmAuthenticateViewModel(dialog);
    ctx->setContextObject(viewModel);
    childItem->setParent(this->parent());
    

    in Popup I have a WebView and it print out - console.log() - each time it navigate to new Url.

    First strange thing:

    • If open the popup by create a component from QML, I got the log data.
      But when open from ViewModel, No log data print to Application Output (Can't debug in QML too)

    Second strange thing:

    • In the code to open popup above, I cannot set the contextObject for the Popup.
      It show a warning "Cannot set context object for internal context" at runtime and QML cannot call to ViewModel's functions

    Third strange thing:

    • If open the popup by code in ViewModel without set the contextObject. Webview load & display the default page.
      When trying to set contextObject. Webview cannot load & display the default page.

    Anyone have any ideal why this happen ?!

    Thanks & Best Regards
    DongLD



  • Please... anyone can help me with this ?!

    Just open a popup from C++ & setContextObject for it.



  • I'm try to build simple sample. Don't know why, but it work (mostly)

    • I can set context object to component
    • I can get a property from context by calling View Model's binding function
    • But I can't "connect" the signal from Qml to C++ functions like main.qml call to showLoginDialog() function
      • It look like all signals in Qml wasn't fired. (No focus highlight, button onClicked not fired...

    Here is the code:

    main.qml

    import QtQuick 2.3
    import QtQuick.Controls 1.2
    
    ApplicationWindow {
        id : mainWindow
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        menuBar: MenuBar {
            Menu {
                title: qsTr("File")
                MenuItem {
                    text: qsTr("&Open")
                    onTriggered:
                    {
                        //Show popup from ViewModel (C++)
                        showLoginDialog();
    
                        //Show popup from QML
    //                    var component = Qt.createComponent("qrc:/login.qml");
    //                    if (component.status == Component.Ready)
    //                    {
    //                        component.createObject(mainWindow);
    //                    }
                    }
                }
                MenuItem {
                    text: qsTr("Exit")
                    onTriggered: Qt.quit();
                }
            }
        }
    }
    
    

    main.cpp

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        QObject *topLevel = engine.rootObjects().at(0);
        QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
    
        //Set Data Context to the Root Context
        QObject* mainViewModel = new MainWindowViewModel(window);
        QQmlContext* ctx = engine.rootContext();
        ctx->setContextObject(mainViewModel);
    }
    

    mainwindowviewmodel.cpp

    #include "mainwindowviewmodel.h"
    
    MainWindowViewModel::MainWindowViewModel(QObject *parent) : QObject(parent)
    {
    
    }
    
    void MainWindowViewModel::showLoginDialog()
    {
        qDebug() << "Show Login Form";
    
        LoginViewModel *viewModel = new LoginViewModel();
        viewModel->setProperty("sampleText", "https://google.com");
        QQmlApplicationEngine engine;
    
        QQmlContext* ctx = new QQmlContext(engine.rootContext());
        ctx->setContextObject(viewModel);
    
        QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/login.qml")));
        QObject *childItem = qobject_cast<QObject*>(component.create(ctx));
        //QQuickWindow *dialog = qobject_cast<QQuickWindow *>(childItem);
    
        //FrmAuthenticateViewModel *viewModel = new FrmAuthenticateViewModel(dialog);
        childItem->setParent(this->parent());
    }
    

    login.qml

    import QtQuick 2.4
    import QtQuick.Controls 1.3
    import QtQuick.Layouts 1.1
    import QtWebKit 3.0
    //import QtWebView 1.0
    import QtTest 1.0
    
    ApplicationWindow {
        id: loginDialog
        title: qsTr("Hello World")
        width: 480
        height: 550
        visible: true
    
            TextField {
                id: txtUrl
                height: 30
                text: binding("sampleText");
                anchors.right: parent.right
                anchors.rightMargin: 100
                anchors.left: parent.left
                anchors.leftMargin: 0
                anchors.top: parent.top
                anchors.topMargin: 0
            }
    
            Button {
                id : btn1
                width: 100
                height: 30
                text: "Go"
                anchors.top: parent.top
                anchors.topMargin: 0
                anchors.right: parent.right
                anchors.rightMargin: 0
                onClicked:
                {
                    text : binding("sampleText");
                }
            }
    }
    

    loginviewmodel.cpp

    #include "loginviewmodel.h"
    
    LoginViewModel::LoginViewModel(QObject *parent) : QObject(parent)
    {
    
    }
    
    QVariant LoginViewModel::binding(const QString &propPath) const
    {
        return this->property(qPrintable(propPath));
    }
    
    QVariant LoginViewModel::getAuthenCode(const QString &message) const
    {
        qDebug() << message;
    }
    

    PS: "Tiếng tôi vang rừng núi... sao không ai trả lời"



  • I'm try to do everything in main.cpp like this... and it work fine.

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        QObject *topLevel = engine.rootObjects().at(0);
        QQuickWindow *window = qobject_cast<QQuickWindow*>(topLevel);
    
        LoginViewModel *viewModel = new LoginViewModel();
        viewModel->setProperty("url", "<AuthenticationUrl>");
    
        QQmlContext *ctx = new QQmlContext(engine.rootContext());
        ctx->setContextObject(viewModel);
    
        QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/login.qml")));
        QObject *childItem = component.create(ctx);
        childItem->setParent(window);
    
        MainWindowViewModel *mainViewModel = new MainWindowViewModel(window);
        QQmlContext *mainContext = engine.rootContext();
        mainContext->setContextObject(mainViewModel);
    
        return app.exec();
    }
    

    After investigation, I found out the root cause is :
    QQmlApplicationEngine engine is not singleton. it completely different instance.

    So, I try to keep track the engine created from the main.cpp
    but QQmlApplicationEngine is Q_DISABLE_COPY
    Then, I create a class (ApplicationContext) with static RootContext object to store the mainContext.

    Then any time I need the "Root Engine", I get it from RootContext like this:

    QQmlContext* rootContext = ApplicationContext::RootContext;
    ... 
    QQmlComponent component(rootContext->engine(), QUrl(QStringLiteral("qrc:/login.qml")));
    

Log in to reply
 

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