您的位置:首页 > 编程语言 > Qt开发

Qt的动态信号与槽机制

2011-06-18 15:31 309 查看
想整理一下QAxObject动态信号和槽的实现,似乎太困难了,有些无从下手,先随便写写,以后看懂了再继续

注:Qt5 staging仓库已经引入一种全新的信号与槽的语法:信号可以和普通的函数、类的普通成员函数、lambda函数连接(而不再局限于信号函数和槽函数),详见 信号与槽的新语法(Qt5)

meta object

网上关于元对象的解释已经很多了,所以我们简单提一下就可以了:

1. 派生自QObject的类可以添加一个 Q_OBJECT 宏

#define Q_OBJECT /
public: /
Q_OBJECT_CHECK /
static const QMetaObject staticMetaObject; /
virtual const QMetaObject *metaObject() const; /
virtual void *qt_metacast(const char *); /
QT_TR_FUNCTIONS /
virtual int qt_metacall(QMetaObject::Call, int, void **); /
private:


2. 运行 moc 对包含该类的文件进行预处理

moc xxx.h -o moc_xxx.cpp

3. 生成的该文件包含我们元对象的所有信息,为了清楚起见,看一下QMetaObject定义的数据成员部分:

struct QMetaObject
{
...
...
private:
struct { // private data
const QMetaObject *superdata;
const char *stringdata;
const uint *data;
const void *extradata;
} d;
}


4. 你可以随便找一个moc_xxx.cpp文件,会发现里面一个字符数组和整数数组。对应这儿的stringdata和data

信号与槽

正常情况下,moc 生成的文件中包含信号与槽的信息,而每一个信号或槽

1、有一个对应的字符串(对应函数原型)

2、有一个索引

3、有一个函数定义体(信号和槽都是一个普通的函数)

如何工作?

信号和槽以字符串形式传递给connect

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection )

connect 从元对象系统查找信号与槽,找到的话,调用QMetaObject::connect建立二者索引之间的联系(这是私有函数,我们可以看到这儿涉及到的只是索引值)

bool QMetaObject::connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
int type = 0, int *types = 0);

信号的发射:信号是一个普通的函数,该函数将调用所有与之关联的槽函数或信号函数等

void QMetaObject::activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);

如何被调用:前面说connect保存了索引值,而根据索引找到函数并调用是通过 qt_metacall 完成的

动态信号与槽

仅仅静态生成信号与槽有些时候是不够用的,比如QAxObject和QAxidget信号与槽都是动态生成的,再就是QtScript模块,script定义的信号与槽,也只能是动态的。

Qt Quarterly 中有一篇文章专门介绍动态的信号与槽的实现 Dynamic Signals and Slots

1. 信号与槽的连接:文中使用了connectDynamicSlot 和 connectDynamicSignal ,而不是我们熟悉的 connect

2. 信号与槽的实现:静态情况下,它们都是普通的函数,然后qt_metacall 根据索引值就可以调用它们;动态情况下,信号和槽是个数可变的,qt_metacall 如何将索引值和信号槽对应起来?信号和槽的实现如何存储?

Q_OBJECT

动态信号槽,我们不能直接包含这个宏,但是这个宏展开后的函数我们还是需要的,由于不能用moc,我们只能自己来实现这个些函数了。最重要的应该是这两个函数

virtual const QMetaObject *metaObject() const;
virtual int qt_metacall(QMetaObject::Call, int, void **);

Dynamic Signals and Slots 一文中,由于引入了自己的connect方法,所以连 metaObject() 这个也不需要的。只剩下一个:

int DynamicQObject::qt_metacall(QMetaObject::Call call, int id, void **arguments)
{
id = QObject::qt_metacall(call, id, arguments);
if (id == -1 || call != QMetaObject::InvokeMetaMethod)
return id;

Q_ASSERT(id < slotList.size());
slotList[id]->call(arguments);
return -1;
}


QAxObject

QAxObject 和 QAxWidget 也都是实现的动态的信号和槽。如果我们看Manual的话,会注意到这样一个警告:

Warning : You can subclass QAxObject, but you cannot use the Q_OBJECT macro in the subclass (the generated moc-file will not compile), so you cannot add further signals, slots or properties. This limitation is due to the metaobject information generated in runtime .

这个概念上似乎很简单:从COM组件的类型库中提取出enum、Interface 和 Event 等信息,然后封装成Qt的enum、信号、槽、属性,生成并填充metaobject元对象。

QAxObject 实实在在地生成了一个QMetaObject的对象,这是和 Dynamic Signals and Slots 一文中涉及到的方法的最大的不同。

假定已经将信号或槽的信息提取并保存到一个结构体中:

struct Method {
Method() : flags(0)
{}
QByteArray type;
QByteArray parameters;
int flags;
QByteArray realPrototype;
};

然后,我们将动态的信号与槽保存到QMap中

QMap<QByteArray, Method> signal_list;
QMap<QByteArray, Method> slot_list;

再然后我们就可以创建一个QMetaObject对象了(非常繁琐无味的一个东西)

static QMetaObject * GenerateMetaObject(const QMetaObject *parentObject)
{

QMetaObject *metaobj = new QMetaObject;

// revision + classname + table + zero terminator
uint int_data_size = 1+1+2+2+2+2+1;

int_data_size += signal_list.count() * 5;
int_data_size += slot_list.count() * 5;

uint *int_data = new uint[int_data_size];
int_data[0] = 1; // revision number
int_data[1] = 0; // classname index
int_data[2] = 0; // num_classinfo
int_data[3] = 0; // idx_classinfo
int_data[4] = signal_list.count() + slot_list.count(); // num_methods
int_data[5] = 10; // idx_signals
int_data[6] = 0; // num_properties
int_data[7] = 0; // idx_properties
int_data[8] = 0; // num_enums
int_data[9] = 0; // idx_enums
int_data[int_data_size - 1] = 0; // eod;

char null('/0');
// data + zero-terminator
QByteArray stringdata("DynamicObject");
stringdata += null;
stringdata.reserve(8192);

uint offset = int_data[5]; //idx_signals

// each signal in form prototype/0parameters/0type/0tag/0
for (QMap<QByteArray, Method>::ConstIterator it = signal_list.begin(); it != signal_list.end(); ++it) {
QByteArray prototype(QMetaObject::normalizedSignature(it.key()));
QByteArray type(it.value().type);
QByteArray parameters(it.value().parameters);
QByteArray tag;
int flags = it.value().flags;

int_data[offset++] = stringdata.length();
stringdata += prototype;
stringdata += null;
int_data[offset++] = stringdata.length();
stringdata += parameters;
stringdata += null;
int_data[offset++] = stringdata.length();
stringdata += type;
stringdata += null;
int_data[offset++] = stringdata.length();
stringdata += tag;
stringdata += null;
int_data[offset++] = flags;
}

// each slot in form prototype/0parameters/0type/0tag/0
for (QMap<QByteArray, Method>::ConstIterator it = slot_list.begin(); it != slot_list.end(); ++it) {
QByteArray prototype(QMetaObject::normalizedSignature(it.key()));
QByteArray type(it.value().type);
QByteArray parameters(it.value().parameters);
QByteArray tag;
int flags = it.value().flags;

int_data[offset++] = stringdata.length();
stringdata += prototype;
stringdata += null;
int_data[offset++] = stringdata.length();
stringdata += parameters;
stringdata += null;
int_data[offset++] = stringdata.length();
stringdata += type;
stringdata += null;
int_data[offset++] = stringdata.length();
stringdata += tag;
stringdata += null;
int_data[offset++] = flags;
}

char *string_data = new char[stringdata.length()];
memset(string_data, 0, sizeof(string_data));
memcpy(string_data, stringdata, stringdata.length());

// put the metaobject together
metaobj->d.data = int_data;
metaobj->d.extradata = 0;
metaobj->d.stringdata = string_data;
metaobj->d.superdata = parentObject;

return metaobj;
}


只有动态生成了QMetaObject对象,QObject::connect 才能用到它上面。上面这个东西 revision number 采用的1(Qt 4.8 moc生成的文件中已经是6了),且仅仅填充了信号与槽,但看起来还是相当困难。

转载声明: 本文转自 http://blog.csdn.net/dbzhang800/archive/2011/06/12/6540089.aspx

参考推荐:

Dynamic Signals and Slots

QT 的信号与槽机制介绍
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: