使用 Qt WebChannel 实现 C++/QML 和 HTML 页面之间交互
在项目开发中,常常会有在原生应用程序中嵌入 HTML 页面或者 Web 项目,并且需要应用程序与所加载的 HTML 页面的相互通信的需求。
本篇文章基于 Qt 框架,讲解如何使用 Qt WebChannel 实现 C++/QML 和 HTML 页面之间交互,包括:
- 在 HTML 页面调用 C++/QML 对象中的函数(异步)
- 向 HTML 中发送 QML/C++ 信号
- 在 HTML 中调用 QML/C++ 对象的属性
- 在 HTML 中调用 QML/C++ 对象的枚举类型和枚举值
2011 年 10 月,诺姆·罗森塔尔(Noam Rosenthal)提出了这个方法,他称之为 Qt WebChannel。他的想法既简单又强大:通过利用 Qt 的自省(introspection,也叫内省)系统,他可以在 JavaScript 客户端创建模拟对象,该对象可以反射服务端 QML/QObject 对象的 API。对于 QML 主机和 HTML/JavaScript 客户端之间的通信方式,他选择了 WebSockets,但是 WebChannel 提供的 API 是完全异步的。
Qt WebChannel 支持服务端(QML/C++ 应用程序)和客户端(HTML/JavaScript 或 QML 应用程序)之间的点对点(peer-to-peer)通信。它提供了一个 JavaScript 库,用于将 C++ 和 QML 应用程序与 HTML/JavaScript 和 QML 客户端无缝集成。客户端必须使用 JavaScript 库来访问主机端应用程序发布的序列化的 QObjects 对象。
注:本文中提到的客户端和服务端其实是在同一个应用程序内,因为 WebChannel 是基于 WebSocket 实现的,所以会有这种客户端和服务端的叫法。
QML/HTML 混合应用程序
本节演示 QML 应用程序和 HTML 之间如何进行交互。
本应用用到了 WebChannel 和 WebEngine 两个主要模块,所以要将下面这行添加到 qmake .pro 文件中:
QT += webchannel webengine
QML 服务端
在 QML 服务端,首先导入 Qt WebChannel 模块,以及 Qt WebEngine 模块:
import QtWebChannel 1.0
import QtWebEngine 1.5
然后创建一个想要发布到 HTML/JavaScript 客户端的对象:
QtObject {
id: myObject
// 注册方法 1
// 使用注册方法 2 时不需要此行代码
WebChannel.id: "foo" //这个 id 可以在 html 中使用
// 以下为 JavaScript 代码可以访问的信号、方法和属性
signal someSignal(string message);
function someMethod(message) {
console.log(message);
someSignal(message);
return "foobar";
}
property string hello: "world"
}
最后将该对象在 WebView 控件中发布到 HTML 客户端中:
WebEngineView {
anchors.fill: parent
url: "file:///C:/test.html"
webChannel: WebChannel {
id: webChannel
// 注册方法 1
registeredObjects: [myObject]
// 注册方法 2
//Component.onCompleted: {
// // "foo" 是该对象在 JavaScript 端的调用标识
// webChannel.registerObject("foo", myObject)
//}
}
}
HTML / JavaScript 客户端
在客户端,首先,通过 Qt 资源 URL 包含客户端 qwebchannel.js
库(该文件可以在 %QtDir%\Src\qtwebchannel\examples\webchannel\shared\qwebchannel.js
中找到,经过测试在 Qt 5.12 以后不将该文件加入资源也可),并在 HTML 文件中插入一段 JavaScript:
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
然后,在 JavaScript 代码中实例化 QWebChannel
对象并设置回调函数。当 web 通道的初始化成功时,将调用回调函数。此外,您可以传递 qt.webChannelTransport
对象到 channel 中,详见下文:
new QWebChannel(qt.webChannelTransport, function(channel) {
// 所有通过 WebChannel 发布的对象都可以在 channel.objects 中访问的到
window.foo = channel.objects.foo;
// 访问一个属性
alert(foo.hello);
// Writing a property will instantly update the client side cache.
// The remote end will be notified about the change asynchronously
foo.hello = "Hello World!";
// 连接信号
foo.someSignal.connect(function(message) {
alert("Got signal: " + message);
});
// 调用方法,并*异步*接收返回值
foo.someMethod("bar", function(ret) {
alert("Got return value: " + ret);
});
// One can also access enums that are marked with Q_ENUM:
//console.log(foo.MyEnum.MyEnumerator);
});
C++/QML/HTML 混合应用程序
利用在 QML 应用程序中可以引用 C++ 类的这个特点,我们也可以实现在 HTML 页面中调用 C++ 类的需求。
首先定义 C++ 对象并将它注册到 QML 中,我们在 main.cpp
中添加下面的一行,注意, TestObejct 类必须直接继承自 Object:
qmlRegisterType<TestObejct>("TestObejct", 1, 0, "TestObejct");
然后在 QML 文件中将这个类对象注册到 WebChannel,你可以使用上节中提到的方法直接调用 C++ 类中已存在的信号、方法、属性和枚举类型,也可以在 QML 中继续扩展其他方法:
import TestObejct 1.0
...
TestObejct {
id: myObject
WebChannel.id: "foo"
// 在 QML中可以继续扩展信号、方法和属性
signal someSignal2(string message);
function someMethod2(message) {
console.log(message);
someSignal2(message);
return "foobar";
}
property string hello2: "world"
}
Qt WebChannel 不只可以在 QML 应用程序中使用,在纯 Qt/C++ 应用程序中也可以创建一个 QWebChannel
并发布 QObject
实例。
1111111111111111
22222
qml与JavaScript交互 我明白了 谢谢
不明白的是 如果是C++ 与 JavaScript交互 那么是不是就是调用 注册到qml中的类中的方法
能解释一下吗?html怎么调用
你说的HTML指的什么?建议你先了解下HTML和JavaScript
怎么在html页面调用
通过 JavaScript 方法
看文章
new QWebChannel(qt.webChannelTransport, function(channel)
这段