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
Forum Updated to NodeBB v4.3 + New Features

Signal from c++ to QML with setContextProperty

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
12 Posts 4 Posters 2.6k Views 1 Watching
  • 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
    #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