百度地图(html,js)与QT(C++)交互

前言

        上节讲到qt for android开发百度地图,已经可以打开地图了,里面的一些功能,如自定义搜索栏,添加控件等等,这些百度地图官方开发文档都提供了例子,可以自定义开发。但是问题也来了,我们开发百度地图,肯定要进行数据等交互。我们使用的是qt for android进行开发,要用qt交互共享单车地图的数据。

百度地图(html,js)与QT(C++)交互_第1张图片

        上图就是介绍用户请求扫码时服务器与客户端交互的流程,用户在地图上请求扫码骑行,要将信息传给qt,qt在把请求传递给服务器端,服务器端解析并做出相应的处理,将响应发送给qt,qt再把响应发送到地图上,根据响应显示给用户看。因为地图是采用html的网页,这就涉及到了C++与html的交互,接下来我就讲解C++与html的交互。

        这也是卡了我很久,在网上查了一大堆的资料,查了一个星期,发现没有,只有一些模模糊糊的资料,还是残缺的,太难了。

C++(qt)与html(JS)的交互 

        需要用哪些东西呢?想要直接交互是不可能的,因为qt并没有封装这个接口,需要自己写一个,以下是交互需要用到的,我来一个个进行讲解

(1)QWebSocketServer服务器

        为什么要用到服务器呢,因为C++(qt)想要直接与html(JS)直接交互,肯定行不通的,居然官方肯定直接给接口了。需要间接交互,通过QWebSocketServer服务器,C++(qt)和html(JS)作为客户端,连接QWebSocketServer服务器,QWebSocketServer服务器在对双方发送的消息分别进行派送。这样就能实现交互啦,代码如下所示

//以非安全模式,创建一个QWebSocketServer 实例作为html(js) 与 C++(qt) 的交互服务器
QWebSocketServer server(QStringLiteral("html(js) 与 C++(qt) 的交互服务器 "),   
                        QWebSocketServer::NonSecureMode);
//监听本机端口12345
if (!server.listen(QHostAddress::LocalHost, 12345)) {
    qFatal("Failed to open web socket server.");
    return 1;
}

服务器有了,还需要客户端 

(2)写一个WebSocketClientWrapper客户端的封装类

        WebSocketClientWrapper.h代码如下

#ifndef WEBSOCKETCLIENTWRAPPER_H
#define WEBSOCKETCLIENTWRAPPER_H

#include 
#include 
#include 

class WebSocketClientWrapper : public QObject
{
    Q_OBJECT

public:
    WebSocketClientWrapper(QWebSocketServer *server, QObject *parent = nullptr);

signals:
    void clientConnected(WebSocketTransport *client);

private slots:
    void handleNewConnection();

private:
    QWebSocketServer *m_server;
};

#endif // WEBSOCKETCLIENTWRAPPER_H

        WebSocketClientWrapper.cpp代码如下 

#include "websocketclientwrapper.h"
#include "websockettransport.h"

WebSocketClientWrapper::WebSocketClientWrapper(QWebSocketServer *server, QObject *parent)
    : QObject(parent)
    , m_server(server)
{
    connect(m_server, &QWebSocketServer::newConnection,
            this, &WebSocketClientWrapper::handleNewConnection);
}

void WebSocketClientWrapper::handleNewConnection()
{
    //调用nextPendingConnection()接受一个挂起的TcpSocket连接,该函数返回一个指向QTcpSocket的指针,
    //同时进入到QAbstractSocket::ConnectedState状态。这样就可以和客户端进行通信了。
    emit clientConnected(new WebSocketTransport(m_server->nextPendingConnection()));
}

         上面的代码什么意思呢?就是一个WebSocket客户端的封装,当监听到QWebSocketServer服务区有连接到来时,发送信号通知QWebChannel有连接到来,服务器因为需要与html交互,要与QWebChannel联合使用,下面会讲解为什么要QWebChannel

(2)QWebChannel

        从上述中我们知道,QWebSocketServer服务器是需要客户端进行交互的,对应的客户端是怎么写的呢,肯定是QWebSocket了。QT中确实要这个已经封装好的QWebSocket函数,我们可以直接创建一个实例即可,在HTML(js)用WebSocket了来连接服务器。     

         在官方的介绍中:C++与html(JS)的交互要使用到QtWebChannel模块,该模块提供了在QML/C++和HTML/Javascript之间的一个简单、易用的桥接,从而使得开发能够使用Qt和Web技术进行混合开发,目前QT官方也推荐是用QtWebChannel来桥接C++和HTML。  

        官方也考虑到了这个问题,采用了QT的特性,信号与槽机制,写了一个qwebchannel.js文件,我们只需要把这个文件放在与html的同一目录下,然后在html文件中引入即可。

        在qt中需要设置一个QWebChannel的 实例channel,然后连接信号与槽,当clientWrapper监听到服务区有连接到来时,触发槽函数&QWebChannel::connectTo

// 设置QWebSocketServer
//QWebSocketServer::SslMode 是运行模式,有两种:安全模式(SecureMode)wss,非安全模式(NonSecureMode)ws,这里使用非安全模式构建
QWebSocketServer server(QStringLiteral("html(js) 与 C++(qt) 的交互服务器 "), 
                        QWebSocketServer::NonSecureMode);


if (!server.listen(QHostAddress::LocalHost, 12345)) {
    qFatal("Failed to open web socket server.");
    return 1;
}

// 在QWebChannel AbstractTransport对象中包装WebSocket客户端
WebSocketClientWrapper clientWrapper(&server);

// 设置 channel
QWebChannel channel(nullptr);
QObject::connect(&clientWrapper, &WebSocketClientWrapper::clientConnected,
                 &channel, &QWebChannel::connectTo);

// 设置 networkInterface 并将其发布到QWebChannel
channel.registerObject(QStringLiteral("msg_interface"), networkInterface::getInstance());

 注册一个类到channel中,名字为msg_interface让这个类与html通信

 QWebChannel::connectTo函数原型如下,需要传入QWebChannelAbstractTransport类型的参数

void connectTo(QWebChannelAbstractTransport *transport);

 这里我们需要重写QWebChannelAbstractTransport这个类

(3)重写QWebChannelAbstractTransport

        写一个WebSocketTransport类,继承QWebChannelAbstractTransport,重写基类的sendMessage函数,websockettransport.h代码如下:

#ifndef WEBSOCKETTRANSPORT_H
#define WEBSOCKETTRANSPORT_H

#include 
#include 


class WebSocketTransport : public QWebChannelAbstractTransport
{
    Q_OBJECT
public:
    explicit WebSocketTransport(QWebSocket *socket);
    virtual ~WebSocketTransport();

    void sendMessage(const QJsonObject &message) override;

private slots:
    void textMessageReceived(const QString &message);

private:
    QWebSocket *m_socket;
};

#endif // WEBSOCKETTRANSPORT_H

        websockettransport.cpp代码如下: 

#include "websockettransport.h"
#include 
#include 
#include 
#include 

/*
    在内部使用QWebSocket的简要QWebSocket实现

    传输将通过QWebSocket接收的所有消息委托给textMessageReceived信号。
    类似地,对sendTextMessage的所有调用都将通过QWebSocket发送到远程客户端。
*/

/*
    构造传输对象并包装给定的套接字。

    套接字也被设置为传输对象的父对象。
*/
WebSocketTransport::WebSocketTransport(QWebSocket *socket): QWebChannelAbstractTransport(socket)
, m_socket(socket)
{
    //
    connect(m_socket, &QWebSocket::textMessageReceived,
            this, &WebSocketTransport::textMessageReceived);
    connect(m_socket, &QWebSocket::disconnected,
            this, &WebSocketTransport::deleteLater);
}

/*
    销毁 WebSocketTransport.
*/
WebSocketTransport::~WebSocketTransport()
{
    m_socket->deleteLater();
}

/*
    序列化JSON消息,并通过WebSocket将其作为文本消息发送到qt客户端。
*/
void WebSocketTransport::sendMessage(const QJsonObject &message)
{
    QJsonDocument doc(message);
    m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
}

/*
    反序列化字符串化的JSON messageData并发出messageReceived。
*/
void WebSocketTransport::textMessageReceived(const QString &messageData)
{
    QJsonParseError error;
    QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);
    if (error.error) {
        qWarning() << "Failed to parse text message as JSON object:" << messageData
                   << "Error is:" << error.errorString();
        return;
    } else if (!message.isObject()) {
        qWarning() << "Received JSON message that is not an object: " << messageData;
        return;
    }
    //----------待续---------2022.5.3
}

        代码主要处理socket客户端的消息和连接处理 ,然后发送给QWebChannel

(4)将qwebchannel.js文件引入html

        将qwebchannel.js文件引入html之后,使用js的WebSocket接口,创建一个WebSocket实例,连接本机的服务器,端口号为:12345,然后在WebSocket连接成功的js代码块中直接new 一个QWebChannel,然后监听注册的对象m_interface的信号即可,通过信号与html来交互,那么html怎么与m_interface交互呢,直接调用m_interface的槽函数即可。


到这里就讲完了,C++与html的交互有一些细节方面的讲不完,等后面有空在补充吧,觉得有用的,点个赞支持一下吧,对我的模拟共享单车开发感兴趣的,欢迎关注我查看哦!持续更新中

 

你可能感兴趣的:(模拟共享单车开发之路,qt,for,android,qt,html,c++)