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. Signal from c++ to QML with setContextProperty
QtWS25 Last Chance

Signal from c++ to QML with setContextProperty

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
12 Posts 4 Posters 2.4k 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.
  • V Offline
    V Offline
    Vana
    wrote on last edited by Vana
    #1

    Hello,
    I do an HTTP request and get a response from it .
    I check the http code status and emit a signal to a qml component to call a slot.
    My goal is to do a simple login form, with api call. When user log in, the qml login form switch to qml index component.
    For that, onSignal, I would like to change visible value.

    request.h

    class request : public QObject, public std::enable_shared_from_this<request>
    {
        Q_OBJECT
    
    signals:
        Q_INVOKABLE void login();
    

    request.cpp

    void request::on_read(boost::system::error_code ec, std::size_t bytes_transferred)
    {
        boost::ignore_unused(bytes_transferred);
    
        if(ec)
            return fail(ec, "read");
    
        //TODO : Improve to get the user infos
        pt::ptree root;
        std::stringstream stringstream;
        stringstream << res.body();
        pt::read_json(stringstream, root);
    
        switch(res.result_int())
        {
        case 200:
            emit login(); //here the signal
            std::cout<<res.result_int()<<std::endl; //I see '200'
            break;
    

    main.cpp

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        boost::asio::io_context io_context;
        boost::asio::ssl::context ctx{ssl::context::sslv23_client};
        load_root_certificates(ctx);
        request request(io_context, ctx);
    
        engine.rootContext()->setContextProperty("http", &request);
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        return app.exec();
    }
    

    main.qml

    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Chat ")
        
        Connections {
            target: http
            onLogin: {
               console.log("login ok ! ") //Here I can't see signal
            }
        }
    
        Index {  
            id: chat
            visible: false
        }
    
    S 1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi and welcome to devnet,

      From the looks of it, I would guess that your network request has finished before your GUI is shown.

      Since you're not using QNetworkAccessManager, I would suggest to implement a slot that starts the request on demand.

      By the way, there's no need to add Q_INVOKABLE to your signals. You don't call them from external objects.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      2
      • V Offline
        V Offline
        Vana
        wrote on last edited by
        #3

        I do not use QNetworkAccessManager, but only Qt as a Gui app. The backend use another framework.

        I already start a slot into Qml like this :
        Signin.qml

            Button {
                id: send
                x: 280
                y: 257
                width: 61
                height: 25
                text: qsTr("Connect")
                onClicked:{
                    http.start_request("myapi.com", "443", "/login", 11, "POST", "username=" + login.text + "password=" + password.text);
                }
            }
        

        I import this component (Signin.qml )into main.qml like this :

        
            SwipeView {
                id: swipeView
                anchors.fill: parent
                currentIndex: tabBar.currentIndex
                visible: true
                Signin {
                    id: signin
                }
        }
        

        I already try to move connection into Signin.Qml but I have the same issue.

        Start_request slot do a shared_ptr :

        std::make_shared<request>(ioc, ctx)->run( );
        

        run function call function on_resolve, which call on_connect, which call on_handshake, which call on_write which call on_read, where is emitted the signal .

        1 Reply Last reply
        0
        • V Vana

          Hello,
          I do an HTTP request and get a response from it .
          I check the http code status and emit a signal to a qml component to call a slot.
          My goal is to do a simple login form, with api call. When user log in, the qml login form switch to qml index component.
          For that, onSignal, I would like to change visible value.

          request.h

          class request : public QObject, public std::enable_shared_from_this<request>
          {
              Q_OBJECT
          
          signals:
              Q_INVOKABLE void login();
          

          request.cpp

          void request::on_read(boost::system::error_code ec, std::size_t bytes_transferred)
          {
              boost::ignore_unused(bytes_transferred);
          
              if(ec)
                  return fail(ec, "read");
          
              //TODO : Improve to get the user infos
              pt::ptree root;
              std::stringstream stringstream;
              stringstream << res.body();
              pt::read_json(stringstream, root);
          
              switch(res.result_int())
              {
              case 200:
                  emit login(); //here the signal
                  std::cout<<res.result_int()<<std::endl; //I see '200'
                  break;
          

          main.cpp

          int main(int argc, char *argv[])
          {
              QGuiApplication app(argc, argv);
              QQmlApplicationEngine engine;
          
              boost::asio::io_context io_context;
              boost::asio::ssl::context ctx{ssl::context::sslv23_client};
              load_root_certificates(ctx);
              request request(io_context, ctx);
          
              engine.rootContext()->setContextProperty("http", &request);
          
              engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
          
              return app.exec();
          }
          

          main.qml

          Window {
              visible: true
              width: 640
              height: 480
              title: qsTr("Chat ")
              
              Connections {
                  target: http
                  onLogin: {
                     console.log("login ok ! ") //Here I can't see signal
                  }
              }
          
              Index {  
                  id: chat
                  visible: false
              }
          
          S Offline
          S Offline
          sharath
          wrote on last edited by sharath
          #4

          @Vana said in Signal from c++ to QML with setContextProperty:

          case 200:
          emit login(); //here the signal
          std::cout<<res.result_int()<<std::endl; //I see '200'

          Where you are calling this on_read(boost::system::error_code ec, std::size_t bytes_transferred) function. i think the control is not coming into this method. you just confirm whether the control is coming into this method by putting some logs.

          1 Reply Last reply
          0
          • V Offline
            V Offline
            Vana
            wrote on last edited by
            #5

            I call on_read(boost::system::error_code ec, std::size_t bytes_transferred) only from c++ side.

            Here the request class.cpp

            #include "request.h"
            #include <iostream>
            #include <boost/beast/http/verb.hpp>
            #include <boost/utility/string_view.hpp>
            
            request::request(boost::asio::io_context& ioc, ssl::context& ctx) : resolver(ioc) , stream(ioc, ctx)
            {
            }
            
            void request::fail(boost::system::error_code ec, char const* what)
            {
                std::cerr << what << ": " << ec.message() << "\n";
            }
            
            void request::run(char const* host, char const* port, char const* target, unsigned int version, QString method, QString body)
            {
                // Set up an HTTP GET request message
                http::verb method_verb;
                boost::string_view method_strview ;
                method_strview = method.toStdString();
                method_verb = boost::beast::http::string_to_verb(method_strview);
                req.version(version);
                req.method(method_verb);
                req.target(target);
                req.set(http::field::host, host);
                req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
                req.set(http::field::content_type, "application/x-www-form-urlencoded");
                req.body() = body.toStdString() ;
                req.prepare_payload();
            
                auto self = shared_from_this();
                resolver.async_resolve(host, port, [self](boost::system::error_code ec, tcp::resolver::results_type result)
                                                   {
                                                        self->on_resolve(ec, result);
                                                   });
             }
            
            void request::on_resolve(boost::system::error_code ec, tcp::resolver::results_type results)
            {
                if(ec)
                {
                    //return fail(ec, "resolve");
                }
                // Make the connection on the IP address we get from a lookup
                std::shared_ptr self = shared_from_this();
                boost::asio::async_connect(stream.next_layer(), results.begin(), results.end(), [self](boost::system::error_code ec, tcp::resolver::iterator)
                                                                                                {
                                                                                                    self->on_connect(ec);
                                                                                                });
            }
            
            void request::on_connect(boost::system::error_code ec)
            {
                if(ec){
                    //return fail(ec, "connect");
                    }
            
                // Perform the SSL handshake
                auto self = shared_from_this();
                stream.async_handshake(ssl::stream_base::client, [self](boost::system::error_code ec)
                                                                 {
                                                                    self->on_handshake(ec);
                                                                 });
            }
            
            void request::on_handshake(boost::system::error_code ec)
            {
                if(ec){
                    return fail(ec, "handshake");
                }
            
                // Send the HTTP request to the remote host
                auto self = shared_from_this();
                http::async_write(stream, req, [self](boost::system::error_code ec, std::size_t bytes_transferred)
                                               {
                                                    self->on_write(ec, bytes_transferred);
                                               });
            }
            
            void request::on_write(boost::system::error_code ec, std::size_t bytes_transferred)
            {
                boost::ignore_unused(bytes_transferred);
            
                if(ec)
                    return fail(ec, "write");
            
                // Receive the HTTP response
                auto self = shared_from_this();
                http::async_read(stream, buffer, res, [self](boost::system::error_code ec, std::size_t bytes_transferred)
                                                      {
                                                        self->on_read(ec, bytes_transferred); // Here the on_read() call
                                                      });
            }
            
            void request::on_read(boost::system::error_code ec, std::size_t bytes_transferred)
            {
                boost::ignore_unused(bytes_transferred);
            
                if(ec)
                    return fail(ec, "read");
            
                //TODO : Improve to get the user infos
                pt::ptree root;
                std::stringstream stringstream;
                stringstream << res.body();
                pt::read_json(stringstream, root);
            
                switch(res.result_int())
                {
                case 200:
                    emit login(); //Here the signal
                    std::cout<<res.result_int()<<std::endl; // I can see this
                    break;
                case 401:{
                    if (root.get<std::string>("message") ==  "wrong username"){
                        std::cout<<"Unknow User"<<std::endl;
                        emit wronglogin();
                        }
                    if (root.get<std::string>("message") ==  "wrong password"){
                        std::cout<<"Wrong password"<<std::endl;
                        emit wrongpassword();
                        }
                    }
                    break;
                }
            
                // Gracefully close the stream
                 auto self = shared_from_this();
                 stream.async_shutdown([self](boost::system::error_code ec)
                                       {
                                            self->on_shutdown(ec);
                                       });
            }
            
            void request::on_shutdown(boost::system::error_code ec)
            {
                if(ec == boost::asio::error::eof)
                {
                    ec.assign(0, ec.category());
                }
                if(ec)
                  return fail(ec, "shutdown");
            }
            
            void request::start_request(QString host, QString port, QString target, unsigned int version, QString method, QString body)
            {
                boost::asio::io_context ioc;
                ssl::context ctx{ssl::context::sslv23_client};
            
                const char* std_host = host.toStdString().c_str();
                const char* std_port = port.toStdString().c_str();
                const char* std_target = target.toStdString().c_str();
                const char* std_method = method.toStdString().c_str();
                const char* std_body = body.toStdString().c_str();
            
                std::make_shared<request>(ioc, ctx)->run(std_host, std_port, std_target, version, std_method, std_body );
            
                ioc.run();
            }
            

            So from QML side, I do this into main.QML:

             Connections {
                    target: http
                    onLogin: {
                       console.log("login ok")
                    }
                }
            

            into Signin.qml i start the http request like this:

             Button {
                    id: send
                    x: 280
                    y: 257
                    width: 61
                    height: 25
                    text: qsTr("Connect")
                    onClicked:{
                        http.start_request("myapi", "443", "/login", 11, "POST", "username=" + login.text + "&password=" + password.text);
                    }
                }
            

            So I can not call on_read() from QML, I only need connect login signal from on_read to QML

            1 Reply Last reply
            0
            • dheerendraD Offline
              dheerendraD Offline
              dheerendra
              Qt Champions 2022
              wrote on last edited by dheerendra
              #6

              The way you have placed the code it should work. You are telling that it is not working. Issue can happen only if

              1. Signal is not emitted
              2. Connection is made after sending the signal
              3. Sender object & receiver objects are different
              4. Objects are destroyed before anything meaning full take place.

              Just ensure that your login() signal is indeed working, try to place the following in your main.cpp & see the login() signal is really working.

              request myrequest(io_context, ctx);
              QObject::connect(&myrequest,&request::login,[](){
                  qDebug() << "Login Successfull" <<endl;
              });
              

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

              1 Reply Last reply
              0
              • V Offline
                V Offline
                Vana
                wrote on last edited by
                #7

                I add this into main.cpp, and I see nothing, only "200" from std::cout<<res.result_int()<<std::endl;

                So the signal is not emitted ?

                1 Reply Last reply
                0
                • dheerendraD Offline
                  dheerendraD Offline
                  dheerendra
                  Qt Champions 2022
                  wrote on last edited by dheerendra
                  #8

                  It is as expected as signal is sent by some other object, we are connecting to different object. Now change your main.cpp to register object like the following. Then see what happens. Please change http->http1. This requires change in qml also from http->http1.

                  request myrequest(io_context, ctx);
                  engine.rootContext()->setContextProperty("http1", &myrequest);
                  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
                  QObject::connect(&myrequest,&request::login,[](){
                      qDebug() << "Login Successfull" <<endl;
                  });
                  

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

                  1 Reply Last reply
                  0
                  • V Offline
                    V Offline
                    Vana
                    wrote on last edited by
                    #9

                    I change request to http1, still have issue.

                    Here all source for the concerned class:

                    1 Reply Last reply
                    0
                    • dheerendraD Offline
                      dheerendraD Offline
                      dheerendra
                      Qt Champions 2022
                      wrote on last edited by
                      #10

                      @dheerendra said in Signal from c++ to QML with setContextProperty:

                      qDebug() << "Login Successfull" <<endl;

                      Did the above debug message come ?

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

                      1 Reply Last reply
                      0
                      • dheerendraD Offline
                        dheerendraD Offline
                        dheerendra
                        Qt Champions 2022
                        wrote on last edited by
                        #11

                        Following line is culprit for your issue.

                        std::make_shared<request>(ioc, ctx)->run(std_host, std_port, std_target, version, std_method, std_body );

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

                        V 1 Reply Last reply
                        0
                        • dheerendraD dheerendra

                          Following line is culprit for your issue.

                          std::make_shared<request>(ioc, ctx)->run(std_host, std_port, std_target, version, std_method, std_body );

                          V Offline
                          V Offline
                          Vana
                          wrote on last edited by
                          #12

                          How I can fix this issue ?
                          create a pointer rather than a smartpointer ?

                          1 Reply Last reply
                          0

                          • Login

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