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

前言

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

        上图就是介绍用户请求扫码时服务器与客户端交互的流程,用户在地图上请求扫码骑行,要将信息传给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 <QObject>
#include <QtWebSockets/QWebSocketServer>
#include <websockettransport.h>

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文件中引入即可。

<script type="text/javascript" src="qwebchannel.js"></script>

        在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 <QtWebChannel/QWebChannelAbstractTransport>
#include <QtWebSockets/QWebSocket>


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 <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QtWebSockets/QtWebSockets>

/*
    在内部使用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的槽函数即可。

<script type="text/javascript">
    window.onload = function() {
        if (location.search != "")
            var baseUrl = (/[?&]webChannelBaseUrl=([A-Za-z0-9\-:/\.]+)/.exec(location.search)[1]);
            else
                var baseUrl = "ws://localhost:12345"; //协议标识符是ws(如果加密,则为wss)

            //output("Connecting to WebSocket server at " + baseUrl + ".");
            //用于创建一个webSocket实例,执行后,客户端就会与服务端连接, 此时的服务器就是qt的QWebSocketServer
            var socket = new WebSocket(baseUrl);

            socket.onclose = function() {
                console.error("web channel closed");
            };
            socket.onerror = function(error) {
            //alert("socket.onerror:" +  error);
                console.error("web channel error: " + error);
            };
            socket.onopen = function() {
            //output("11");
                new QWebChannel(socket, function(channel) {
                    // 声明 msg_interface 变为全局(即整个html)可见变量
                    window.msg_interface = channel.objects.msg_interface;

                    //监视msg_bridge的信号,当msg_bridge的sendBikeMsg信号触发时,就会触发该函数,相当于qt中的信号与槽
                    msg_interface.ListAllBikeMsgSig.connect(function(bikeid, status, longitude, latitude)
                    {
                        //alert("客户端已经收到共享单车服务器端的同步请求回应,正在处理......id:"+ bikeid +" longitude:" + longitude +" status"+ status+ " latitude:" + latitude);
                        addBikeMarker(bikeid, status, longitude, latitude);
                    });


                    //进入客户端后,尝试连接服务器
                    msg_interface.connectBikeServer();
                    //尝试获取权限
                    msg_interface.getPermission();

                    //javascript 字符串截取, arr=str.split(”,”);
            });
        }
    }

        //END SETUP
</script>

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

 

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt是一款跨平台的C++图形用户界面应用程序开发框架,能够轻松开发高质量的跨平台应用程序。而百度地图是目前国内最流行的Web地图服务,为开发者提供了非常便捷的地图展示、位置检索、路线规划等能力。在Qt中使用百度地图,并能够与js交互,可以大大拓宽了应用的功能。 我们可以使用Qt Quick的WebView来实现百度地图的加载,同时也能够在WebView中嵌入JavaScript代码,实现和地图的互动。在Qt Quick中,可以使用QtWebView模块的WebEngineView或WebEngineViewExperimental类型的对象加载Web页面,并进行动态链接库的调用,从而实现与JavaScript的交互。 具体步骤如下: 1. 首先,在Qt Creator中创建一个Qt Quick项目,并在QML文件中加入一个WebView组件,用于加载百度地图。 2. 在C++代码中调用WebEngineView或WebEngineViewExperimental类型的对象,通过setUrl()方法加载百度地图的URL。 3. 通过WebView对象的runJavaScript()方法,在QML文件中嵌入JavaScript代码,并获取、设置百度地图中的特定属性或事件。 例如,我们可以在JavaScript中调用百度地图的JavaScript API,获取当前地图的中心点、缩放级别、添加标记、绘制图形等动作,从而实现和地图的交互。 总之,Qt的WebEngineView提供了非常便捷的方法来在Qt应用中加载Web页面,并与JavaScript进行交互,因此,我们可以使用它轻松地实现与百度地图交互,为我们的应用程序增加更多的功能和乐趣。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值