請尊重原創(chuàng)作品和譯文。轉(zhuǎn)載請保持文章完整性,并以超鏈接形式注明原始作者地址http://blog.csdn.net/changsheng230,方便其他朋友提問和指正。
在Qt/Qt Quick宏淺議一文中,我們將介紹Qt中經(jīng)常使用的幾個宏: Q_OBJECT, SIGNAL與SLOT, Q_SIGNALS 與 Q_SLOTS, Q_EMIT ,Q_INVOKABLE, Q_PROPERTY。相比其他宏,Q_INVOKABLE 顯得更加神秘,但Q_INVOKABLE的理解與使用變得越來越重要。本文將圍繞Q_INVOKABLE以及相對應(yīng)的invokeMethod展開討論。
Q_INVOKABLE
#define Q_INVOKABLE
重新回顧一下Q_INVOKABLE的定義,它在$QTDIR/src/corelib/kernel/qobjectdefs.h 中,簡單被define,目的在于讓moc識別。
使用Q_INVOKABLE來修飾成員函數(shù),目的在于被修飾的成員函數(shù)能夠被元對象系統(tǒng)所喚起。
QMetaObject::invokeMethod
靜態(tài)方法QMetaObject::invokeMethod() 的定義如下:
- bool QMetaObject::invokeMethod ( QObject * obj, const char * member,Qt::ConnectionType type,
- QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument( 0 ), …)
invokeMethod的用法為,嘗試調(diào)用對象obj的方法member(注意member可以為信號或者是槽),如何member可以被調(diào)用,則返回真,否則返回假。QMetaObject::invokeMethod可以是異步調(diào)用,也可以是同步調(diào)用。這取決與它的連接方式Qt::ConnectionType type。如果type為Qt::DirectConnection,則為同步調(diào)用,若為Qt::QueuedConnection,則為異步調(diào)用。例如:
- QMetaObject::invokeMethod(object, "methodName",
- Qt::QueuedConnection,
- Q_ARG(type1, arg1),
- Q_ARG(type2, arg2));
上述調(diào)用為異步調(diào)用。請注意,因為上面所示的參數(shù)需要被在構(gòu)建事件時進行硬拷貝,參數(shù)的自定義型別所對應(yīng)的類需要提供一個共有的構(gòu)造函數(shù)、析構(gòu)函數(shù)以及拷貝構(gòu)造函數(shù)。而且必須使用注冊Qt型別系統(tǒng)所提供的qRegisterMetaType() 方法來注冊這一自定義型別。
Q_INVOKABLE與QMetaObject::invokeMethod均由元對象系統(tǒng)喚起。這一機制在Qt C++/QML混合編程,跨線程編程,Qt Service Framework 以及 Qt/ HTML5混合編程以及里廣泛使用。
Qt C++/QML混合編程
QML中調(diào)用C++方法借助了Qt元對象系統(tǒng)。考慮在QML中使用Qt C++定義的方法,如下代碼所示:
- import Qt 4.7
- import Shapes 5.0
- Item {
- width: 300; height: 200
- Ellipse {
- x: 50; y: 35; width: 200; height: 100
- color: "blue"
- MouseArea {
- anchors.fill: parent
-
- onClicked: parent.color = parent.randomColor()
- }
- }
- }
為了讓上述QML代碼成功的調(diào)用下面這段代碼定義的randomColor()函數(shù),最為關(guān)鍵的一點見randomColor方法用Q_INVOKABLE 修飾。
- #include <QDeclarativeItem >
- class EllipseItem : public QDeclarativeItem
- {
- Q_OBJECT
- public:
- Q_INVOKABLE QColor randomColor() const;
- …
- }
更多細節(jié),請參看我的另一篇博文:QML與C++混合編程使用
在跨線程編程中的使用
我們?nèi)绾握{(diào)用駐足在其他線程里的QObject方法呢?Qt提供了一種非常友好而且干凈的解決方案:向事件隊列post一個事件,事件的處理將以調(diào)用我們所感興趣的方法為主(當(dāng)然這需要線程有一個正在運行的事件循環(huán))。而觸發(fā)機制的實現(xiàn)是由moc提供的內(nèi)省方法實現(xiàn)的。因此,只有信號、槽以及被標(biāo)記成Q_INVOKABLE的方法才能夠被其它線程所觸發(fā)調(diào)用。如果你不想通過跨線程的信號、槽這一方法來實現(xiàn)調(diào)用駐足在其他線程里的QObject方法。另一選擇就是將方法聲明為Q_INVOKABLE,并且在另一線程中用invokeMethod喚起。
更多細節(jié),譯文事件循環(huán)與線程
Qt Service Framework
Qt服務(wù)框架是Qt Mobility 1.0.2版本推出的,一個服務(wù)(service)是一個獨立的組件提供給客戶端(client)定義好的操作。客戶端可以通過服務(wù)的名稱,版本號和服務(wù)的對象提供的接口來查找服務(wù)。 查找到服務(wù)后,框架啟動服務(wù)并返回一個指針。
服務(wù)通過插件(plug-ins)來實現(xiàn)。為了避免客戶端依賴某個具體的庫,服務(wù)必須繼承自QObject。這樣QMetaObject 系統(tǒng)可以用來提供動態(tài)發(fā)現(xiàn)和喚醒服務(wù)的能力。要使QmetaObject機制充分的工作,服務(wù)必須滿足,其所有的方法都是通過 signal,slot,property 或invokable method和Q_INVOKEBLE來實現(xiàn)
其中,最常見的與servicer交互的方法如下:
- QServiceManager manager;QObject *storage ;
- storage = manager.loadInterface("com.nokia.qt.examples.FileStorage"); if (storage) QMetaObject::invokeMethod(storage, "deleteFile", Q_ARG(QString, "/tmp/readme.txt"));
上面的代碼通過service的元對象提供的invokeMethod方法,調(diào)用文件存儲對象的deleteFile() 方法。客戶端不需要知道對象的類型,因此也沒有鏈接到具體的service庫。 當(dāng)然在服務(wù)端的deleteFile方法,一定要被標(biāo)記為Q_INVOKEBLE,才能夠被元對象系統(tǒng)識別
Qt服務(wù)框架的一個亮點是它支持跨進程通信,服務(wù)可以接受遠程進程。在服務(wù)管理器上注冊后 進程通過signal,slot,invokable method和property來通信,就像本地對象一樣。服務(wù)可以設(shè)定為在客戶端間共享,或針對一個客戶端。 請注意,在Qt服務(wù)框架推出之前,信號、槽以及invokable method僅支持跨線程。 下圖是跨進成的服務(wù)/客戶段通信示意圖(圖片來自諾基亞論壇)。這里我們可以清楚的看到,invokable method和Q_INVOKEBLE 是跨進城、跨線程對象之間通信的重要利器。

有關(guān)Qt Service Framework的更多討論和用例,請參見Qt Service Framework文檔