使用 QWebChannel 实现 JS 与 C++ 双向通信(超详细 + 踩坑总结 + Demo)

使用 QWebChannel 实现 JS 与 C++ 双向通信(超详细 + 踩坑总结 + Demo)

使用 QWebChannel 实现 JS 与 C++ 双向通信(超详细 + 踩坑总结 + Demo)

在基于 QWebEngine 的项目中,要让 前端 JavaScript 与 后端 C++ 互相通信,是非常关键的能力。 Qt 官方提供的方案就是 QWebChannel,它能让你像调用本地对象一样从 JS 访问 C++,并且支持信号/槽、异步回调等。

但实际项目中常见各种问题:

  • JS 侧无法拿到对象?
  • 信号不触发?
  • 跨线程导致闪退?
  • 对象销毁后 JS 仍然在调用?
  • Page/Page再创建导致 channel 失效?

本文将带你彻底搞懂 QWebChannel 的机制,避坑,并给出可运行的 Demo。

一、QWebChannel 的通信原理

JS 与 C++ 的交互流程如下:

C++ QObject ←→ WebChannel Transport (QWebEngine) ←→ JS 对象 

实际通信依赖两部分:

① C++ 侧:QObject + QWebChannel

  • QObject 必须继承自 QObject
  • 想暴露给 JS 的属性/方法必须加 Q_INVOKABLE 或 Q_PROPERTY
  • 信号可以直接给 JS 发送事件
  • 将 QObject 注册进 QWebChannel:
channel->registerObject("bridge", myBridgeObject); 

② JS 侧:qwebchannel.js

网页加载后必须初始化:

new QWebChannel(qt.webChannelTransport, function(channel) {     window.bridge = channel.objects.bridge; }); 

然后:

// 调用 C++ bridge.sendMessage("hello"); // 接收 C++ 信号 bridge.messageChanged.connect(function(msg){     console.log("C++ emit:", msg); }); 

二、一个可跑的双向通信 Demo(最小可运行)

1. C++ 端代码
bridge.h
#pragma once #include <QObject> class Bridge :public QObject {     Q_OBJECT public:     explicit Bridge(QObject* parent = nullptr) : QObject(parent) {}     // JS 调用 C++     Q_INVOKABLE void sendMessage(const QString& msg) {         qDebug() << "JS 调用 C++:" << msg;         emit messageChanged("C++ 收到:" + msg);     } signals:     // C++ → JS     void messageChanged(const QString& msg); }; 
mainwindow.cpp
#include "mainwindow.h" #include <QWebEngineView> #include <QWebEnginePage> #include <QWebChannel> MainWindow::MainWindow(QWidget* parent)     : QMainWindow(parent) {     auto* view = new QWebEngineView(this);     auto* page = new QWebEnginePage(this);     view->setPage(page);     setCentralWidget(view);     // 创建 C++ 对象     auto* bridge = new Bridge(this);     // 创建 QWebChannel     auto* channel = new QWebChannel(this);     channel->registerObject("bridge", bridge);     page->setWebChannel(channel);     view->load(QUrl("qrc:/index.html"));     // 模拟 3 秒后给 JS 发消息     QTimer::singleShot(3000, [bridge]() {         emit bridge->messageChanged("来自 C++ 的问候!");     }); } 

2. 前端:index.html

<!DOCTYPE html> <html> <head>     <meta charset="UTF-8" />     <script src="qrc:///qtwebchannel/qwebchannel.js"></script> </head> <body>     <h2>QWebChannel Demo</h2>     <input id="msg" placeholder="发送给 C++ 的消息">     <button onclick="callCpp()">发送</button>     <p id="log"></p>     <script>         new QWebChannel(qt.webChannelTransport, function (channel) {             window.bridge = channel.objects.bridge;             // C++ → JS             bridge.messageChanged.connect(function (msg) {                 log("收到 C++:" + msg);             });         });         function callCpp() {             const msg = document.getElementById("msg").value;             bridge.sendMessage(msg);         }         function log(msg) {             document.getElementById("log").innerHTML += msg + "<br>";         }     </script> </body> </html> 

这个 Demo 能实现:

  • JS 调 C++
  • C++ 调 JS(信号)
  • 异步通信
  • 页面加载自动初始化 WebChannel

三、QWebChannel 常见问题与避坑指南(非常重要)

1. JS 侧总是拿不到对象(undefined)

典型错误:

console.log(bridge); // undefined 

正确做法:所有 JS 调用必须在 QWebChannel 初始化之后

new QWebChannel(qt.webChannelTransport, function(channel){     window.bridge = channel.objects.bridge; }); 

⚠ 不要在 window.onload 或顶层就调用 bridge。

2. 跨线程访问导致崩溃(最常见)

若你把 Bridge 放到子线程,会直接崩溃。

原因: QWebChannel 通信必须在 UI / WebEngine 所在线程使用,否则 QObject 会被跨线程访问。

正确方案:

  • bridge 必须在主线程
  • 如果你需要跨线程,可在 Bridge 中封装信号转发:
Q_INVOKABLE void updateDataFromWorkerThread(const QString& msg) {     emit messageChanged(msg); // 仍然在主线程发 } 

Qt 会自动通过 queued connection 切回主线程。

3. 页面重新加载后 channel 失效

当你 reload()、load() 新页面后: 之前的 JS 对象全部失效。

必须在每次页面加载后重新建立 WebChannel

示例:

connect(page, &QWebEnginePage::loadFinished, this, [=](bool ok){     if(ok) {         page->setWebChannel(channel);     } }); 

⚠ Qt 5 必做,Qt 6 已自动处理但建议仍写上。

4. C++ 信号没有触发 JS 回调

常见原因:

  • Bridge 对象被销毁
  • 信号签名不匹配
  • JS 回调写在 WebChannel 初始化外部
  • 信号参数为自定义类型但未 qRegisterMetaType
排查顺序:
  • C++ 打印信号是否发出
  • JS log 是否有回调
  • 参数类型是否是 QVariant 能转的基本类型
  • Bridge 是否挂靠在 MainWindow(不要让其随 WebPage 销毁)

5. 复杂参数(对象/数组)导致 JS 不接收

支持:

  • QString
  • int/double/bool
  • QVariantList(JS Array)
  • QVariantMap(JS Object)

但不支持 C++ 自定义结构体。

解决:
QVariantMap obj; obj["name"] = "Tom"; obj["age"] = 12; emit messageChanged(obj); 

JS:

bridge.messageChanged.connect(function (obj) {     console.log(obj.name); }); 

五、总结

QWebChannel 是 Qt WebEngine 中最可靠、最强大的前后端通信方式,但需要注意:

  • Bridge 必须在主线程
  • JS 必须在初始化回调后才能使用对象
  • 参数使用 QVariant 可序列化
  • 页面刷新后必须重新 setWebChannel
  • 跨线程调用需谨慎(信号转发)

往期精彩回顾

☞QWebEngine 实战:自定义右键菜单、文件下载、Cookie 管理与 User-Agent 设置

☞ QWebEngine 常用 API 全面梳理

图片

☞ QWebEngine 系列组件全关系梳理

Read more

OpenClaw+优云智算Coding Plan:从灵感到成文,再到公众号发布的全流程AI自动化

OpenClaw+优云智算Coding Plan:从灵感到成文,再到公众号发布的全流程AI自动化

1. 背景 在自媒体运营、技术分享和日常内容创作中,许多从业者面临碎片化、低效率和重复劳动的问题。从灵感闪现到文章发布,整个过程涉及多个步骤如构思、撰写、排版及上传等,需要频繁切换工具与手动调整格式,耗时费力且容易出错。 目前市面上的AI工具大多只能解决特定环节的问题,无法覆盖整个创作流程;而专业自动化平台要么操作复杂,要么成本高昂,难以普及使用。为此,我使用OpenClaw开源AI智能体(龙虾)和优云智算Coding Plan大模型服务搭建了一个流水线。通过OpenClaw的任务管理和工具调用能力,加上优云智算提供的稳定低价算力支持,实现了“灵感输入→文案生成→内容优化→公众号发布”的端到端全流程自动化,极大提高了效率,让创作者能够更加专注于创意本身。 2. AI大模型配置 优云智算Coding Plan是聚合了OpenAI、Claude、DeepSeek、智谱GLM、MiniMax等全球主流大模型的订阅式算力服务,兼容OpenAI API协议,支持Claude Code/Codex/OpenClaw等AI工具,能完美对接OpenClaw,为内容创作提供稳定的AI生成能力,本

Langflow:这个拖拽式AI工作流神器正在颠覆传统编程

Langflow:这个拖拽式AI工作流神器正在颠覆传统编程

Langflow 是一个用于构建和部署由大语言模型(LLMs)驱动的 AI Agent 和逻辑流程(Workflows)的可视化工具。它不仅提供了类似积木搭建式的可视化界面来快速创建Agent,还提供一套完整的API系统,使每个Agent都能轻松集成进任意技术栈的应用中。你可以将它看作是 AI 世界的前端工程图板:可视化 + 可部署 + 模块配置自由。 Stars 数77,217Forks 数6,889 主要特点 * 可视化流程搭建器(Visual Builder): Langflow 提供拖拽式编辑界面,开发者可以像“搭乐高积木”一样便捷、快速地连接各类语言模型、工具组件,构建出一个Agent的完整推理或对话流程。即使是不熟悉Python开发的使用者,也能轻松上手。 * 多模型兼容与集成:支持所有主流的大语言模型(LLMs),比如OpenAI系列、Anthropic Claude、Mistral、Google Gemini等。此外,Langflow已支持向量数据库如Pinecone、Chroma、Qdrant,

AMD显卡用户专属:零门槛部署本地AI大模型完全指南

AMD显卡用户专属:零门槛部署本地AI大模型完全指南 【免费下载链接】ollama-for-amdGet up and running with Llama 3, Mistral, Gemma, and other large language models.by adding more amd gpu support. 项目地址: https://gitcode.com/gh_mirrors/ol/ollama-for-amd 还在为无法在AMD GPU上运行本地AI大模型而烦恼吗?Ollama-for-amd项目正是为你量身打造的解决方案。这个开源项目专门增强了AMD GPU支持,让你轻松在本地部署Llama 3、Mistral、Gemma等主流大语言模型,充分释放AMD显卡的AI计算潜力。 问题一:我的AMD显卡真的能跑AI模型吗? 解决方案:确认硬件兼容性 首先需要了解你的AMD显卡是否在支持列表中。根据官方文档,以下系列显卡已通过充分测试: Linux系统兼容显卡 * Radeon

论文AI率从60%降到20%以内的完整攻略:亲测有效的3步法

论文AI率从60%降到20%以内的完整攻略:亲测有效的3步法

论文AI率从60%降到20%以内的完整攻略:亲测有效的3步法 上周一个学弟在群里发了一张截图,检测报告上面的AI率是63.4%,他已经修改了两遍,依然没降下来。他当时快崩溃了,说答辩在15天后,导师要求AI率必须低于20%,感觉毫无希望。 这种情况其实很多人都经历过。AI写的论文痕迹很重,换几个词、调整几句语序,根本没用,检测系统识别的是深层语言特征,不是表面文字。今天这篇文章,把我试验过的3步攻略完整写出来,60%能不能降到20%以内,看完你自己判断。 先搞清楚:为什么AI率这么难降 很多人修改了半天还是降不下来,根本原因是搞错了方向。 AIGC检测系统(知网、万方、大雅这些)识别的不是具体的词汇,而是语言模式。AI生成的文本有几个典型特征:句式结构高度规整、逻辑过渡词密集("首先、其次、再次、最后"这套)、段落长度均匀、主动被动句比例异常……这些特征是一个整体,你改几个词根本动不了这个模式。