Qt QWebSocket实现JS调用C++

目录

  • 前言
  • 1、QWebChannel如何与网页通信
  • 2、QWebSocket+QWebChannel与网页通信
    • 2.1 WebSocketTransport
    • 2.2 WebSocketClientWrapper
    • 2.3 初始化WebSocket服务器
    • 2.4 前端网页代码修改
  • 总结

前言

本篇主要介绍实现JS调用C++的另一种方式,即QWebSocket+QWebChannel。与之前的一篇文章QWebEngine 加载网页及交互,实现C++与JS 相互调用中提到的仅通过QWebChannel 实现JS调C++相比,本文介绍的这种方式,更灵活,能实现更加复杂的业务功能。

1、QWebChannel如何与网页通信

这篇文章中提到的QWebChannel实现JS调用C++,有两个重要的步骤:

  1. 在前端代码中引入qwebchannel.js,并创建QWebChannel对象。
  2. 在C++代码中创建QWebChannel实例并注册JS端访问的C++对象,然后将其设置到QWebEnginePage中 ui->webview->page()->setWebChannel(pWebChannel);

为什么要这么做呢?根据Qt官方文档可知,要通过 QWebChannel 进行C++与JS 通信,前端必须使用 qwebchannel.js 提供的 JavaScript API。对于在 Qt WebEngine 中运行的前端页面,可以通过 qrc:///qtwebchannel/qwebchannel.js 加载该文件。对于外部浏览器页面,需要将该文件复制到 Web 服务器上。然后,实例化一个 QWebChannel 对象,并传递一个传输对象和一个回调函数给它。该回调函数将在QWebChannel初始化完成并发布的对象可用时被调用。代码如下:

new QWebChannel(qt.webChannelTransport, function(channel) {//在此处获取C++中注册到QWebChannel的对象
})

qt.webChannelTransport 是 QtWebEngine 挂载到前端全局环境中的 window.qt.webChannelTransport,即传输对象。传输对象实现了一个最小的消息传递接口。它有 send() 函数,该函数接受一个序列化的 JSON 消息并将其传输给服务器端的 QWebChannelAbstractTransport 对象。此外,当接收到来自服务器的消息时,应调用其 onmessage 属性。这部分代码在qwebchannel.js文件中,如下所示:


var QWebChannel = function(transport, initCallback)
{if (typeof transport !== "object" || typeof transport.send !== "function") {console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +" Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));return;}var channel = this;this.transport = transport;//JS端向C++端发送消息this.send = function(data){if (typeof(data) !== "string") {data = JSON.stringify(data);}channel.transport.send(data);}//接收C++端传来的消息this.transport.onmessage = function(message){var data = message.data;if (typeof data === "string") {data = JSON.parse(data);}switch (data.type) {case QWebChannelMessageTypes.signal:channel.handleSignal(data);break;case QWebChannelMessageTypes.response:channel.handleResponse(data);break;case QWebChannelMessageTypes.propertyUpdate:channel.handlePropertyUpdate(data);break;default:console.error("invalid message received:", message.data);break;}}
};

那qt.webChannelTransport何时挂载的呢?就是在C++端调用page()->setWebChannel(pWebChannel)时将qt.webChannelTransport挂载到JS环境中,这一点可以通过注销这行代码运行程序看效果。你会看到终端报一个 js: Uncaught ReferenceError: qt is not defined的错误。所以要在网页加载完成之前调用setWebChannel函数。通过下图能更清楚的了解这个交互过程
在这里插入图片描述

2、QWebSocket+QWebChannel与网页通信

2.1 WebSocketTransport

先看代码,WebSocketTransport类继承自QWebChannelAbstractTransport,用于发送和接收消息。它通过 textMessageReceived 处理所有QWebSocket接收的消息。同样,所有 sendTextMessage 的调用将通过 QWebSocket 发送给远程客户端。类声明如下

class 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;
};

源码如下:

#include "websockettransport.h"#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QWebSocket>WebSocketTransport::WebSocketTransport(QWebSocket *socket)
: QWebChannelAbstractTransport(socket)
, m_socket(socket)
{connect(socket, &QWebSocket::textMessageReceived,this, &WebSocketTransport::textMessageReceived);connect(socket, &QWebSocket::disconnected,this, &WebSocketTransport::deleteLater);
}void WebSocketTransport::sendMessage(const QJsonObject &message)
{QJsonDocument doc(message);m_socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJsonDocument::Compact)));
}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;}emit messageReceived(message.object(), this);
}

2.2 WebSocketClientWrapper

WebSocketClientWrapper 是连接到WebSocket服务的客户端的简单封装,将连接的socket 通过clientConnected信号传给消费者。


class WebSocketTransport;
class QWebSocketServer;
class WebSocketClientWrapper : public QObject
{Q_OBJECTpublic:WebSocketClientWrapper(QWebSocketServer *server, QObject *parent = nullptr);signals:void clientConnected(WebSocketTransport *client);private slots:void handleNewConnection();private:QWebSocketServer *m_server;
};

源码如下:

#include "websocketclientwrapper.h"
#include "websockettransport.h"
#include <QWebSocketServer>WebSocketClientWrapper::WebSocketClientWrapper(QWebSocketServer *server, QObject *parent): QObject(parent), m_server(server)
{connect(server, &QWebSocketServer::newConnection,this, &WebSocketClientWrapper::handleNewConnection);
}void WebSocketClientWrapper::handleNewConnection()
{emit clientConnected(new WebSocketTransport(m_server->nextPendingConnection()));
}

2.3 初始化WebSocket服务器

初始化WebSocket服务器,并连接到QWebChannel

    m_webSocketServer  = new QWebSocketServer(QStringLiteral("QWebSocketServer + QWebChannel Test"), QWebSocketServer::NonSecureMode);if (!m_webSocketServer->listen(QHostAddress::LocalHost, 65535)) {qFatal("Failed to open web socket server.");return 1;}m_webSocketClientWrapper = new WebSocketClientWrapper(m_webSocketServer);m_pWebObj =  new WebObject();QWebChannel *pWebChannel = new QWebChannel();pWebChannel->registerObject("nativeObj", m_pWebObj);//连接到webchannelconnect(m_webSocketClientWrapper, &WebSocketClientWrapper::clientConnected,&pWebChannel, &QWebChannel::connectTo);

2.4 前端网页代码修改

前端网页代码如下:

<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, viewport-fit=cover"><title>QWebEngineTest</title><script type="text/javascript" src="./qwebchannel.js"></script><script type="text/javascript">//C++ 调用showalert函数function showalert(text) {alert(text)}	//C++ 调用getJsData函数function getJsData() {return "C++ Call JS demo"}	var nativeObj window.onload = function() {//创建websocket客户端var socket = new WebSocket('ws://127.0.0.1:65535');socket.onclose = function() {console.error("web channel closed");};socket.onerror = function(error) {console.error("web channel error: " + error);};socket.onopen = function() {new QWebChannel(socket, function(channel) {nativeObj = channel.objects.nativeObj;nativeObj.nativeTextChanged.connect(function(text){alert("nativeTextChanged: " + text)})});}}function jsCallCpp (){nativeObj.setNativeText("JS Call C++ test ")}function getNativeText(){alert("new nativeText is: "  + nativeObj.nativeText)}</script></head><body><p>QWebEngineTest</p><button onclick="jsCallCpp()" >调用C++对象的函数setNativeText</button><button onclick="getNativeText()" >获取C++对象属性nativeText </button></body>
</html>

运行效果如下图所示
在这里插入图片描述

至此我们实现了QWebSocket+QWebChannel与网页通信的功能,与单纯使用QWebChannel实现网页通信相比,QWebSocket+QWebChannel方式允许前端代码在任何浏览器上运行,而单纯使用QWebChannel的方式只能将前端网页嵌入到QWebEngine中展现。除此之外,前端代码中,QWebChannel 对象的创建时机也不同,QWebSocket+QWebChannel方式要求在onopen回调中创建QWebChannel对象,而只使用QWebChannel的方式在C++端调用该接口setWebChannel(pWebChannel)后就可以。

var socket = new WebSocket('ws://127.0.0.1:65535');
socket.onclose = function() {console.error("web channel closed");
};socket.onerror = function(error) {console.error("web channel error: " + error);
};socket.onopen = function() {//在onOpen函数中创建QWebChannelnew QWebChannel(socket, function(channel) {nativeObj = channel.objects.nativeObj;nativeObj.nativeTextChanged.connect(function(text){alert("nativeTextChanged: " + text)})});
}

总结

以上就是本文要讲的内容了,本文详细介绍了QWebChannel与网页端通信的两种方式,希望通过阅读本文,能帮你快速掌握在Qt 前后端混合开发模式下C++与JS通信的方法。对文中内容有任何疑问,都可以留言讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/144955.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ之消息应答和持久化

文章目录 前言一、消息应答1.概念2.自动应答3.消息应答方法4.Multiple 的解释5.消息自动重新入队6.消息手动应答代码7.手动应答效果演示 二、RabbitMQ持久化1.概念2.队列如何实现持久化3.消息实现持久化4.不公平分发5.预取值 总结 前言 在RabbitMQ中&#xff0c;我们的消费者在…

SQL题

[极客大挑战 2019]EasySQL 进行简单的尝试&#xff0c;就知道是单引号的字符型注入 万能密码进行一个简单的尝试 结果就出来了 还是要了解一下原理 输入的是1&#xff0c;形成的sql语句是错误的SELECT*FROM table_name WHERE username1and password123; 第一个单引号和第二个…

01Urllib

1.什么是互联网爬虫&#xff1f; 如果我们把互联网比作一张大的蜘蛛网&#xff0c;那一台计算机上的数据便是蜘蛛网上的一个猎物&#xff0c;而爬虫程序就是一只小蜘蛛&#xff0c;沿着蜘蛛网抓取自己想要的数据 解释1&#xff1a;通过一个程序&#xff0c;根据Url(http://www.…

Flutter应用-使用sqflite升级数据库

文章目录 问题描述具体做法代码示例更多条件限制升级 数据库迁移和备份简介数据库迁移数据库备份 问题描述 使用fluttter开发的应用程序发布后&#xff0c;发现数据库有些设计不合理。如何来更新数据库呢&#xff1f; 使用sqflite来处理数据库&#xff0c;但是第一版软件发布后…

JQuery ajax 提交数据提示:Uncaught TypeError:Illegal invocation

JQuery ajax 提交数据提示&#xff1a;Uncaught TypeError:Illegal invocation 1 问题描述 用jQuery Ajax向DRF接口提交数据的时候&#xff0c;console提示&#xff1a;Uncaught TypeError:Illegal invocation(未捕获的异常&#xff1a;非法调用)。 这个问题可能有两种原因导…

Zookeeper学习笔记(1)—— 基础知识

Zookeeper概述 Zookeeper 是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的 Apache 项目 工作机制 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受…

【AI视野·今日Robot 机器人论文速览 第六十三期】Thu, 26 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Fri, 27 Oct 2023 Totally 27 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers 6-DoF Stability Field via Diffusion Models Authors Takuma Yoneda, Tianchong Jiang, Gregory Shakhnarovich, Matthew R. …

微软Surface/Surface pro笔记本电脑进入bios界面

微软Surface笔记本电脑进入bios界面 方法一推薦這種方法&#xff1a;Surface laptop 进BIOS步骤 开机后&#xff0c;不停按音量键进bios界面。 方法二&#xff1a;Surface Book、Surface Pro进bios步骤 1、关闭Surface&#xff0c;然后等待大约10秒钟以确保其处于关闭状态。…

SQL中的数据类型和规范化,助力数据存储优化

大家好&#xff0c;目前优化数据存储对于获得良好的性能始终至关重要&#xff0c;选择合适的数据类型并应用正确的规范化过程对于决定其性能至关重要。本文将介绍最重要和最常用的数据类型和规范化过程。 一、SQL中的数据类型 SQL中主要有两种数据类型&#xff1a;字符串和数…

小程序action-sheet结合自定义tabbar显示

要实现此效果&#xff0c;遇到的问题&#xff1a;背景在电脑端调试的情况正常的情况下&#xff0c;手机端点击事件工单&#xff0c;返回回来的时候action-sheet卡住在屏幕上&#xff0c;点击遮罩层都不消失。更奇怪的是 这种情况并不是每次发生&#xff0c;而是有时候发生&…

给VSCode插上一双AI的翅膀

#AI编程助手哪家好&#xff1f;DevChat“真”好用# 文章目录 前言一、安装DevChat1.1、访问地址1.2、注册1.3、在VSCode里安装DevChat插件1.3.1、未安装状态1.3.2、已安装状态 二、设置Access Key2.1. 点击左下角管理&#xff08;“齿轮”图标&#xff09;—命令面板&#xff…

DNS域名解析服务

1.概述 1.1.产生原因 IP 地址:是互联网上计算机唯一的逻辑地址&#xff0c;通过IP 地址实现不同计算机之间的相互通信&#xff0c;每台联网计算机都需要通过I 地址来互相联系和分别&#xff0c;但由于P 地址是由一串容易混淆的数字串构成&#xff0c;人们很难记忆所有计算机的…

【7】Spring Boot 3 集成组件:缓存组件 spring cache + spring data redis

目录 【7】Spring Boot 3 集成组件&#xff1a;缓存组件 spring cache spring data redis什么是缓存抽象声明式注解JSR-107对应SpEL上下文数据 引入依赖cache 支持的缓存类型缓存类型配置NONESIMPLEREDIS自定义配置 CAFFEINE Hazelcast...总结 个人主页: 【⭐️个人主页】 需要…

企业邮箱认证指南:安全与高效的邮箱认证方法

企业邮箱是专门为企业提供的电子邮件服务&#xff0c;安全性和专业性更高。在开始使用企业邮箱之前&#xff0c;很多人会有一些问题&#xff0c;比如企业邮箱需要认证吗、如何开通企业邮箱&#xff0c;以及哪款企业邮箱好。 1、企业邮箱在使用前需要认证吗&#xff1f; 答案是肯…

esp-01刷固件/下载软件到内部单片机的方法

此文章为转载&#xff0c;非原创 一、准备 需要准备三个东西&#xff1a; 1.esp模块。ESP-01 和 ESP-01s 的引脚及 flash 容量基本完全兼容&#xff0c;只是内部硬件设计粗糙与否的区别&#xff0c;所以理论上都适用。 2.官方固件。此部分可以从安信可官方教程中下载&#xff0…

MySQL库的操作『增删改查 ‖ 编码问题 ‖ 备份与恢复』

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; MySQL 学习 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 &#x1f381;软件版本&#xff1a; MySQL 5.7.44 文章目录 1.创建数据库2.数据库中的编码问题2.1.字符集与校验集2.3.支持的字符…

数据结构:AVLTree的插入和删除的实现

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》 文章目录 前言一、AVLTree二、AVLTree的插入插入新增节点调整平衡因子旋转左单旋(新增节点位于较高右子树的右侧)右单旋(新增节点位于较高左子树的左侧)右左双旋(新增节点在较高右子树的左子…

Unity中Shader雾效的实现方法三

文章目录 前言一、声明雾效所需要的内置变体二、在 v2f 中&#xff0c;定义 float4 worldPos &#xff1a;TEXCOORD1三、在顶点着色器中添加&#xff0c;会自动取o.worldPos.w裁剪空间下的坐标z值.在这里插入图片描述 四、在片元着色器&#xff0c;添加以下两句话1、UNITY_EXTR…

三江城115m²3室2厅2卫,现代简约不单是居所更是对生活的向往。福州中宅装饰,福州装修

【前言】 简洁有力&#xff0c;静默无声。 以简约精致的方式&#xff0c;展现现代都市生活&#xff1b; 经典不因潮流褪色&#xff0c;不为悦人只为悦己。 项目信息 项目名称 | 三江城 设计地址 | 福建福州 项目面积 | 115㎡ 项目户型 | 3室2厅 设计风格 | 现代简约 全…

LeetCode(23)找出字符串中第一个匹配项的下标【数组/字符串】【简单】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 找出字符串中第一个匹配项的下标 1.题目 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 hays…