Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Wait for QML-Dialog in C++ Backend
Forum Updated to NodeBB v4.3 + New Features

Wait for QML-Dialog in C++ Backend

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
5 Posts 3 Posters 2.0k Views
  • 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.
  • K Offline
    K Offline
    kain
    wrote on 12 Mar 2019, 17:58 last edited by
    #1

    Re: Wait for dialog response

    Hey there,

    I am referring to that upper thread which did not have an usable answer for me. I need some kind of dialog-mechanism in my (backend-) code which is able to invoke some Dialog in QML and (the important part) receives the result from it.

    So far I have a small test-application to invoke some dialog:

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQuickView m_view;
    
        m_view.setSource(QUrl("qrc:///main.qml"));
        m_view.show();
    
        auto object = qobject_cast<QObject*> (m_view.rootObject());
    
        QVariant returnedValue;
        QVariant msg = "Hello from C++";
        QMetaObject::invokeMethod(object, "showMessageBox", Qt::ConnectionType::DirectConnection,  Q_RETURN_ARG(QVariant, returnedValue));
    // I want to wait for the result
        qDebug() << returnedValue.toString();
    
    
    
        return app.exec();
    }
    

    QML:

    Item {
        id: window
        visible: true
        width: 640
        height: 480
    
        function showMessageBox() {
            return dialog.open()
        }
    
    
        Dialog {
            id: dialog
            title: "Title"
            modal: true
            width: 200
            standardButtons: Dialog.Ok | Dialog.Cancel
        }
    }
    

    Is it somehow possible to wait for that QML-invocation? I know I could rewrite my code and split it into several signal-handlers but that would be a very unpractical way for me..

    Thanks!

    K 1 Reply Last reply 13 Mar 2019, 07:17
    0
    • D Offline
      D Offline
      dheerendra
      Qt Champions 2022
      wrote on 13 Mar 2019, 01:54 last edited by
      #2

      I suggest to use signal/slots mechanism to make it clean interface. Also is your code not working ?

      Dheerendra
      @Community Service
      Certified Qt Specialist
      http://www.pthinks.com

      1 Reply Last reply
      0
      • K kain
        12 Mar 2019, 17:58

        Re: Wait for dialog response

        Hey there,

        I am referring to that upper thread which did not have an usable answer for me. I need some kind of dialog-mechanism in my (backend-) code which is able to invoke some Dialog in QML and (the important part) receives the result from it.

        So far I have a small test-application to invoke some dialog:

        int main(int argc, char *argv[])
        {
            QGuiApplication app(argc, argv);
            QQuickView m_view;
        
            m_view.setSource(QUrl("qrc:///main.qml"));
            m_view.show();
        
            auto object = qobject_cast<QObject*> (m_view.rootObject());
        
            QVariant returnedValue;
            QVariant msg = "Hello from C++";
            QMetaObject::invokeMethod(object, "showMessageBox", Qt::ConnectionType::DirectConnection,  Q_RETURN_ARG(QVariant, returnedValue));
        // I want to wait for the result
            qDebug() << returnedValue.toString();
        
        
        
            return app.exec();
        }
        

        QML:

        Item {
            id: window
            visible: true
            width: 640
            height: 480
        
            function showMessageBox() {
                return dialog.open()
            }
        
        
            Dialog {
                id: dialog
                title: "Title"
                modal: true
                width: 200
                standardButtons: Dialog.Ok | Dialog.Cancel
            }
        }
        

        Is it somehow possible to wait for that QML-invocation? I know I could rewrite my code and split it into several signal-handlers but that would be a very unpractical way for me..

        Thanks!

        K Offline
        K Offline
        KroMignon
        wrote on 13 Mar 2019, 07:17 last edited by
        #3

        @kain In my point of view, the easiest solution is to use a QObject and work with signals/slots.
        I would first create a simple QObject class, like this:

        class DialogInterface : public QOject
        {
            Q_OBJECT
        
        public:
            explicit DialogInterface (Object *parent  = 0): QObject(parent) {}
        
            // You can add as parameter what ever you want to become from QML
            Q_INVOKABLE void dialogClosed(int select) {}
        
        signals:
            void requestDialog();
        }
        

        modifiy the main.cpp to add DialogInterface instance and shared it with QML:

        int main(int argc, char *argv[])
        {
            QGuiApplication app(argc, argv);
            QQuickView m_view;
        
            m_view.setSource(QUrl("qrc:///main.qml"));
            m_view.show();
        
            // create intance and share it with QML context
            DialogInterface  dialogInterface;
            m_view.rootContext()->setContextProperty("dialogInterface", &dialogInterface);
        
            // request dialog to be show
           QTimer::singleShort(0, &dialogInterface, &DialogInterface::requestDialog);
        
            return app.exec();
        }
        

        And finaly the QML part:

        Item {
            id: window
            visible: true
            width: 640
            height: 480
        
           Connections {
               target: dialogInterface
               onRequestDialog: {
                    dialog.open()
                }
            }
        
            Dialog {
                id: dialog
                title: "Title"
                modal: true
                width: 200
                standardButtons: Dialog.Ok | Dialog.Cancel
                onOk: {
                     console.log("OK pressed")
                     dialogInterface.dialogClosed(0);
                }
                onDiscard: {
                     console.log("Cancel pressed")
                     dialogInterface.dialogClosed(-1);
                }
            }
        }
        

        It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

        1 Reply Last reply
        0
        • K Offline
          K Offline
          kain
          wrote on 13 Mar 2019, 09:28 last edited by
          #4

          Thank you, but that does not solve my problem.
          I want a blocking mechanism when I invoke the dialog.
          In my code I have several places where I need to raise some dialog (notificaion, warning, yes/no, etc). It is just not very feasible to split this code up.

          Imagine I got the following to (pseudo)code-snippets which run in parallel on different QThreads:

          SnippetA:

          doSomethingA1();
          auto result = dialogService.showMessageBox("Do you want to save your progress?");
          if (result == Dialog::accepted)
            save();
          doSomethingA2();
          

          SnippetB:

          doSomethingB1();
          auto result = dialogService.showMessageBox("Do you want to to delete everything, kill every process and shutdown?");
          if (result == Dialog::accepted)
            destroyeverything();
          else
            doSomethingB2();
          

          This works fine if I have that blocking mechanism like above. But now If I change this to be asynchronous:

          SnippetA:

          
          classA::someMethod() {
            doSomethingA1();
            emit dialogService.showMessageBox("Do you want to save your progress?");
          }
          
          classA::slotHandlerOnAccept() {
            save();
            doSomethingA2();
          }
          
          classA::slotHandlerOnReject() {
              doSomethingA2();
          }
          
          

          SnippetB:

          
          classB::someMethod() {
            doSomethingB1();
            emit dialogService.showMessageBox("Do you want to to delete everything, kill every process and shutdown?");
          }
          
          classB::slotHandlerOnAccept() {
            destroyeverything();
          }
          
          classB::slotHandlerOnReject() {
              doSomethingB2();
          }
          
          

          I hope it is clear to see that this code will cause some trouble if both classes want to show a dialog at the same time. How would I deal with that?

          Or would it be more suitiable to create a dialogService which sits in its own thread and which blocks via an local eventloop until the result of the dialog is emitted?

          K 1 Reply Last reply 13 Mar 2019, 12:50
          0
          • K kain
            13 Mar 2019, 09:28

            Thank you, but that does not solve my problem.
            I want a blocking mechanism when I invoke the dialog.
            In my code I have several places where I need to raise some dialog (notificaion, warning, yes/no, etc). It is just not very feasible to split this code up.

            Imagine I got the following to (pseudo)code-snippets which run in parallel on different QThreads:

            SnippetA:

            doSomethingA1();
            auto result = dialogService.showMessageBox("Do you want to save your progress?");
            if (result == Dialog::accepted)
              save();
            doSomethingA2();
            

            SnippetB:

            doSomethingB1();
            auto result = dialogService.showMessageBox("Do you want to to delete everything, kill every process and shutdown?");
            if (result == Dialog::accepted)
              destroyeverything();
            else
              doSomethingB2();
            

            This works fine if I have that blocking mechanism like above. But now If I change this to be asynchronous:

            SnippetA:

            
            classA::someMethod() {
              doSomethingA1();
              emit dialogService.showMessageBox("Do you want to save your progress?");
            }
            
            classA::slotHandlerOnAccept() {
              save();
              doSomethingA2();
            }
            
            classA::slotHandlerOnReject() {
                doSomethingA2();
            }
            
            

            SnippetB:

            
            classB::someMethod() {
              doSomethingB1();
              emit dialogService.showMessageBox("Do you want to to delete everything, kill every process and shutdown?");
            }
            
            classB::slotHandlerOnAccept() {
              destroyeverything();
            }
            
            classB::slotHandlerOnReject() {
                doSomethingB2();
            }
            
            

            I hope it is clear to see that this code will cause some trouble if both classes want to show a dialog at the same time. How would I deal with that?

            Or would it be more suitiable to create a dialogService which sits in its own thread and which blocks via an local eventloop until the result of the dialog is emitted?

            K Offline
            K Offline
            KroMignon
            wrote on 13 Mar 2019, 12:50 last edited by KroMignon
            #5

            @kain Yes, I think I have understand what you want to achieve. It ist not so easy.

            I seen one possibility which is not too difficult.
            You create a kind of template class to register the dialog message and store the result.
            Something like this:

            class DialogRequest : public QOject
            {
                Q_OBJECT
            
                Q_PROPERTY(QString message READ message CONSTANT)
            
            public:
                explicit DialogRequest(const QString& message, Object *parent  = 0): QObject(parent), m_message(message) {}
                QString message() const { return m_message; }
            
            signals:
                 void done(QObject* dialog, int result);
            
            private:
                QString m_message;
            }
            class DialogInterface : public QOject
            {
                Q_OBJECT
            
            public:
                explicit DialogInterface (Object *parent  = 0): QObject(parent) {}
            
                DialogRequest * createMessage(const QString& message)
                {
                    // add parent to avoid QML taking ownership of instance!!!
                    return new DialogRequest(message, this);
                }
            
                void showDialog(DialogRequest * dlg) { emit requestDialog(dlg);   }
            
            signals:
                void requestDialog(QObject* dialogObj);
            }
            

            Create new QML Component called, for example MyDialog:

            Dialog {
                id: dialog
                property var dialogInfo
                title: !!dialogInfo ? dialogInfo.message : "Title"
                modal: true
                width: 200
                standardButtons: Dialog.Ok | Dialog.Cancel
                onOk: {
                     console.log("OK pressed")
                     dialogInfo .done(dialogInfo , 0);
                }
                onDiscard: {
                     console.log("Cancel pressed")
                     dialogInfo.done(dialogInfo , -1);
                }
            }
            

            change QML as follow:

            Item {
                id: window
                visible: true
                width: 640
                height: 480
            
               Connections {
                   target: dialogInterface
                   onRequestDialog: {
                        var newDialog = Qt.createQmlObject("MyDialog.qml", window, "dynamicMessage")
                        newDialog.dialogInfo = dialogObj
                        newDialog.open()
                    }
                }
            }
            

            Then in C++ something like this

              auto* newDialog = dialogInterface.createMessage("This is a title");
              
               connect(newDialog, &DialogRequest::done, this, [&](QObject* dialog, int result){
                    dialog->deleteLater();
                    if(result == 0)
                    {
                          // do Ok stuff
                    }
                    else
                    {
                         // do error stuff
                    }
            });
               dialogInterface.requestDialog(newDialog);
            

            This is just an example in which way it can be done, and must be fine tune/adapted.
            Just to give you a starting impulse

            Hope this will help you to solve your problem.

            It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

            1 Reply Last reply
            1

            1/5

            12 Mar 2019, 17:58

            • Login

            • Login or register to search.
            1 out of 5
            • First post
              1/5
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved