请尊重原创作品和译文。转载请保持文章完整性,并以超链接形式注明原始作者地址http://blog.csdn.net/changsheng230,方便其他朋友提问和指正。
刚开始接触Qt的朋友可能对Qt在使用当中需要声明的各色各样的宏感到神秘而又陌生,本文将介绍Qt中经常使用的几个宏: Q_OBJECT, SIGNAL与SLOT, Q_SIGNALS 与 Q_SLOTS, Q_EMIT ,Q_INVOKABLE, Q_PROPERTY:
宏的头文件出处: $QTDIR/src/corelib/kernel/qobjectdefs.h
#define Q_OBJECT /
public: /
Q_OBJECT_CHECK /
static const QMetaObject staticMetaObject; /
Q_OBJECT_GETSTATICMETAOBJECT /
virtual const QMetaObject *metaObject() const; /
virtual void *qt_metacast(const char *); /
QT_TR_FUNCTIONS /
virtual int qt_metacall(QMetaObject::Call, int, void **); /
宏Q_OBJECT是Qt所有宏中最为重要的一个,Q_OBJECT是使用信号槽机制以及其他所有元对象系统提供的服务(内省、invokeMethod,元对象property系统等等)的前提条件。有关Q_OBJECT的讨论请参考Qt源码分析之QObject。
这两个宏是调用connect方法时用到:
QObject::connect(myButton, SIGNAL(clicked()), label, SLOT(showText()));
那么宏SIGNAL和SLOT为我们做了那些事情呢,看一下源代码:
$QTDIR/src/corelib/kernel/qobjectdefs.h # define SLOT(a) qFlagLocation("1"#a QLOCATION) # define SIGNAL(a) qFlagLocation("2"#a QLOCATION) $QTDIR/src/corelib/kernel/qobject.cpp const char *qFlagLocation(const char *method) { static int idx = 0; flagged_locations[idx] = method; idx = (idx+1) % flagged_locations_count; return method; }
原来它会基于把我们定义的信号、槽的名称返回一个字符串,比如SIGNAL(clicked()) 返回字符串 “2clicked()”, SLOT(showText())返回字符串“1showText()”
Q_SIGNALS 与 Q_SLOTS # define slots # define signals protected # define Q_SLOTS # define Q_SIGNALS protected
Q_SIGNALS 与 Q_SLOTS是Qt 4.1引入的,它们用来替换关键字signals和slots,原因是更好的与第三方信号槽机制兼容,比如boost库。尽管Q_SIGNALS 与 Q_SLOTS看起来没有做什么。其实不然,QT的元对象编译器moc会识别声明在头文件中的宏Q_SIGNALS,Q_SLOTS。并做为依据,生成元对象模型数据,详见文中最后所示代码实例
#define Q_EMIT #define emit
Q_EMIT用来替换关键字emit,原因也是更好的与第三方信号槽机制兼容,比如boost库。
这里要注意,我们看到Q_EMIT看起来同样的简单, 但它们是有区别的!表面的区别在于Q_SIGNALS 与 Q_SLOTS用在头文件中,而Q_EMIT用在代码视线中。 本质的区别的在于,Q_SIGNALS 与 Q_SLOTS将被moc识别,是必须使用的。而Q_EMIT或者emit是可有可无的。它不会被moc识别,它存在的唯一理由是:增加代码的可读性。 也就是说如下代码都能正常工作,但2)的写法也许会惹怒你的同事。
void method() { 1) emit signalA(); 2) signalA(); }
#define Q_INVOKABLE
使用Q_INVOKABLE来修饰成员函数,目的在于被修饰的成员函数能够被元对象系统所唤起。这一机制在Qt C++/QML混合编程,Qt service framework, 以及Qt/ HTML5混合编程里广泛使用。我会随后另撰写一文做深入探讨。
#define Q_PROPERTY(text)
使用Q_PROPERTY用以声明属性,属性类似于成员变量,但它能够被元对象系统所访问。QML的属性便是利用该机制得以实现的。 Q_PROPERTY的用法如下:
Q_PROPERTY(QString title READ title WRITE setTitle USER true)
接下来,让我们结合代码来看一下上述宏的使用以及元对象编译器是如何利用这些宏的。
#include <QDeclarativeItem > class EllipseItem : public QDeclarativeItem { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) public: EllipseItem(QDeclarativeItem *parent = 0); void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); const QColor &color() const; void setColor(const QColor &newColor); Q_INVOKABLE QColor randomColor() const; public Q_SLOTS: void try1(); void try2() {} Q_SIGNALS: void colorChanged(); void ready(); private: QColor m_color; };
以下代码由元对象编译器moc根据上述头文件自动生成:
static const uint qt_meta_data_EllipseItem[] = { // content: 5, // revision 0, // classname 0, 0, // classinfo 5, 14, // methods 1, 39, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 2, // signalCount // signals: signature, parameters, type, tag, flags 13, 12, 12, 12, 0x05, 28, 12, 12, 12, 0x05, // slots: signature, parameters, type, tag, flags 36, 12, 12, 12, 0x0a, 43, 12, 12, 12, 0x0a, // methods: signature, parameters, type, tag, flags 57, 12, 50, 12, 0x02, // properties: name, type, flags 71, 50, 0x43495103, // properties: notify_signal_id 0, 0 // eod }; static const char qt_meta_stringdata_EllipseItem[] = { "EllipseItem/0/0colorChanged()/0ready()/0" "try1()/0try2()/0QColor/0randomColor()/0" "color/0" };
从上面代码实例我们可以看到, QT的元对象编译器moc会识别声明在头文件中的宏Q_SIGNALS,Q_SLOTS, Q_PROPERTY, Q_PROPERTY。并以此做为依据,生成了元对象数据表。在这张元对象数据表中,我们已可以看到,moc根据头文件所声明的宏定义,识别出: