Signal from c++ to QML with setContextProperty
-
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 }
-
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. -
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.qmlButton { 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 functionon_resolve
, which callon_connect
, which callon_handshake
, which callon_write
which callon_read
, where is emitted the signal . -
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 }
@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.
-
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
-
The way you have placed the code it should work. You are telling that it is not working. Issue can happen only if
- Signal is not emitted
- Connection is made after sending the signal
- Sender object & receiver objects are different
- 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; });
-
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; });
-
I change request to http1, still have issue.
-
@dheerendra said in Signal from c++ to QML with setContextProperty:
qDebug() << "Login Successfull" <<endl;
Did the above debug message come ?
-
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 );
-
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 );