槽机制和Connect函数

就我个人来理解,信号槽机制与Windows下消息机制类似,消息机制是基于回调函数,Qt中用信号与槽来代替函数指针,使程序更安全简洁。信号和槽机制是 Qt 的核心机制,可以让编程人员将互不相关的对象绑定在一起,实现对象之间的通信。当对象改变其状态时,信号就由该对象发射 (emit) 出去,而且对象只负责发送信号,它不知道另一端是谁在接收这个信号。这样就做到了真正的信息封装,能确保对象被当作一个真正的软件组件来使用。槽用于接收信号,而且槽只是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且对象并不了解具体的通信机制。

所有从 QObject 或其子类 ( 例如 Qwidget ) 派生的类都能够包含信号和槽。因为信号与槽的连接是通过 QObject 的 connect() 成员函数来实现的。

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

其中 sender 与 receiver 是指向对象的指针,SIGNAL() 与 SLOT() 是转换信号与槽的宏。

从Qobject(QObject.h)源码中可看到QObject::connect的定义是这样的:

1.    static bool connect(const QObject *sender, const char *signal,  

2.                        const QObject *receiver, const char *member, Qt::ConnectionType =  

3.        #ifdef qdoc  

4.                            Qt::AutoConnection  

5.        #else  

6.            #ifdef QT3_SUPPORT  

7.                                Qt::AutoCompatConnection  

8.        #else  

9.                                    Qt::AutoConnection  

10.         #endif  

11.     #endif  

12.     );  

13. inline bool connect(const QObject *sender, const char *signal,  

14.                     const char *member, Qt::ConnectionType type =  

15.     #ifdef qdoc  

16.                      Qt::AutoConnection  

17.     #else  

18.         #ifdef QT3_SUPPORT  

19.                                 Qt::AutoCompatConnection  

20.         #else  

21.                                 Qt::AutoConnection  

22.         #endif  

23.     #endif  

24.     ) const;  

其中第二个connect的实现其实只有一句话:

1.    { return connect(asender, asignal, this, amember, atype); }  

所以对于connect函数的学习其实就是研究第一个connect函数。

我们在使用connect函数的时候一般是这样调用的:

1.    connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));  

这里用到了两个宏:SIGNAL() 和SLOT();通过connect声明可以知道这两个宏最后倒是得到一个const char*类型。
在qobjectdefs.h中可以看到SIGNAL() 和SLOT()的宏定义:

1.    #ifndef QT_NO_DEBUG  

2.    # define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__)  

3.    # define METHOD(a)   qFlagLocation("0"#a QLOCATION)  

4.    # define SLOT(a)     qFlagLocation("1"#a QLOCATION)  

5.    # define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)  

6.    #else  

7.    # define METHOD(a)   "0"#a  

8.    # define SLOT(a)     "1"#a  

9.    # define SIGNAL(a)   "2"#a  

10. #endif  

所以这两个宏的作用就是把函数名转换为字符串并且在前面加上标识符。

比如:SIGNAL(read())展开后就是"2read()";同理SLOT(read())展开后就是"1read()"。

1.    connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));  

2.    实际上就是connect(sender,“2signal()”,receiver,“1slot())”;  

搞明白了实际的参数就可以来看connect的真正实现过程了,在QObject.cpp文件中可以找到connect的实现代码。

1.    bool QObject::connect(const QObject *sender, const char *signal,  

2.                          const QObject *receiver, const char *method,  

3.                          Qt::ConnectionType type)  

4.    {  

5.        {  

6.            const void *cbdata[] = { sender, signal, receiver, method, &type };  

7.            if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))  

8.                return true;  

9.        }  

10.   

11.     if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {  

12.         qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",  

13.                  sender ? sender->metaObject()->className() : "(null)",  

14.                  (signal && *signal) ? signal+1 : "(null)",  

15.                  receiver ? receiver->metaObject()->className() : "(null)",  

16.                  (method && *method) ? method+1 : "(null)");  

17.         return false;  

18.     }  

19.     QByteArray tmp_signal_name;  

20.   

21.     if (!check_signal_macro(sender, signal, "connect", "bind"))  

22.         return false;  

23.     const QMetaObject *smeta = sender->metaObject();  

24.     const char *signal_arg = signal;  

25.     ++signal; //skip code  

26.     int signal_index = smeta->indexOfSignal(signal);  

27.     if (signal_index < 0) {  

28.         // check for normalized signatures  

29.         tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);  

30.         signal = tmp_signal_name.constData() + 1;  

31.   

32.         signal_index = smeta->indexOfSignal(signal);  

33.         if (signal_index < 0) {  

34.             err_method_notfound(sender, signal_arg, "connect");  

35.             err_info_about_objects("connect", sender, receiver);  

36.             return false;  

37.         }  

38.     }  

39.   

40.     QByteArray tmp_method_name;  

41.     int membcode = extract_code(method);  

42.   

43.     if (!check_method_code(membcode, receiver, method, "connect"))  

44.         return false;  

45.     const char *method_arg = method;  

46.     ++method; // skip code  

47.   

48.     const QMetaObject *rmeta = receiver->metaObject();  

49.     int method_index = -1;  

50.     switch (membcode) {  

51.     case QSLOT_CODE:  

52.         method_index = rmeta->indexOfSlot(method);  

53.         break;  

54.     case QSIGNAL_CODE:  

55.         method_index = rmeta->indexOfSignal(method);  

56.         break;  

57.     }  

58.     if (method_index < 0) {  

59.         // check for normalized methods  

60.         tmp_method_name = QMetaObject::normalizedSignature(method);  

61.         method = tmp_method_name.constData();  

62.         switch (membcode) {  

63.         case QSLOT_CODE:  

64.             method_index = rmeta->indexOfSlot(method);  

65.             break;  

66.         case QSIGNAL_CODE:  

67.             method_index = rmeta->indexOfSignal(method);  

68.             break;  

69.         }  

70.     }  

71.   

72.     if (method_index < 0) {  

73.         err_method_notfound(receiver, method_arg, "connect");  

74.         err_info_about_objects("connect", sender, receiver);  

75.         return false;  

76.     }  

77.     if (!QMetaObject::checkConnectArgs(signal, method)) {  

78.         qWarning("QObject::connect: Incompatible sender/receiver arguments"  

79.                  "\n        %s::%s --> %s::%s",  

80.                  sender->metaObject()->className(), signal,  

81.                  receiver->metaObject()->className(), method);  

82.         return false;  

83.     }  

84.   

85.     int *types = 0;  

86.     if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)  

87.             && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))  

88.         return false;  

89.   

90.     QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);  

91.     const_cast<QObject*>(sender)->connectNotify(signal - 1);  

92.     return true;  

93. }  


上面是去除了debug代码的connect实现。

 

1.    const void *cbdata[] = { sender, signal, receiver, method, &type };  

2.    if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))  

3.          return true;  

判断连接是否已经建立。
QInternal::ConnectCallback在qglobal.cpp中实现。

1.    bool QInternal::activateCallbacks(Callback cb, void **parameters)  

2.    {  

3.        Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");  

4.      

5.        QInternal_CallBackTable *cbt = global_callback_table();  

6.        if (cbt && cb < cbt->callbacks.size()) {  

7.            QList<qInternalCallback> callbacks = cbt->callbacks[cb];  

8.            bool ret = false;  

9.            for (int i=0; i<callbacks.size(); ++i)  

10.             ret |= (callbacks.at(i))(parameters);  

11.         return ret;  

12.     }  

13.     return false;  

14. }  

QInternal_CallBackTable 定义为(qglobal.cpp)

1.    struct QInternal_CallBackTable {  

2.        QVector<QList<qInternalCallback> > callbacks;  

3.    };  

qInternalCallback定义为(qnamespace.h)

1.    typedef bool (*qInternalCallback)(void **);这是一个函数指针 返回值是bool,只有一个参数为void**。这个指针在调用registerCallback加入列表。  


 

 

1.    if (!check_signal_macro(sender, signal, "connect", "bind"))  

2.        return false;  

判断signal是否合法。

在QObject.cpp文件中可以找到check_signal_macro的实现

1.    static bool check_signal_macro(const QObject *sender, const char *signal,  

2.                                    const char *func, const char *op)  

3.    {  

4.        int sigcode = extract_code(signal);  

5.        if (sigcode != QSIGNAL_CODE) {  

6.            if (sigcode == QSLOT_CODE)  

7.                qWarning("Object::%s: Attempt to %s non-signal %s::%s",  

8.                         func, op, sender->metaObject()->className(), signal+1);  

9.            else  

10.             qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s",  

11.                      func, op, sender->metaObject()->className(), signal);  

12.         return false;  

13.     }  

14.     return true;  

15. }  


extract的实现也在QObject中,它就是去字符串第一个字符,并且只取低2位的值。

1.    static int extract_code(const char *member)  

2.    {  

3.        // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE  

4.        return (((int)(*member) - '0') & 0x3);  

5.    }  


这里又有两个宏:QSIGNAL_CODE 和QSLOT_CODE。它们也是在qobjectdefs.h文件中定义的。

1.    #ifdef QT3_SUPPORT  

2.    #define METHOD_CODE   0                        // member type codes  

3.    #define SLOT_CODE     1  

4.    #define SIGNAL_CODE   2  

5.    #endif  


这个定义与之前的SIGNAL和SLOT的定义是对应的。

 

 

 

1.    const QMetaObject *smeta = sender->metaObject();  

2.    const char *signal_arg = signal;  

3.    ++signal; //skip code  

4.    int signal_index = smeta->indexOfSignal(signal);  

5.    if (signal_index < 0) {  

6.        // check for normalized signatures  

7.        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);  

8.        signal = tmp_signal_name.constData() + 1;  

9.      

10.     signal_index = smeta->indexOfSignal(signal);  

11.     if (signal_index < 0) {  

12.         err_method_notfound(sender, signal_arg, "connect");  

13.         err_info_about_objects("connect", sender, receiver);  

14.         return false;  

15.     }  

16. }  

获取signal的索引。

metaObject()是在moc_name.cpp文件中生成的。

1.    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;  

其中staticMetaObject也是在moc文件中定义的

1.    const QMetaObject MainWindow::staticMetaObject = {  

2.        { &QMainWindow::staticMetaObject, qt_meta_stringdata_MainWindow,  

3.          qt_meta_data_MainWindow, 0 }  

4.    };  

qt_meta_stringdata_MainWindow(具体名字和类名有关)就是staticconstchar[]类型。它记录了全部的signals和slots等的函数名、返回值和参数表的信息。

qt_meta_data_MainWindow(具体名字和类名有关)是staticconstuint[]类型。它记录了每一个函数的函数名、返回值和参数表在qt_meta_stringdata_MainWindow中的索引。同时它还记录了每一个函数的类型具体在qmetaobject.cpp文件中定义。

1.    enum MethodFlags  {  

2.        AccessPrivate = 0x00,  

3.        AccessProtected = 0x01,  

4.        AccessPublic = 0x02,  

5.        AccessMask = 0x03, //mask  

6.      

7.        MethodMethod = 0x00,  

8.        MethodSignal = 0x04,  

9.        MethodSlot = 0x08,  

10.     MethodConstructor = 0x0c,  

11.     MethodTypeMask = 0x0c,  

12.   

13.     MethodCompatibility = 0x10,  

14.     MethodCloned = 0x20,  

15.     MethodScriptable = 0x40  

16. };  


indexOfSignal(signal);的实现在qmetaobject.cpp中。其主要作用是利用qt_meta_stringdata_MainWindow 和qt_meta_data_MainWindow查找已经定义了的signal并返回索引。
 

 

 

1.    QByteArray tmp_method_name;  

2.    int membcode = extract_code(method);  

3.      

4.    if (!check_method_code(membcode, receiver, method, "connect"))  

5.        return false;  

6.    const char *method_arg = method;  

7.    ++method; // skip code  

8.      

9.    const QMetaObject *rmeta = receiver->metaObject();  

10. int method_index = -1;  

11. switch (membcode) {  

12. case QSLOT_CODE:  

13.     method_index = rmeta->indexOfSlot(method);  

14.     break;  

15. case QSIGNAL_CODE:  

16.     method_index = rmeta->indexOfSignal(method);  

17.     break;  

18. }  

19. if (method_index < 0) {  

20.     // check for normalized methods  

21.     tmp_method_name = QMetaObject::normalizedSignature(method);  

22.     method = tmp_method_name.constData();  

23.     switch (membcode) {  

24.     case QSLOT_CODE:  

25.         method_index = rmeta->indexOfSlot(method);  

26.         break;  

27.     case QSIGNAL_CODE:  

28.         method_index = rmeta->indexOfSignal(method);  

29.         break;  

30.     }  

31. }  

32.   

33. if (method_index < 0) {  

34.     err_method_notfound(receiver, method_arg, "connect");  

35.     err_info_about_objects("connect", sender, receiver);  

36.     return false;  

37. }  


校验method并且查找它的索引。过程与signal类似。

 

1.    if (!QMetaObject::checkConnectArgs(signal, method)) {  

2.        qWarning("QObject::connect: Incompatible sender/receiver arguments"  

3.                 "\n        %s::%s --> %s::%s",  

4.                 sender->metaObject()->className(), signal,  

5.                 receiver->metaObject()->className(), method);  

6.        return false;  

7.    }  


判断signal和method是否兼容,checkConnectArgs函数的在qmetaObject.cpp文件中实现。这个函数校验了signal和method的参数。当两者的参数一致或method参数比signal参数少(method与signal前几个参数一致)的时候返回true,其它返回false。

 

 

 

1.    int *types = 0;  

2.    if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)  

3.            && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))  

4.        return false;  


如果是以发消息的方式执行method就需要对参数类型进行判断。queuedConnectionTypes在QObject.cpp实现。实际上是在QMetatype.cpp中定义了一个

static conststruct { constchar * typeName;int type;} types[];在这里记录了全部类型和名称如({"void",QMetaType::Void});Void在Qmetatype.h中定义。

 

 

1.    QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);  


调用QMetaObject的connect函数,再次不详细写出。

 

1.    const_cast<QObject*>(sender)->connectNotify(signal - 1);  


最后调用虚函数connectNotify表示connect已经执行完成。

 



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值