研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多。后来由于项目的需要,也没有再接触Qt了。现在要重新拾起来,于是要从基础学起。
Now,开始学习Qt事件处理机制。
元对象系统的构成
- QObject为所有需要利用元对象系统的对象提供一个基类。
- Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号和槽。
- Meta Object Compiler(MOC),为每个QObject派生类生成代码,以支持meta-object功能。
- QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta-object功能,编译器在扫描一个源文件时,如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成该类对应MetaObject类以及对QObject的函数override(重载)。
QObject和QMetaObject
QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特有的signal&slot信息。
virtual QObject::metaObject();
该方法返回一个QObject对应的metaObject对象,如上文所说,如果一个类的声明中包含了Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息
下面通过QMetaObject的接口来解释QMetaObject提供的信息。
1)基本信息
structQ_CORE_EXPORT QMetaObject { const char *className() const; const QMetaObject *superClass() const;
struct { // private data
const QMetaObject *superdata; //父类QMetaObject实例的指针 const char *stringdata; //一段字符串内存块,包含MetaObject信息之字符串信息 const uint *data; //一段二级制内存块,包含MetaObject信息之二进制信息 const void *extradata; //额外字段,暂未使用 } d;
...
};
2)classinfo:提供额外的类信息-名值对。用户可以在类的生命中以Q_CLASSINFO(name,value)的方式添加。
int classInfoOffset() const; int classInfoCount() const; int indexOfClassInfo(const char *name) const; QMetaClassInfo classInfo(int index) const;
example:
class MyClass : publicQObject { Q_OBJECT Q_CLASSINFO("author", "Sabrina Schweinsteiger") Q_CLASSINFO("url", "http://doc.moosesoft.co.uk/1.0/") public: ... };
3)constructor:提供该类的构造方法信息。
int constructorCount() const; int indexOfConstructor(const char *constructor) const; QMetaMethod constructor(int index) const;
4)enum:描述该类声明体重所包含的枚举类型信息。
int enumeratorOffset() const; int enumeratorCount() const; int indexOfEnumerator(const char *name) const; QMetaEnum enumerator(int index) const;
5)method:描述类中所包含方法信息:包括property,signal,slot等。
int methodOffset() const; int methodCount() const; int indexOfMethod(const char *method) const; int indexOfSignal(const char *signal) const; int indexOfSlot(const char *slot) const; QMetaMethod method(int index) const;
6)property:类型的属性信息。
int propertyOffset() const; int propertyCount() const; int indexOfProperty(const char *name) const; QMetaProperty property(int index) const;
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。
举例说明MOS(Meta Object System)
TestObject继承QObject,定义了两个Property:PropertyA和PropertyB;两个classinfo:Author,version;一个枚举:TestEnum。
#include <QObject> class TestObject : publicQObject { Q_OBJECT Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false) Q_PROPERTY(QString propertyB READ getPropertyB WRITE getPropertyB RESET resetPropertyB) Q_CLASSINFO("Author", "Long Huihu") Q_CLASSINFO("Version", "TestObjectV1.0") Q_ENUMS(TestEnum) public: enumTestEnum { EnumValueA, EnumValueB }; public: TestObject(); signals: voidclicked(); voidpressed(); publicslots: void onEventA(const QString &); void onEventB(int); }
TestObject的moc文件:
#include "TestObject.h" #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'TestObject.h' doesn't include <QObject>." #elif Q_MOC_OUTPUT_REVISION != 62 #error "This file was generated using the moc from 4.6.0. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif QT_BEGIN_MOC_NAMESPACE static const uint qt_meta_data_TestObject[] ={ //content: 4, //revision 0, //classname 2, 14, //classinfo 4, 18, //methods 2, 38, //properties 1, 44, //enums/sets 0, 0, //constructors 0, //flags 2, //signalCount //classinfo: key, value 22, 11, 44, 29, //signals: signature, parameters, type, tag, flags 53, 52, 52, 52, 0x05, 63, 52, 52, 52, 0x05, //slots: signature, parameters, type, tag, flags 73, 52, 52, 52, 0x0a, 91, 52, 52, 52, 0x0a, //properties: name, type, flags 113, 105, 0x0a095007, 123, 105, 0x0a095007, //enums: name, flags, count, data 133, 0x0, 2, 48, //enum data: key, value 142, uint(TestObject::EnumValueA), 153, uint(TestObject::EnumValueB), 0 //eod }; static const char qt_meta_stringdata_TestObject[] ={ "TestObject