浅析Qt(C++),QML与HTML之间的交互
来源 https://zhuanlan.zhihu.com/p/62987738
写在前面
本文适合有一定Qt及HTML经验的人阅读。
Qt(C++)和QML间交互
想要了解Qt(C++)和QML间的信息交互,就不得不提到Qt的信号与槽机制。
信号与槽
信号与槽是qt的特有信息传输机制。它本质上是一种观察者模式。当某个事件触发时,它就会发出一个类似广播的信号。如果有对象对这个信号感兴趣,它就使用连接函数,将想要处理的信号和自己的一个函数(qt中成为槽)绑定来进行处理。当信号发出时,槽函数就会自动被执行。
我们通过一个例子来进行说明。
类的定义
首先,我们定义一个c++的类,该类需要继承QObject类,这样才有信号槽的能力。同时,需要在该类中添加Q_OBJECT宏。例:
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
};
使用Q_PROPERTY定义属性,该属性可被qml使用。它还具有一些附加特性:READ用于读属性的值;WRITE用于设置属性的值;NOTIFY则定义一个信号,该信号用来表示属性发生改变。信号会携带一个参数,表示属性的新值。l例如:
Q_PROPERTY(QString mystring READ getString WRITE setString NOTIFY mystringChanged)
绑定槽函数
QT使用connect函数来绑定信号和槽,例:
connect(this, SIGNAL(mystringChanged(QString)), this, SLOT(onMystringChanged(QString)));
上面代码中,当有mystringChanged信号发出时,onMystringChanged函数就是被执行。信号与槽机制不仅可以用来进行c++类之间的通信,也可以用来实现c++和qml之间的通信,我们继续使用上面的例子来说明。
信号发送
当mystring有变化时,会触发setString回调。我们可以在setString函数中触发mystringChanged信号。
void MyClass::setString(QString string){
emit mystringChanged(string);//发送信号
}
将类注册到QML中
QT使用qmlRegisterType方法将类注册到QML中,例:
qmlRegisterType<MyClass>("RegisterMyType",1,0,"MyClassType");
其中,第一个参数是作为QML中引用的url;第二个参数是主版本号;第三个参数是次版本号;第四个参数是QML中使用的元素名称。本例中,QML模块可以使用下面方法引用该类,例:
import RegisterMyType 1.0
QML中对象创建
经过上面的步骤之后,我们就可以直接在QML中创建MyClassType对象了。例:
MyClassType {
id: myobj
}
QML中连接信号
对象创建成功后,我们可以为QML绑定感兴趣的信号了。
Connections {
target: myobj;
onMystringChanged: {
// 这里的value是signal信号函数里面的参数
console.log("value: " + value)
}
}
QML直接使用对象
除了上面的方法,我们还可以通过直接使用对象的方式,来进行信号的绑定。在上面的例子中,我们可以下面的方式,我们首先在C++代码中做如下声明:
QQmlApplicationEngine *qmlEngine = new QQmlApplicationEngine;
QQmlComponent component(qmlEngine, QUrl("qrc:/MyClass.qml"));
MyClass *myClass = qobject_cast<MyClass *>(component.create());
qmlEngine->rootContext()->setContextProperty("myQmlClass", myClass);
其中,QQmlComponent用来封装QML组件。需要注意的是,MyClass.qml中,需要使用上面讲到的MyClassType作为顶层元素。 setContextProperty函数定义暴露给QML的对象。第一个参数是QML中使用的对象名称,相当于重命名,可在QML中直接使用;第二个参数暴露给QML的对象。而信号的绑定,只需要将上面讲到的Connections中的target修改为myQmlClass即可。即:
Connections {
target: myQmlClass;
onMystringChanged: {
// 这里的value是signal信号函数里面的参数
console.log("value: " + value)
}
}
Qt和HTML间交互
Qt和HTML间的交互式通过WebChannel来实现的。
WebChannel
WebChannel提供一种机制使得QObject或者QML访问HTML。所有的属性,信号和公共的槽函数都可以被HTML使用。
WebChannel由一些属性和方法组成。
属性包括:
registeredObjects
A list of objects which should be accessible to remote clients.
The objects must have the attached id property set to an identifier, under which the object is then known on the HTML side.
Once registered, all signals and property changes are automatically propagated to the clients. Public invokable methods, including slots, are also accessible to the clients.
If one needs to register objects which are not available when the component is created, use the imperative registerObjects method.
简单翻译一下:
这是一个提供给HTML访问的object列表。
object需要有id标识,这样才能被HTML识别。
object一旦被注册,所有的信号和属性的改变都会被自动传递到客户端。还包括公共的方法和槽。
如果组件创建时object还不可用,可以使用registerObject方法。
transports
A list of transport objects, which implementQWebChannelAbstractTransport. The transports are used to talk to the remote clients.
一个传输列表,实现了QWebChannelAbstractTransport类。用来跟客户端交互。
其它方法具体不再赘述,可以参考后面的参考文献。这里我们主要讲一下registeredObjects的用法。
registeredObjects提供给HTML访问的object列表。object须声明id属性,这样HTML才能知道它;同时,object要声明WebChannel.id,HTML使用该id访问对象。
QtObject定义如下:
QtObject {
id: myObject
WebChannel.id: "myWebObject"
property string name: "QtObjectName"
signal onMystringChanged(var myStr)
}
WebChannel定义如下:
WebChannel {
id: myChannel
registeredObjects: [myObject]
}
WebEngineView
WebChannel声明好之后,下面就是如何使用它。我们定义WebEngineView元素,用来加载HTML文件,并指定关联的WebChannel,使用方式如下:
WebEngineView {
id: webView
url: "./map.html"
webChannel: myChannel
}
至此,QML端的工作就已经完成了。下面讲一下如何在HTML端使用WebChannel。
引入qwebchannel.js库
HTML想要使用QWebChannel,需要先引用qwebchannel库,这是一个JavaScript类库,使用方式如下:
<script type="text/javascript" src="qwebchannel.js"></script>
然后在增加如下代码:
new QWebChannel(qt.webChannelTransport, function(channel) {
var myClass = channel.objects.myClass;
var myObject = channel.objects.myWebObject;
myObject.onMystringChanged.connect(function(myStr) {
console.log(myStr);
});
});
总结
信号与槽机制是Qt的核心思想,我们需要加深理解,在实际应用中灵活使用。
这里只讲了C++和QML,QML和HTML间的交互。C++和HTML间也可以直接交互,以后有时间再来跟大家一起分享。
==================== End