使用Q_INVOKABLE來修飾成員函數,目的在於被修飾的成員函數能夠被元對象系統所喚起
Q_INVOKABLE與QMetaObject::invokeMethod均由元對象系統喚起。這一機制在Qt C++/QML混合編程,跨線程編程,Qt Service Framework 以及 Qt/ HTML5混合編程以及里廣泛使用。
一,Qt C++/QML混合編程
QML中調用C++方法借助了Qt元對象系統。考慮在QML中使用Qt C++定義的方法,如下代碼所示
mport 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 // 調用C++中定義的randomColor方法 onClicked: parent.color = parent.randomColor() } } }
為了讓上述QML代碼成功的調用下面這段代碼定義的randomColor()函數,最為關鍵的一點見randomColor方法用Q_INVOKABLE 修飾。
二,跨線程編程中的使用
我們如何調用駐足在其他線程里的QObject方法呢?Qt提供了一種非常友好而且干凈的解決方案:向事件隊列post一個事件,事件的處理將以調用我們所感興趣的方法為主(當然這需要線程有一個正在運行的事件循環)。而觸發機制的實現是由moc提供的內省方法實現的。因此,只有信號、槽以及被標記成Q_INVOKABLE的方法才能夠被其它線程所觸發調用。如果你不想通過跨線程的信號、槽這一方法來實現調用駐足在其他線程里的QObject方法。另一選擇就是將方法聲明為Q_INVOKABLE,並且在另一線程中用invokeMethod喚起。
QMetaObject::invokeMethod
靜態方法QMetaObject::invokeMethod() 的定義如下:
bool QMetaObject::invokeMethod ( QObject * obj, const char * member,Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument( 0 ), …)
invokeMethod的用法為,嘗試調用對象obj的方法member(注意member可以為信號或者是槽),如果member可以被調用,則返回真,否則返回假。QMetaObject::invokeMethod可以是異步調用,也可以是同步調用。這取決與它的連接方式Qt::ConnectionType type。如果type為Qt::DirectConnection,則為同步調用,若為Qt::QueuedConnection,則為異步調用。例如:
QMetaObject::invokeMethod(object, "methodName", Qt::QueuedConnection, Q_ARG(type1, arg1), Q_ARG(type2, arg2));
原文:https://blog.csdn.net/txgc1009/article/details/6630928
Qt反射
Qt反射機制是基於moc(meta object compiler)實現的,在這里多插一句(可以說Qt所有C++沒有的特性,幾乎都和這個有關系)。但是需要注意的是Qt提供的反射式基本的反射,不支持類的反射,這個與Java,C#還是有差別的。
moc講解
通常C++的編譯過程為
預處理->編譯->鏈接->運行
Qt編譯的過程中,有一個moc的過程,在Qt工程構建過程中的qmake其實就是干這個事的。moc->預處理->編譯->鏈接->運行。
在moc過程中,需要處理的事情如下:
1、 識別一些特殊的宏Q_OBJECT、Q_PROPERTY、Q_INVOKABLE。。。; 如果碰到這些關鍵字,Qt自然就會去生成對應的moc文件。
2、 slot,signal自然也是如此。
3、 uidesigner,同樣也是在這個階段處理的;
詳細內容
反射前期准備
1、 首先得繼承於Q_Object,同時需要在class中加入Q_OBJECT,但是Q_Object的構造函數默認是私有的不讓繼承。
在類中直接使用Q_GADGET也可以實現反射,。。。據說只能實現部分功能,目前我只實現到能遍歷成員屬性,函數,但是不能訪問其中的值。
這個過程其實就是定義QMetaObject的過程,具體見Qt源碼
2、 注冊類成員變量需要使用Q_PROPERTY
Q_PROPERTY( type member READ get WRITE set) 其中READ,WRITE是關鍵字
Type表示成員的類型(不支持自定義類型,對Qt很多基本類型都支持);
Member代表你給該成員另外起的名字,可以和變量名不同;get,set就是自己在C++函數里面定義的基本的訪問函數名,不需要寫參數。直接上代碼:
3、 注冊類成員函數
如果你希望這個函數能夠被反射,那么很簡單,只需要在類的函數聲明前加入Q_INVOKABLE關鍵字。
例如Q_INVOKABLE int func( QString flag );
#include <QObject> class MyClass : public QObject { Q_OBJECT Q_PROPERTY(int Member1 READ Member1 WRITE setMember1 ) Q_PROPERTY(int Member2 READ Member2 WRITE setMember2 ) Q_PROPERTY(QString MEMBER3 READ Member3 WRITE setMember3 ) public: explicit MyClass(QObject *parent = 0); signals: public slots: public: Q_INVOKABLE int Member1(); Q_INVOKABLE int Member2(); Q_INVOKABLE QString Member3(); Q_INVOKABLE void setMember1( int mem1 ); Q_INVOKABLE void setMember2( int mem2 ); Q_INVOKABLE void setMember3( const QString& mem3 ); Q_INVOKABLE int func( QString flag ); private: int m_member1; int m_member2; QString m_member3; };
得到注冊的類成員變量
MyClass theObj; const QMetaObject* metaObj = theObj.metaObject(); //1.遍歷類的屬性 int propertyCnt = metaObj->propertyCount(); for ( int i = 0; i < propertyCnt; ++ i ) { QMetaProperty oneProperty = metaObj->property( i ); cout << " name: " << oneProperty.name(); cout << " type: " << QVariant::typeToName( oneProperty.type()) << "\n"; }
主要思路就是得到其元對象,得到其元屬性,然后就能得到你需要的信息,具體的訪問函數有name,type,需要注意的是得到的type是枚舉值,還在Qt提供了typeToName的函數,你可以得到想要的(例如不是空洞的2,而是”int”)。
得到注冊的類成員函數
//2.遍歷類的函數成員 int methodCnt = metaObj->methodCount(); for ( int idx = 0; idx < methodCnt; ++ idx ) { QMetaMethod oneMethod = metaObj->method( idx ); cout << "--------begin-------" << "\n"; cout << " typeName: " << oneMethod.typeName() << "\n"; cout << " signature: " << oneMethod.signature() << "\n"; cout << " methodType: " << oneMethod.methodType() << "\n"; cout << "--------end---------" << "\n"; }
和遍歷類屬性一致,其實就是根據元對象,得到元函數;
其中typeName代表返回類型,signature只的是函數的原貌,methodType代表函數的類型,在Qt中分為三類(槽,信號,普通函數)。
訪問類成員屬性(get,set)
//3.使用反射 cout << "-------test property-----------" << "\n"; MyClass newObj; newObj.setProperty("Member1", 66); cout << newObj.property( "Member1" ).toString().toStdString() << "\n"; cout << newObj.Member1() << "\n"; cout << "--------end----------" << "\n";
在這里使用的是QObject的property() 和setProperty方法,來訪問成員信息。但是對於使用Q_GADGET宏的類,是不能使用這個方法的,還在尋找解決方法,基本思路當然是重寫。
調用注冊的函數
int ret; MyClass newObj; newObj.setMember1( 20 ); newObj.setMember2( 50 ); QMetaObject::invokeMethod( &newObj, "func", Qt::DirectConnection, Q_RETURN_ARG(int, ret ), Q_ARG(QString, "+"));
//普通函數的調用
在MyClass中,我們定義了int func( QString flag );這個函數,利用反射的調用方式如上,主要是理解invokeMethod的用法,其中Qt::DirectConnection是函數的執行方式,分為(異步和同步),Q_RETURN_ARG是返回參數,Q_ARG是傳入參數,需要按函數聲明中參數的順序依次傳入,Qt最多支持9個參數,對於一般的應用沒有問題。還有疑問,請移步具見Qt強大的幫助文檔。
反射的應用
反射反射,就我目前的認知水平來看,通過使用字符串,來實現函數的通用化調用,例如你可以利用反射把很多函數放置到數組中,實現一次遍歷,全部調用。
目前我見到的大多是利用反射來操作數據庫,例如hibernate,其實可以利用Qt的反射,快速實現所謂的hibernate,(最近自己獨立實現了一套,很方便)。
