QWebEngine 之 C++与Js通信
Qt 嵌入式网页技术
- 在传统的Widget中嵌入支持H5 和 js 技术的控件
- 对于client中的JS本质来说,Qt WebEngine主要是提供了一个JS的宿主环境- Chromuim项目下的v8引擎。另外在Qt提供的web渲染引擎是Chromuim项目中的blink。
QWebChannel简介
The QWebChannel fills the gap between C++ applications and HTML/JavaScript applications. By publishing a QObject derived object to a QWebChannel and using the qwebchannel.js on the HTML side, one can transparently access properties and public slots and methods of the QObject. No manual message passing and serialization of data is required, property updates and signal emission on the C++ side get automatically transmitted to the potentially remotely running HTML clients. On the client side, a JavaScript object will be created for any published C++ QObject. It mirrors the C++ object’s API and thus is intuitively useable.
QWebChannel 提供了在 Server(C++应用)和 client 端(HTML/JS)之间点对点的通信能力。通过向 client 端的 QWebChannel 发布 QObject 的 派生对象,进而实现在 client 端无缝读取来自 Qt 端的 公共插槽 和 QObject 的 属性值 和 方法。在整个通信过程中,无需任何手动序列化传入的参数。所有 Qt 端的 属性 更新,signal 触发,都会 自动且异步 更新到 client 端。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9MVH8TAe-1639468062205)(./Pic/GUI_QWebChannel.png)]
- C++ 需要从后端调用前端的js函数执行某些操作,
- 前端也需用直接调用后端的C++接口,执行某些Action。
- QWebChannel就是这两者之间的桥梁
js 与 C++ 互相调用
Qt后端,需要提供一个对象,包含所谓的槽函数,然后注册该对象到QWebChannel对象,并且,需要将该QWebChannel对象注册到QWebEngineView中的QWebEnginePage对象中。Demo如下所示:
//C++对象
class WebClass : public QObject
{
Q_OBJECT
public slots:
void jscallme()
{
QMessageBox::information(NULL,"jscallme","I'm called by js!");
}
};
WebClass *webobj = new WebClass();
QWebChannel *channel = new QWebChannel(this); //QWebChannel 对象
channel->registerObject("webobj", webobj); //将webobj注册到QWebChannel对象中
view->page()->setWebChannel(channel); //WebEngineView下的page设置channel
为了调用c++槽函数,javascript需要new一个QWebChannel对象
new QWebChannel(qt.webChannelTransport,function(channel){
var webobj = channel.objects.webobj;
window.foo = webobj;
});
- QWebChannal 类在qwebchannel.js中定义,因此前端需要先load该脚本
- 传递给channel的第二个参数,用于C++端暴露的对象 channel.objects.webobj 给js端的变量 var webobj
- widnow.foo 可用于任何地方,因此可以向如下方式一样调用webobj的方法
foo.jscallme();
Then instantiate a QWebChannel object and pass it a transport object and a callback function, which will be invoked once the initialization of the channel finishes and the published objects become available.
然后实例化一个 QWebChannel 对象,并传递给它一个传输对象和一个回调函数,一旦通道初始化完成并且发布的对象可用,就会调用该函数。
示例代码中,qt.webChannelTransport 即是transport对象,而function(channel)而 initCallback 是在 QWebChannel 完成实例化且接受到来自 Qt 端的发布对象后才会被调用的回调函数。在回调函数被调用时,发布对象 一定是可用的,而且包含了所有来自 Qt 端的共享信息,如 属性,方法,可被监听的 cpp signal 等信息。
/**
* @description window.qt.webChannelTransport 可用 WebSocket 实例代替。
* 经实践发现,Qt 向全局注入的 window.qt 仅有属性 webChannelTransport,并且该对象仅有
* 两个属性方法:send 和 onmessage
* send 方法用于 js 端向 Qt 端传输 `JSON` 信息
* onmessage 用于接受 `Qt` 端发送的 `JSON` 信息
*/
new QWebChannel(window.qt.webChannelTransport, initCallback)
js 向C++传递参数
jscallme()参数为空,如下所示。 并没有入参。
void jscallme()
{
QMessageBox::information(NULL,"jscallme","I'm called by js!");
}
当js希望将前端的参数传递给后端C++时, 该如何实现
void jscallme(const QString &datafromjs)
{
QMessageBox::information(NULL,"jscallme","I'm called by js!");
m_data = datafromjs;
}
H5端的调用为
foo.jscallme(somedata);
C++接口中的const定义不能被省略, 否则容易出现如下的error:
Could not convert argument QJsonValue(string, “sd”) to target type .
当然,也可以直接设置对象的属性参数,像这样。
foo.someattribute = "somedata";
要实现上述在前端设置变量属性,在后端直接修改的方式,还需要在C++类声明内声明qt property属性
class WebClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString someattribute MEMBER m_someattribute) //使用Q_PROPERTY声明someattribute
public slots:
void jscallme()
{
QMessageBox::information(NULL,"jscallme","I'm called by js!");
}
private:
QString m_someattribute;
};
但是还是不建议这么做,最好还是通过明确的接口去改变对象的成员,否则后去维护可能会出现麻烦。
从C++端传递参数到js端
场景: 当网页打开后,用户点击业务,请求服务器端数据,C++后端访问服务器, 如何将数据返回给前端,让前端拿着该数据去刷新H5页面。
使用信号传递数据
将待传递的数据,通过信号的方式传出。Javascript必须将该信号连接至接收该信号的接口。
class WebClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString someattribute MEMBER m_someattribute)
public slots:
void jscallme()
{
QMessageBox::information(NULL,"jscallme","I'm called by js!");
}
void setsomeattribute(QString attr)
{
m_someattribute=attr;
emit someattributeChanged(m_someattribute);
}
signals:
void someattributeChanged(QString & attr); //信号传参
private:
QString m_someattribute;
};
var updateattribute=function(text)
{
$("#attrid").val(text);
}
new QWebChannel(qt.webChannelTransport,function(channel){
var webobj = channel.objects.webobj;
window.foo = webobj;
webobj.someattributeChanged.connect(updateattribute);
});
- 更直接的方法
上述方法,通过传递信号的方法来改变值。 此时html中的attrid 元素的值会变成hello.
webobj->setsomeattubute("hello");
class WebClass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString someattribute MEMBER m_someattribute NOTIFY someattributeChanged)// 增加NOTIFY
public slots:
void jscallme()
{
QMessageBox::information(NULL,"jscallme","I'm called by js!");
}
signals:
void someattributeChanged(QString & attr);
private:
QString m_someattribute;
};
当在C++端调用webobj->setProperty(“someattribute”,”hello”), someattributeChanged会自动释放,从webpage 的内容也会自动修改。相当于前后端自动同步。
C++直接调用Js方法
view->page()->runJavaScript("jsfun();",[this](const QVariant &v) { qDebug()<<v.toString();});
假设**jsfun()**已经在前端定义,返回结果会被异步地当做参数而传递至lambda表达式。
|