Qt的Signal和Slot机制(一)

最近在用Qt开发项目,它的Signal和Slot机制引起了我的兴趣,闲暇无聊,看了下源代码,写下了一些自己的心得。但其中难免有错误之处,望各位看官不吝指出。

 

第一节 Signal和Slot的钥匙

 

我们知道Qt 通过”connect” 函数,将一个Signal 和Slot 对应了起来。为了形成对应,必有一结构来维护和保存这个对应关系。这个结构就是我们的幕后英雄 QMetaObject 。一般我们只会在由Qt 自动生成的moc 打头的cpp 文件里看到。

它主要有以下三个变量,

const QMetaObject *superdata ;

const char *stringdata ;

const uint *data ;

 

superdata ,父类的QMetaObject 指针,当我们在本类找不到相应的Signal 和Slot 时,可以去父类中找。

换句话说,父类的Signal 和Slot ,子类一样可以使用。

 

stringdata ,一个字符串,保存了类名,Signal ,Slot 函数名,和他们的参数名字

 

data ,一个数组,从这个数组中包含了“QMetaObjectPrivate “结构的信息,Signal 信息,Slot 信息还有其他的诸如properties的信息,Signal 信息和Slot 信息保存了Signal 和Slot 函数名字的起始位置,结合上面的stringdata ,我们可以获得函数名,同时从保存信息的位置,确定了Signal 和Slot 的索引值

 

我们来看以下的一个例子,

class QTestA : public QObject

{

       Q_OBJECT

 

public:

       QTestA (QObject *parent );

       ~QTestA ();

 

 

signals:

       void SignalA1 ();

       void SignalA2 (int i );

 

public slots:

 

       void SlotA1 ();

       void SlotA2 (char *szBuf ,int nSize );

 

private:

      

};

 

这是一个继承QObject 的类,它有两个Singal 和两个Slot 。

以下是它的QMetaObject 内容,取自由Qt 工程自动生成的moc 打头的cpp 文件。

 

static const uint qt_meta_data_QTestA [] = {

  // content:

       2,       // revision

       0,       // classname

       0,    0, // classinfo

       4,   12, // methods

       0,    0, // properties

       0,    0, // enums/sets

       0,     0, // constructors

/ 以上部分 是QMetaObjectPrivate 结构信息

  // signals: signature, parameters, type, tag, flags

       8,    7,    7,    7, 0x05,

      21,   19,    7,    7, 0x05,

 

  // slots: signature, parameters, type, tag, flags

      35,    7,    7,    7, 0x0a,

      56,   44,    7,    7, 0x0a,

 

       0        // eod

};

它的data 成员变量内容,索引号0 到11 是 “QMetaObjectPrivate “结构的信息。

“QMetaObjectPrivate “结构如下:

struct QMetaObjectPrivate

{

    int revision ;

    int className ;

    int classInfoCount , classInfoData ;

    int methodCount , methodData ;

    int propertyCount , propertyData ;

    int enumeratorCount , enumeratorData ;

    int constructorCount , constructorData ;

};

 

 

我们可以看到 qt_meta_data_QTestA [4] 也就是QMetaObjectPrivate 结构中的methodCount 变量 的值为4 ,说明有四个方法(本例中2 个Signal 和,2 个Slot 加起来正好是4 )。又可以看到qt_meta_data_QTestA [5] 也就是QMetaObjectPrivate 结构中的methodData 变量 的值为12 ,说明Method 的信息从qt_meta_data_QTestA 的第12 个数组元素开始(即qt_meta_data_QTestA[12]),正好是 “ // signals: signature, parameters, type, tag, flags ”

它下面的两行就是我们所设定的两个Signal 函数的信息。

而在“ // slots: signature, parameters, type, tag, flags ”的下面是两个Slot 函数的信息。

他们的信息都是5 个一组

static const char qt_meta_stringdata_QTestA [] = {

    "QTestA/0/0SignalA1()/0i/0SignalA2(int)/0"

    "SlotA1()/0szBuf,nSize/0SlotA2(char*,int)/0"

};

这是他的stringdata 变量的内容,里面包含了类名( QTestA ),第一个Signal 函数名( SignalA1() ),

,第二个Singal 函数参数名(i ),第二个Signal 函数名( SignalA2(int) ), 第一个Slot 函数名( SlotA1() )

,第二个Slot 函数参数名( Buf,nSize ),第二个Slot 函数名( SlotA2(char*,int) )。

 

每一部分都用/0 结束,这样便于字符串操作,只要加上 偏移,就能得到该字符串,而不用管有多长。因为有关字符串的操作,都会碰到/0 后自动终止。

 

结合 data 成员变量内容,来自于data 成员变量内容第一条Signal 函数信息” 8,    7,    7,    7, 0x05”

其中8 ,表示第一个Signal 函数的名字的在“ qt_meta_stringdata_QTestA ”字符串的偏移位置是8 (注意/0 算一个字符,别数错了:p ),所以,指向它的名字的指针位置就是 qt_meta_stringdata_QTestA+8 。如果我们要拷贝它的名字,就非常简单。

出来的值就是  SignalA1() 

比如以下代码,

char szSignal[256] = {0};

strcpy(szSignal, qt_meta_stringdata_QTestA+8);

看这时候,用/0 的分割的好处就出来了,我们只需要知道它从什么位置开始,不需要知道在什么位置结束。因为strrcpy 碰到/0 就不会再拷贝了。

 

第二条Signal 函数的信息是“ 21,   19,    7,    7, 0x05

21 表示,第二个Signal 函数名字的偏移位置是21 ,所以他的值是 SignalA2(int)

 

同理,第一条Slot 函数的信息是” 35,    7,    7,    7, 0x0a ”

所以第一个Slot 函数的偏移量是35, 得出的值是 SlotA1()

第二条Slot 函数的信息是“56,   44,    7,    7, 0x0a ”,得出的值是 SlotA2(char*,int) 

也许有看管会问,如何知道一条信息是slot 还是signal 呢,看最后一个参数貌似0x05 是表示的Signal ,

0x0a 表示的是Slot ,但我也看到过其他值。非常奇怪。

 

同时这几条信息的排列顺序就是,函数的索引值

// signals: signature, parameters, type, tag, flags

       8,    7,    7,    7, 0x05, // 表示SignalA1 函数的信息,它的索引值0

      21,   19,    7,    7, 0x05,// 表示SignalA2 函数的信息,它的索引值1

 

  // slots: signature, parameters, type, tag, flags

      35,    7,    7,    7, 0x0a,// 表示SlotA1 函数的信息,它的索引值2

      56,   44,    7,    7, 0x0a,// 表示SlotA2 函数的信息,它的索引值3

 

索引值将会在这个函数使用

int QTestA ::qt_metacall (QMetaObject ::Call _c , int _id , void **_a )

{

    _id = QObject ::qt_metacall (_c , _id , _a );

    if (_id < 0)

        return _id ;

    if (_c == QMetaObject ::InvokeMetaMethod ) {

        switch (_id ) {

        case 0: SignalA1 (); break ;

        case 1: SignalA2 ((*reinterpret_cast < int (*)>(_a [1]))); break ;

        case 2: SlotA1 (); break ;

        case 3: SlotA2 ((*reinterpret_cast < char *(*)>(_a [1])),(*reinterpret_cast < int (*)>(_a [2]))); break ;

        default : ;

         }

        _id -= 4;

    }

    return _id ;

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值