0

I emit a signal from c++ and try to call a slot from QML. I use Setcontextproperty and Connection :

main.qml

import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3

Window {
 visible: true
 width: 640
 height: 480

 Index {
    id: chat
    visible: false
  }

  SwipeView {
    id: swipeView
    anchors.fill: parent
    currentIndex: tabBar.currentIndex

    Signin {
        id: signin
    }

 }

TabBar {
        id: tabBar
        x: 274
        y: 123
        currentIndex: swipeView.currentIndex

        TabButton {
            text: qsTr("Login")
        }
  }

}

Signin.qml

    Button {
    id: send
    x: 280
    y: 257
    width: 61
    height: 25
    text: qsTr("Connect")
    Connections {
        target: http
        onLogin : console.log("test200");
    }

    onClicked:{
        http.start_request("/login", "443", "/", 11, "POST", "username=" + login.text + "&password=" + password.text)
       }

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

#include "request.h"

#include <QDebug>
#include <QObject>
#include <QQuickItem>
#include <QQmlContext>


boost::asio::io_service io_service;

int main(int argc, char *argv[])
{

QGuiApplication app(argc, argv);

QQmlApplicationEngine engine;


std::string login = "allo";

boost::asio::io_context ioc;
boost::asio::ssl::context ctx{ssl::context::sslv23_client};
request request(ioc, ctx);


engine.rootContext()->setContextProperty("tcp_client", &c);
engine.rootContext()->setContextProperty("http", &request);

engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

);

return app.exec();
}

request.h

#ifndef REQUEST_H
#define REQUEST_H

#include <QString>
#include "hash_password.h"
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <QObject>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

using tcp = boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
namespace http = boost::beast::http;
namespace pt = boost::property_tree;

class request : public QObject, public std::enable_shared_from_this<request>
{
Q_OBJECT

signals:
 void login();

public:
explicit request(boost::asio::io_context& ioc, boost::asio::ssl::context& ctx);
void run(char const* host, char const* port, char const* target, int version, QString method, QString body = NULL);
Q_INVOKABLE void start_request( QString host, QString port, QString target, int version, QString method, QString body = NULL);
std::string respond;
QString GetmessageError;

private:
hash_password hashed_password;
tcp::resolver resolver;
boost::asio::ssl::stream<tcp::socket> stream;
boost::beast::flat_buffer buffer;
boost::beast::http::request<http::string_body> req;
boost::beast::http::response<http::string_body> res;

void on_resolve(boost::system::error_code ec, boost::asio::ip::tcp::resolver::results_type results);
void on_connect(boost::system::error_code ec);
void on_handshake(boost::system::error_code ec);
void on_write(boost::system::error_code ec, std::size_t bytes_transferred);
void on_read(boost::system::error_code ec, std::size_t bytes_transferred);
void on_shutdown(boost::system::error_code ec);
void fail(boost::system::error_code ec, char const* what);

//json_parser Jsonresponse;
std::string GetApiResponse(std::stringstream Jsonres, pt::ptree root);

};

#endif // REQUEST_H

request.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, int version, QString method, QString body)
{
 // Set SNI Hostname (many hosts need this to handshake successfully)
 if(! SSL_set_tlsext_host_name(stream.native_handle(), host))
 {
    boost::system::error_code ec{static_cast<int>(::ERR_get_error()), 
  boost::asio::error::get_ssl_category()};
    std::cerr << ec.message() << "\n";
    respond = "test" ;
    return;
 }

// 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();

// Look up the domain name
resolver.async_resolve(host, port, std::bind(&request::on_resolve, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
 }

void request::on_resolve(boost::system::error_code ec, tcp::resolver::results_type results)
{
 if(ec){
    //return fail(ec, "resolve");
    respond = "test" ;
}
// Make the connection on the IP address we get from a lookup
boost::asio::async_connect(stream.next_layer(), results.begin(), 
results.end(), std::bind( &request::on_connect, shared_from_this(), std::placeholders::_1));
 }

void request::on_connect(boost::system::error_code ec)
{
 if(ec){
    //return fail(ec, "connect");
     respond = ec.message();
    }

 // Perform the SSL handshake
 stream.async_handshake(ssl::stream_base::client, std::bind(&request::on_handshake, shared_from_this(), std::placeholders::_1));
  }

  void request::on_handshake(boost::system::error_code ec)
  {
  if(ec){
    return fail(ec, "handshake");
  }

 // Send the HTTP request to the remote host
 http::async_write(stream, req, std::bind(&request::on_write, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
 }

 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
 http::async_read(stream, buffer, res, std::bind(&request::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
  }

  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");

   if(res.result_int() == 200)
   {
   std::cout<<res.result_int()<<std::endl;
   emit login();
   }

   else
    std::cout<<"allola"<<std::endl;


// Gracefully close the stream
  stream.async_shutdown(std::bind(&request::on_shutdown, shared_from_this(), std::placeholders::_1));
  }

  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");

  // If we get here then the connection is closed gracefully
  }

  void request::start_request(QString host, QString port, QString target, 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();
  }

  std::string request::GetApiResponse(std::stringstream Jsonres, pt::ptree root)
 {
  pt::read_json(Jsonres, root);
  std::cout<<root.get<std::string>("user.permission")<<std::endl;
  return root.get<std::string>("user.permission");
 }

I can see the http request (from logs), can see the cout "200" from c++ in request.cpp but not the response from qml, console.log("test200").

So what is wrong ?

Update: minimal example with same issue main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "request.h"
#include <QQmlContext>

int main(int argc, char *argv[])
{
  QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

  QGuiApplication app(argc, argv);

  QQmlApplicationEngine engine;
  request request;
  engine.rootContext()->setContextProperty("http", &request);
  engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

  if (engine.rootObjects().isEmpty())
    return -1;

  return app.exec();
 }

request.h

#ifndef REQUEST_H
#define REQUEST_H

#include <QObject>

class request : public QObject
{
  Q_OBJECT

 public:
 request();

 signals:
 void connection();

 public:
 Q_INVOKABLE void start();
 };

  #endif // REQUEST_H

request.cpp

#include "request.h"
#include <iostream>

request::request()
{
}

void request::start()
{
  for(int i=0; i<10; i++)
    std::cout<<"Hello World from c++"<<std::endl;
  emit connection();
}

main.qml

import QtQuick 2.0
import QtQuick.Controls 2.3

ApplicationWindow {
width: 640
height: 480

Connections {
    target: http
    onConnection: console.log("Hello from QML");
}

Button {
    id: button
    x: 251
    y: 258
    text: qsTr("Button")
    onClicked: http.start()
}
}
Vana
  • 753
  • 3
  • 11
  • 20
  • I update my post with complete request class – Vana May 21 '18 at 10:13
  • I'm not an expert in boost, but I wanted to replicate your problem but I need the hash_password code, but I think the connection is between a signal that is emitted in another thread, is that correct? – eyllanesc May 21 '18 at 17:45
  • You can delete include 'hash_password.h", I have not implemented this classe yet. (The class in empty^^) – Vana May 21 '18 at 18:15
  • The issue in only between the signal emit from c++ and qml component. But I think you can not test my code, because you need to access my api – Vana May 21 '18 at 18:17
  • Have you tried to do a connection on the C++ side? Also the question from *eyllanesc* again: Is it emitted in another thread? And: Could you provide a *minimal, complete and verifiable* example that reproduces your problem? So no `hash_password` or other things that we can't run. – derM - not here for BOT dreams May 22 '18 at 12:26
  • I update my code, I delete the include to hash_password class and add full main.cpp – Vana May 22 '18 at 19:12
  • I upadte my post with main.qml . But my app is a little big, so I can not really post all code. And this app do http request to another web app – Vana May 22 '18 at 20:13
  • 1
    @Vana Nobody has asked you to publish your project, they often confuse the concept of an MCVE because they have not read the proposed link, the MCVE is not your project, it could if your project is small, an MCVE refers in your case to that you create an minimum code that we can copy and execute quickly without the need for external elements. If you make life easy for us, we will try to make it for you as well. – eyllanesc May 22 '18 at 20:18
  • I update my post with minimal code with same issue – Vana May 24 '18 at 19:02
  • @Vana In your example you do not call `onClicked: http.start()`, in addition to it `Item` with `QQmlApplicationEngine` does not work, you have to use `Window` or `ApplicationWindow`, correcting it works correctly. So what you have added is not an MCVE – eyllanesc May 24 '18 at 19:16
  • I update my post with fixed issues – Vana May 24 '18 at 19:24
  • @Vana That code does work, it does not reproduce your problem. – eyllanesc May 24 '18 at 19:33
  • @Vana see https://imgur.com/a/7ZBXoyW – eyllanesc May 24 '18 at 19:35
  • I think I found where is the issue. I create an instance of my class when I click on a button. So I have to get the signal of this instance – Vana May 24 '18 at 19:37
  • So I think i must add Connection{} into QML Button instance, right ? – Vana May 24 '18 at 19:42
  • @Vana I tell you back, your last current code is correct, I do not understand what you say. Have you tried your MCVE? also use @ next to my nickname to be notified in a timely manner. – eyllanesc May 24 '18 at 19:49
  • @eyllanesc yes I try my MCVE. – Vana May 24 '18 at 20:08
  • @Vana And it worked for you ?, for me who have tried your last code I have not had any problems. – eyllanesc May 24 '18 at 20:09
  • @eyllanesc yes it works. But on my first code (with boost) it does not work. – Vana May 24 '18 at 20:10
  • @eyllanesc and I use the same connection – Vana May 24 '18 at 20:11
  • @Vana So your last code is not an MCVE, so we can not help you, try to create one that looks more like your original code but eliminates the connection to your API. For me as a comment is that you are running from another thread and that can cause problems, if I can not reproduce it can not help you at all. When you have something better you advise. bye. – eyllanesc May 24 '18 at 20:12

0 Answers0