淺析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