Qt 与 Web 通信指南(QWebEngineView + QWebChannel)
概览
- 目标:让网页 JavaScript 与 Qt 客户端进行双向通信(调用本地方法、通知 UI)。
- 技术栈:
Qt WebEngineWidgets+Qt WebChannel+ JavaScriptqwebchannel.js。 - 适用场景:在
QWebEngineView内嵌外部或内部网页,并需要与桌面端交互。
通信模型
- Qt 暴露一个
QObject(如WebBridge),注册到QWebChannel。 - Web 端通过
qwebchannel.js获得该对象(例如bridge),直接调用其slot方法。 - Qt 端通过
signals向 UI 或其他模块转发事件,或主动调用evaluateJavaScript与 Web 交互。
快速集成步骤
-
引入依赖并链接:
-
CMake:
cmakefind_package(Qt6 REQUIRED COMPONENTS WebEngineWidgets WebChannel) target_link_libraries(QuantClient PRIVATE Qt6::WebEngineWidgets Qt6::WebChannel)
-
-
实现桥对象(Qt → Web 暴露):
src/gui/web/WebBridge.h/src/gui/web/WebBridge.cpp- 暴露
slot,在需要处emit信号给 UI 层。
-
在
QWebEngineView完成加载前注册通道:webView->page()->setWebChannel(channel);
-
Web 端加载
qwebchannel.js并初始化通道,获取bridge对象进行调用。
Qt 侧代码示例
cpp
// StrategyPerformanceDialog.cpp(片段)
QWebChannel* channel = new QWebChannel(this);
WebBridge* bridge = new WebBridge(this);
channel->registerObject(QStringLiteral("bridge"), bridge);
webView->page()->setWebChannel(channel);
QObject::connect(bridge, &WebBridge::openStrategyDetailRequested, this, &StrategyPerformanceDialog::onStrategyDetailRequested);
cpp
// WebBridge.h(片段)
class WebBridge : public QObject {
Q_OBJECT
public:
explicit WebBridge(QObject *parent = nullptr);
public slots:
void refreshStrategyData();
void openStrategyDetail(const QString &strategyId);
signals:
void openStrategyDetailRequested(const QString &strategyId);
};
cpp
// WebBridge.cpp(片段)
void WebBridge::openStrategyDetail(const QString &strategyId) {
emit openStrategyDetailRequested(strategyId);
}
Web 侧代码示例
页面可改(直接在 HTML 中)
html
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script>
new QWebChannel(qt.webChannelTransport, function(channel) {
const bridge = channel.objects.bridge;
bridge.refreshStrategyData();
bridge.openStrategyDetail("strategy_123");
});
</script>
页面不可改(Qt 侧注入)
cpp
// 加载完成后动态注入脚本
connect(webView, &QWebEngineView::loadFinished, this, [webView](bool){
webView->page()->runJavaScript(R"((function(){
var s=document.createElement('script');
s.src='qrc:///qtwebchannel/qwebchannel.js';
s.onload=function(){
new QWebChannel(qt.webChannelTransport,function(c){
window.bridge=c.objects.bridge;
});
};
document.head.appendChild(s);
})();)"
);
});
预注入(更稳,文档创建阶段)
cpp
QWebEngineScript script;
script.setName(QStringLiteral("qwebchannel-loader"));
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
script.setWorldId(QWebEngineScript::MainWorld);
script.setRunsOnSubFrames(true);
script.setSourceCode(QStringLiteral(
"(function(){var s=document.createElement('script');"
"s.src='qrc:///qtwebchannel/qwebchannel.js';"
"s.onload=function(){new QWebChannel(qt.webChannelTransport,function(c){window.bridge=c.objects.bridge;});};"
"document.head.appendChild(s);})();"
));
webView->page()->scripts().insert(script);
常见问题与排错
- 链接错误:
Undefined symbols for architecture arm64→ 未将WebBridge.cpp加入构建或未链接Qt6::WebChannel。 - 头文件找不到:
'config/AppConfig.h' file not found→#include相对路径错误,从src/gui/mainwindow到src/gui/config需用../config/AppConfig.h。 qwebchannel.js加载失败:确保使用qrc:///qtwebchannel/qwebchannel.js,并已链接Qt6::WebChannel。- JS 不生效:确认已调用
webView->page()->setWebChannel(channel),且对象名与 JS 一致(如bridge)。
安全与最佳实践
- 只暴露必要的
slot,在 Qt 侧校验参数与权限。 - 对外部页面建议使用注入方式并限制可用接口。
- 使用明确的对象名与命名空间,避免冲突。
扩展能力
- Qt → Web 主动调用:
webView->page()->runJavaScript("doSomething()")。 - Web → Qt 回调返回值:
QWebEnginePage::runJavaScript支持回调函数拿到执行结果。