当前位置: 首页 > news >正文

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通信的方法。对文中内容有任何疑问,都可以留言讨论!

相关文章:

Qt QWebSocket实现JS调用C++

目录 前言1、QWebChannel如何与网页通信2、QWebSocketQWebChannel与网页通信2.1 WebSocketTransport2.2 WebSocketClientWrapper2.3 初始化WebSocket服务器2.4 前端网页代码修改 总结 前言 本篇主要介绍实现JS调用C的另一种方式&#xff0c;即QWebSocketQWebChannel。与之前的…...

Android Matrix的使用详解(通过矩阵获取到图片缩放比例和角度)

网上查了好久相关的资料&#xff0c;都没有明确的答案。最终通过多次测试结果&#xff0c;结合安卓定义的矩阵含义&#xff0c;推算出来矩阵的数学含义以及相关的计算公式 1.获取Matrix矩阵&#xff1a; Matrix matrix new Matrix(); float[] matrixValues new float[9]; …...

【Spring】bean的生命周期

这里写目录标题 1. 在类中提供生命周期控制方法&#xff0c;并在配置文件中配置init-method&destroy-method&#xff08;配置&#xff09;关闭容器操作1&#xff1a;ctx.close()关闭容器操作2&#xff1a;关闭钩子&#xff1a;ctx.registerShutdownHook() 2. 实现接口来做和…...

C#运算符重载

运算符重载允许你重新定义内置运算符&#xff08;如、-、*等&#xff09;的行为&#xff0c;以便它们可以用于自定义类型&#xff08;类/结构体&#xff09;。通过运算符重载&#xff0c;你可以为自定义类型创建更直观和灵活的操作。 在C#中&#xff0c;可以重载的运算符如下&…...

【L2GD】: 无环局部梯度下降

文章链接&#xff1a;Federated Learning of a Mixture of Global and Local Models 发表期刊&#xff08;会议&#xff09;: ICLR 2021 Conference&#xff08;机器学习顶会&#xff09; 往期博客&#xff1a;FLMix: 联邦学习新范式——局部和全局的结合 目录 1.背景介绍2. …...

2023-11-14 LeetCode每日一题(阈值距离内邻居最少的城市)

2023-11-14每日一题 一、题目编号 1334. 阈值距离内邻居最少的城市二、题目链接 点击跳转到题目位置 三、题目描述 有 n 个城市&#xff0c;按从 0 到 n-1 编号。给你一个边数组 edges&#xff0c;其中 edges[i] [fromi, toi, weighti] 代表 fromi 和 toi 两个城市之间的…...

AdServices归因和iAd归因集成

AdServices framework 是 Apple 专门为 ASA 提供的归因框架 。尤其在ATT 政策推出以后&#xff0c;app 获取用户 IDFA 的比例大幅降低&#xff0c;传统的依靠IDFA 的方法也无法准确归因。 但是 Apple 为 ASA 开了一个后门&#xff0c;其他广告渠道无法获取用户的 IDFA 作为身份…...

关于 内部类 你了解多少?(详解!!)

目录 1. 什么是内部类&#xff1f; 2. 内部类的分类 3. 内部类 3.1 实例内部类 3.2 静态内部类 4. 局部内部类 5. 匿名内部类 6.对象的打印 “不积跬步无以至千里&#xff0c;不积小流无以成江海。”每天坚持学习&#xff0c;哪怕是一点点&#xff01;&#xff01;&a…...

CNVD-2021-09650:锐捷NBR路由器(guestIsUp.php)RCE漏洞复现 [附POC]

文章目录 锐捷NBR路由器guestIsUp.php远程命令执行漏洞(CNVD-2021-09650)复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 锐捷NBR路由器guestIsUp.php远程命令执行漏洞(CNVD-2021-09650)复现 [附POC] 0x01 前言 免…...

如何在Docker部署Draw.io绘图工具并远程访问

文章目录 前言1. 使用Docker本地部署Drawio2. 安装cpolar内网穿透工具3. 配置Draw.io公网访问地址4. 公网远程访问Draw.io 前言 提到流程图&#xff0c;大家第一时间可能会想到Visio&#xff0c;不可否认&#xff0c;VIsio确实是功能强大&#xff0c;但是软件为收费&#xff0…...

Android APK打包的过程主要步骤

Android APK打包的过程可以概括为以下几个主要步骤&#xff1a; 编译源代码&#xff1a;将开发好的Java源代码编译成Dalvik字节码文件&#xff08;.dex文件&#xff09;&#xff0c;Android安卓该文件包含了Android平台上的运行程序的指令集。打包资源文件&#xff1a;将应用程…...

吃透 Spring 系列—MVC部分

目录 ◆ SpringMVC简介 - SpringMVC概述 - SpringMVC快速入门 - Controller中访问容器中的Bean - SpringMVC关键组件浅析 ◆ SpringMVC的请求处理 - 请求映射路径的配置 - 请求数据的接收 - Javaweb常用对象获取 - 请求静态资源 - 注解驱动 标签 ◆ SpringMV…...

Java面试题(每天10题)-------连载(32)

目录 设计模式篇 1、工厂方法模式&#xff08;利用创建同一接口的不同实例&#xff09;&#xff1a; 2、抽象工厂模式&#xff08;多个工厂&#xff09; 3、单例模式&#xff08;保证对象只有一个实例&#xff09; 4、原型模式&#xff08;对一个原型进行复制、克隆产生类…...

HDP集群Kafka开启SASLPLAINTEXT安全认证

hdp页面修改kafka配置 java代码连接kafka增加对应的认证信息 props.put("security.protocol","SASL_PLAINTEXT");props.put("sasl.mechanism","PLAIN");props.put("sasl.jaas.config","org.apache.kafka.common.securi…...

判断上颌下颌的stl模型坐标轴是否正常

文章目录 研究方向:如何判断?又如何纠正?如何判断?Demo实现:先判断一个遍历相关文件夹下的所有病例如何纠正?Demo相关知识点研究方向:如何判断?又如何纠正? 如何判断? 当然,我们不能以坐标的正负来判断 我们看到这个Bounding Box里面有stl模型的xyz三轴方向的最大值与最…...

C/C++---------------LeetCode第1189. “气球” 的最大数量

气球的最大数量 题目及要求统计法在main内使用 题目及要求 给你一个字符串 text&#xff0c;你需要使用 text 中的字母来拼凑尽可能多的单词 “balloon”&#xff08;气球&#xff09;。 字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 “ba…...

Arthas(阿尔萨斯)--(三)

目录 一、Arthas学习 1、class/classloader相关命令一 1、sc 2、sm 2、class/classloader相关命令二 1、jad 2、mc 3、redefine 三、class/classloader相关命令三 一、Arthas学习 Arthas(阿尔萨斯)--(一) Arthas(阿尔萨斯)--(二) 1、class/classloader相关命令一 …...

《变形监测与数据处理》笔记/期末复习资料(择期补充更新)

变形&#xff1a; 变形是物体在外来因素作用下产生的形状、大小及位置的变化&#xff08;随时间域和空间域的变化&#xff09;&#xff0c;它是自然界普遍存在的现象。 变形体&#xff1a; 一般包括工程建筑物、构筑物、大型机械设备以及其他自然和人工对象等。 变形体和变形…...

Linux:进程替换和知识整合

文章目录 进程程序替换替换原理进程替换的理解 环境变量与进程替换命令行解释器实现逻辑 进程程序替换 前面已经学习了子进程的创建&#xff0c;但是子进程的创建不管怎么说&#xff0c;都是父进程代码的一部分&#xff0c;那么实际上如果想要子进程执行新的程序呢&#xff1f…...

React组件在什么情况下会重新渲染

当我们使用React编写组件时&#xff0c;组件的重新渲染是一个重要的概念。重新渲染是指React组件在特定情况下会重新执行其渲染函数&#xff0c;更新用户界面以反映最新的数据。很多情况下&#xff0c;组件不必要的重新渲染会严重影响性能&#xff0c;所以要充分了解触发组件重…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...

虚幻基础:角色旋转

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 移动组件使用控制器所需旋转&#xff1a;组件 使用 控制器旋转将旋转朝向运动&#xff1a;组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转&#xff1a;必须移动才能旋转&#xff0c;不移动不旋转控制器…...

接口 RESTful 中的超媒体:REST 架构的灵魂驱动

在 RESTful 架构中&#xff0c;** 超媒体&#xff08;Hypermedia&#xff09;** 是一个核心概念&#xff0c;它体现了 REST 的 “表述性状态转移&#xff08;Representational State Transfer&#xff09;” 的本质&#xff0c;也是区分 “真 RESTful API” 与 “伪 RESTful AP…...

作为点的对象CenterNet论文阅读

摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表&#xff0c;并对每一个位置进行分类。这种做法既浪费又低效&#xff0c;并且需要额外的后处理。在本文中&#xff0c;我们采取了不同的方法。我们将物体建模为单…...

关于 ffmpeg设置摄像头报错“Could not set video options” 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/148515355 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…...