Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

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

    General and Desktop
    1
    4
    2930
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Dong
      Dong last edited by

      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

      1 Reply Last reply Reply Quote 0
      • Dong
        Dong last edited by

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

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

        1 Reply Last reply Reply Quote 0
        • Dong
          Dong last edited by Dong

          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"

          1 Reply Last reply Reply Quote 0
          • Dong
            Dong last edited by Dong

            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")));
            
            1 Reply Last reply Reply Quote 0
            • First post
              Last post