Chinaunix首页 | 论坛 | 博客
  • 博客访问: 371755
  • 博文数量: 43
  • 博客积分: 1493
  • 博客等级: 上尉
  • 技术积分: 660
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-01 10:57
文章分类

全部博文(43)

文章存档

2015年(1)

2013年(1)

2011年(6)

2010年(13)

2009年(13)

2008年(9)

分类: LINUX

2008-10-31 08:57:57

                        Qte通讯之signal slot

 

 

       如果一个类需要利用qte提供的signal 机制,则需要做的是在申明类的时候,添加这样一个宏:Q_OBJECT,并且继承自qobject,这个宏展开后是这样的:

       #define Q_OBJECT                                   \

public:                                               \

    QMetaObject *metaObject() const {                  \

         return staticMetaObject();                  \

    }                                               \

    const char *className() const;                  \

    static QMetaObject* staticMetaObject();             \

    QT_TR_FUNCTION                                 \

protected:                                          \

    void initMetaObject();                         \

private:                                       \

static QMetaObject *metaObj;

       ………………………………………………………………………………

#ifndef QT_NO_TRANSLATION

#define QT_TR_FUNCTION static QString tr(const char*); \

                     static QString tr(const char*, const char*);

#else

#define QT_TR_FUNCTION // inherit the one from QObject

#endif

换句话说,只要你引用这样一个宏,你的类里面就多了这么一些函数和一个成员变量。

假设有这么一个头文件:

#include

 

class ClassName:public QObject

{

Q_OBJECT

public slots:

    int slot1();

signals:

    void signal1();

};

看看通过moc编译后的结果(这只是其中两个我们关心的函数):

QMetaObject* ClassName::staticMetaObject()

{

    if ( metaObj )

    return metaObj;

    QMetaObject* parentObject = QObject::staticMetaObject();

    static const QUParameter param_slot_0[] = {

    { 0, &static_QUType_int, 0, QUParameter::Out }

    };

    static const QUMethod slot_0 = {"slot1", 1, param_slot_0 };

    static const QMetaData slot_tbl[] = {

    { "slot1()", &slot_0, QMetaData::Public }

    };

    static const QUMethod signal_0 = {"signal1", 0, 0 };

    static const QMetaData signal_tbl[] = {

    { "signal1()", &signal_0, QMetaData::Public }

    };

    metaObj = QMetaObject::new_metaobject(

    "ClassName", parentObject,//这里传递的两个数据表示类名和父类名

    slot_tbl, 1,//这里是槽集,和槽的个数

    signal_tbl, 1,//这里是信号集,和信号的个数

#ifndef QT_NO_PROPERTIES

    0, 0,

    0, 0,

#endif // QT_NO_PROPERTIES

    0, 0 );

    cleanUp_ClassName.setMetaObject( metaObj );

    return metaObj;

}

QMetaObject的结构不是很简单,但你可以理解成这样一种结构,里面存放的是信号和槽的信息。QMetaData你可以理解为描述信号或槽的数据结构,比如它的名称,它的函数指针,还有它的访问权限。QUParameter是用来描述槽(信号)的参数的结构,比如它的名称,类型,属性等。QUMethod是用来描述具体的某个槽(信号)的,包括它的名称,参数个数,和具体的参数描述。

接下来看看QMetaObject的构造函数都做了些什么:

QMetaObject::QMetaObject( const char *class_name, const char *superclass_name,

                       QMetaData *slot_data,        int n_slots,

                       QMetaData *signal_data, int n_signals,

#ifndef QT_NO_PROPERTIES

                       QMetaProperty *prop_data, int n_props,

                       QMetaEnum *enum_data, int n_enums,

#endif

                       QClassInfo *class_info, int n_info )

{

    if ( !objectDict ) {                // first meta object created

       objectDict= new QObjectDictionary( 211,

                                 TRUE,   // no copying of keys

                                 FALSE );      // case sensitive

       CHECK_PTR( objectDict );

       objectDict->setAutoDelete( TRUE );     // use as master dict

    }

 

    classname = class_name;                    // set meta data

    superclassname = superclass_name;

    slotDict = init( slotData = slot_data, n_slots );

    signalDict = init( signalData = signal_data, n_signals );

 

    d = new QMetaObjectPrivate;

    reserved = 0;

 

#ifndef QT_NO_PROPERTIES

    d->propData = prop_data;

    d->numPropData = n_props;

    d->enumData = enum_data;

    d->numEnumData = n_enums;

#endif

    d->classInfo = class_info;

    d->numClassInfo = n_info;

    objectDict->insert( classname, this );    // insert into object dict

    superclass = objectDict->find( superclassname ); // get super class meta object

}

在里我觉得最关键是三步:

slotDict = init( slotData = slot_data, n_slots );

signalDict = init( signalData = signal_data, n_signals );

这两步就负责初试化两个dict存放信号的和存放槽的。

就是通过把它们的名字连接到响应的槽或信号的数据结构。

objectDict->insert( classname, this );

这步是负责把相应的类名和类指针连接到一起。

void ClassName::signal1()

{

    activate_signal( staticMetaObject()->signalOffset() + 0 );

}

这里面可以看出,一个信号,经过编译后就变成了另外一个函数activate_signal这个函数待会再讲。

接下来看看connect的实现过程。

假设为:

Connect(this,SIGNAL(signal1()),this,SLOT(slot1()));

看看signalslot被展开后的结果。

#if defined(_OLD_CPP_)

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

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

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

#else

#define METHOD(a)     "0"#a

#define SLOT(a)           "1"#a

#define SIGNAL(a)       "2"#a

#endif

你可以简单的理解为SIGNAL(signal1()) 将会被展开成“2signal1()

SLOT(slot1())将会被展开成“1slot1()

connect又会做些什么呢?

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

                     const QObject *receiver, const char *member )

{

#if defined(CHECK_NULL)

    if ( sender == 0 || receiver == 0 || signal == 0 || member == 0 ) {

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

               sender ? sender->className() : "(null)",

               signal ? signal+1 : "(null)",

               receiver ? receiver->className() : "(null)",

               member ? member+1 : "(null)" );

       return FALSE;

    }

#endif

    QMetaObject *smeta = sender->queryMetaObject();//get metaobject

    if ( !smeta ){                       // no meta object

       qWarning("the smeta is null,added by wf\n");

       return FALSE;

    }

 

#if defined(CHECK_RANGE)

    if ( !check_signal_macro( sender, signal, "connect", "bind" ) ){

       qWarning("the check_signal_macro is null,added by wf\n");

       return FALSE;

    }

#endif

    QCString signal_name = signal;

    signal++;                             // skip member type code

    QMetaData *sm = smeta->signal(signal,TRUE);

    if ( !sm ) {

       signal--;

#ifdef DEBUG_WHITESPACE

       static int bad = 0;

       qDebug("Invalid whitespace in sender: (%d), %s::%s", ++bad, sender->className(), signal+1);

#endif

       signal_name = qt_rmWS( signal ); // white space stripped

       signal = signal_name;

       signal++;

       sm = smeta->signal(signal,TRUE);

    }

#ifdef DEBUG_WHITESPACE

    else {

       static int good = 0;

       if (!(good%10))

              qDebug("Good senders: %d", good);

       good++;

    }

#endif

    if ( !sm ) {

#if defined(CHECK_RANGE)

       err_member_notfound( SIGNAL_CODE, sender, signal, "connect" );

       err_info_about_candidates( SIGNAL_CODE, smeta, signal, "connect" );

       err_info_about_objects( "connect", sender, receiver );

#endif

       qWarning("the err_info check error,added by wf\n");

       return FALSE;

    }

    signal = sm->name;                            // use name from meta object

 

    int membcode = member[0] - '0';         // get member code

 

    QObject *s = (QObject *)sender;         // we need to change them

    QObject *r = (QObject *)receiver;              //   internally

 

#if defined(CHECK_RANGE)

    if ( !check_member_code( membcode, r, member, "connect" ) ){

       qWarning("the check member code err,added by wf\n");

       return FALSE;

    }

#endif

    QCString member_name = member;

    member++;                                 // skip code

    QMetaData   *rm = 0;

    QMetaObject *rmeta = r->queryMetaObject();

    if ( !rmeta ){                        // no meta object

       qWarning("the rmeta is err,added by wf\n");

       return FALSE;

    }

#ifdef DEBUG_WHITESPACE

    static int member_bad = 0;

    static int member_good = 0;

#endif

    switch ( membcode ) {                // get receiver member

       case SLOT_CODE:

           rm = rmeta->slot( member, TRUE );

           if ( !rm ) {

              member--;

#ifdef DEBUG_WHITESPACE

              qDebug("Invalid whitespace in receiver: (%d), %s::%s", ++member_bad, receiver->className(), member+1);

#endif

              member_name = qt_rmWS( member ); // white space stripped

              member = member_name;

              member++;

              rm = rmeta->slot(member,TRUE);

           }

#ifdef DEBUG_WHITESPACE

           else {

              if (!(member_good%10))

                  qDebug("Good receivers: %d", member_good);

              member_good++;

           }

#endif

           break;

       case SIGNAL_CODE:

           rm = rmeta->signal( member, TRUE );

           if ( !rm ) {

              member--;

#ifdef DEBUG_WHITESPACE

              qDebug("Invalid whitespace in receiver: %d, %s::%s", ++member_bad, receiver->className(), member+1);

#endif

              member_name = qt_rmWS( member ); // white space stripped

              member = member_name;

              member++;

              rm = rmeta->signal(member,TRUE);

           }

#ifdef DEBUG_WHITESPACE

           else {

              static int member_good = 0;

              if (!(member_good%10))

                  qDebug("Good receivers: %d", member_good);

              member_good++;

           }

#endif

           break;

    }

    if ( !rm ) {                           // no such member

#if defined(CHECK_RANGE)

       err_member_notfound( membcode, r, member, "connect" );

       err_info_about_candidates( membcode, rmeta, member, "connect" );

       err_info_about_objects( "connect", sender, receiver );

#endif

       qWarning("the err check in rm is null is err,added by wf\n");

       return FALSE;

    }

#if defined(CHECK_RANGE)

    if ( !s->checkConnectArgs(signal,receiver,member) )

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

               "\n\t%s::%s --> %s::%s",

               s->className(), signal,

               r->className(), member );

#endif

    if ( !s->connections ) {                // create connections dict

       s->connections = new QSignalDict( 7, TRUE, FALSE );

       CHECK_PTR( s->connections );

       s->connections->setAutoDelete( TRUE );

    }

    QConnectionList *clist = s->connections->find( signal );

    if ( !clist ) {                         // create receiver list

       clist = new QConnectionList;

       CHECK_PTR( clist );

       clist->setAutoDelete( TRUE );

       s->connections->insert( signal, clist );

    }

    QConnection *c = new QConnection(r, rm->ptr, rm->name);

    CHECK_PTR( c );

    clist->append( c );

    if ( !r->senderObjects ) {                    // create list of senders

       r->senderObjects = new QObjectList;

       CHECK_PTR( r->senderObjects );

    }

    r->senderObjects->append( s );           // add sender to list

    s->connectNotify( signal_name );

    return TRUE;

}

这段代码还是比较长,我们只看最重要的:

QMetaObject *smeta = sender->queryMetaObject()

这函数最后会调用staticMetaObject(),而这个函数,我们上面已经分析过了,

它就是把此类的信号和槽分别放到一个dict里面存起来,并返回一个QMetaObject,这里面存放了这些信号和槽的信息。

QCString signal_name = signal;

    signal++;                             // 这里加1刚好把前面的数字12去掉了。

QMetaData *sm = smeta->signal(signal,TRUE);

这几句话的作用就是通过smeta调用signal函数传一个字符串进去返回一个描信号的结构。

       接下来如法炮制,通过queryMetaObject返回接收者的QMetaObject,再通过一个switch语句确定接收的函数是信号还是槽。

  if ( !s->connections ) {                   // create connections dict

       s->connections = new QSignalDict( 7, TRUE, FALSE );

       CHECK_PTR( s->connections );

       s->connections->setAutoDelete( TRUE );

    }

    QConnectionList *clist = s->connections->find( signal );

    if ( !clist ) {                         // create receiver list

       clist = new QConnectionList;

       CHECK_PTR( clist );

       clist->setAutoDelete( TRUE );

       s->connections->insert( signal, clist );

    }

    QConnection *c = new QConnection(r, rm->ptr, rm->name);

    CHECK_PTR( c );

    clist->append( c );

    if ( !r->senderObjects ) {                    // create list of senders

       r->senderObjects = new QObjectList;

       CHECK_PTR( r->senderObjects );

    }

    r->senderObjects->append( s );          

这段代码的意思就是,建立一个QSignalDict的结构,把信号和连接在此信号的槽或者信号的连表中,注意,这里是说,一个信号,对应一个链表,而这个连表存放的是所有连接在此信号上的信号和槽。

最后接收者再吧发送者连接到自己的发送连表中。

如果用一句话来表达connect的作用就是:

把相应的slot插入到此信号将要触发的信号和槽的链表中。

再看看这是怎么触发的:

先前知道了,触发一个信号可以调用它本身,也可以写emit signal1,它会去调用activate_signal,现在看看这个函数吧:

 

void QObject::activate_signal( const char *signal )

{

    if ( !connections )

       return;

    QConnectionList *clist = connections->find( signal );

    if ( !clist || signalsBlocked() )

       return;

    typedef void (QObject::*RT)();

    RT r;

    QConnectionListIt it(*clist);

    register QConnection *c;

    register QObject *object;

    while ( (c=it.current()) ) {

       ++it;

       object = c->object();

       object->sigSender = this;

       r = (RT)*(c->member());

       (object->*r)();

    }

}

代码很简单,就是通过此信号的名字,在一个集合里面查找其对应的connectionlist

然后对此list里面的每个connection,判断其发送者是不是等于当前的对象,如果是,就调用此信号对应的函数或槽。

 

总结一下它的整个过程:

首先通过Q_OBJECT的申明引入了一个staticMetaObject()函数,当调用connect的时候,它先调用这个函数把此类的信号和槽存放到一个相应的dict中去供以后查询所用。然后建立一个链表把连接到此信号的槽或者信号先放到一个connectionlist中去,最后把此链表与响应的信号名字连接这一起。触发的时候根据此名字即可以找到相应的函数,然后调用,一起ok了!

文件:qte通讯之signal slot.rar
大小:11KB
下载:下载
阅读(1796) | 评论(0) | 转发(0) |
0

上一篇:qt qcop机制

下一篇:arm指令学习

给主人留下些什么吧!~~