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

QT中关于信号与槽机制的实现原理

2009-10-14 14:34 861 查看
一:
[
每个对象
]
都有一个相应的纪录该对象的
[
元对象
]

关于元对象的类:

QMetaObject
类:

/*******************
生成元对象需要的输入参数
*****************/

//
类名

const char * const class_name,

//
父类名

QMetaObject *superclass,

//
记录
slot
信息

const QMetaData * const slot_data,

//
记录槽的个数

int n_slots,

//
记录
signal
信息

const QMetaData * const signal_data,

//
记录信号的个数

int n_signals

/*******************
元对象类提供的方法
**************************/

int
numSlots( bool super = FALSE ) const;//
返回槽的个数

int
numSignals( bool super = FALSE ) const;//
返回信号的个数

int
findSlot( const char *, bool super = FALSE ) const;//
查找槽

int
findSignal( const char *, bool super = FALSE ) const;//
查找信号

//
返回指定位置的槽

const QMetaData *slot( int index, bool super = FALSE ) const;

//
返回指定位置的信号

const QMetaData *signal( int index, bool super = FALSE ) const;

//
所有槽名字的列表

QStrList
slotNames( bool super = FALSE ) const;

//
所有信号名字的列表

QStrList
signalNames( bool super = FALSE ) const;

//
槽的起始索引

int
slotOffset() const;

//
信号的起始索引

int
signalOffset() const;

/***********************
两个获取类的元对象的方法
*****************/

static QMetaObject *metaObject( const char *class_name );

static bool hasMetaObject( const char *class_name );

QMetaData
类:

//
记录元对象数据
for
信号与槽

struct QMetaData

{

const char *name;
//
名称

const QUMethod* method;
//
详细描述信息

enum Access { Private, Protected, Public };

Access access;
//
访问权限

};

二:
[QObject
类实现了信号与槽机制
]

它利用元对象纪录的信息,实现了信号与槽机制


1
)信号与槽建立连接的实现

接口函数:

//
连接

//
参数(发送对象,信号,接收对象,处理信号的信号
/
槽)

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

const QObject *receiver, const char *member );

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

const char *member ) const;

static bool
disconnect( const QObject *sender, const char *signal,

const QObject *receiver, const char *member );

bool
disconnect(const char *signal=0,

const QObject *receiver=0, const char *member=0 );

bool
disconnect( const QObject *receiver, const char *member=0 );

//
连接的内部实现

//
(发送对象,信号的索引,接收对象,处理信号的类型,处理信号信号
/
槽的索引)

static void connectInternal(const QObject *sender, int signal_index,

const QObject *receiver, int membcode, int member_index );

static bool disconnectInternal(const QObject *sender, int signal_index,

const QObject *receiver, int membcode, int member_index );

信号与槽连接的实现原理:


阶段

bool QObject::connect( const QObject *sender,//
发送对象

const char *signal,//
信号

const QObject *receiver, //
接收对象

const char *member //


)

{

//
检查发送对象,信号,接收对象,槽不为
null

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

return FALSE;

}

//
获取发送对象的元对象

QMetaObject *smeta = sender->metaObject();

//
检查信号

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

return FALSE;

//
获取信号的索引

int signal_index = smeta->findSignal( signal, TRUE );

if ( signal_index < 0 ) {
// normalize and retry

nw_signal = qt_rmWS( signal-1 );
// remove whitespace

signal = nw_signal.data()+1;
// skip member type code

signal_index = smeta->findSignal( signal, TRUE );

}

//
如果信号不存在,则退出

if ( signal_index < 0
) {
// no such signal

return FALSE;

}

//
获取信号的元数据对象

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

//
获取信号名字

signal = sm->name;

//
获取处理信号的类型(是信号
/
槽)

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

//
发送信号对象

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

//
接收信号对象

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

//
获取接收对象的元对象

QMetaObject *rmeta = r->metaObject();

int member_index = -1;

switch ( membcode ) {
// get receiver member

case QSLOT_CODE://
如果是槽

//
获取槽索引

member_index = rmeta->findSlot( member, TRUE );

if ( member_index < 0 ) {
// normalize and retry

nw_member = qt_rmWS(member);
// remove whitespace

member = nw_member;

member_index = rmeta->findSlot( member, TRUE );

}

break;

case QSIGNAL_CODE://
如果是信号

//
获取信号索引

member_index = rmeta->findSignal( member, TRUE );

if ( member_index < 0 ) {
// normalize and retry

nw_member = qt_rmWS(member);
// remove whitespace

member = nw_member;

member_index = rmeta->findSignal( member, TRUE );

}

break;

}

/
如果接收对象不存在相应的信号或槽,则退出

if ( member_index < 0
) {

return FALSE;

}

//
检查连接的参数
(
发送的信号,接收对象,处理信号的槽或信号
)

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

return FALSE;

} else {

//
获取处理信号的元数据对象

const QMetaData *rm = membcode == QSLOT_CODE ?

rmeta->slot( member_index, TRUE ) :

rmeta->signal( member_index, TRUE );

if ( rm ) {

//
建立连接

//(
发送信号的对象,信号的索引,接收信号的对象,

处理信号的类型,处理信号的索引
)

connectInternal( sender, signal_index, receiver, membcode, member_index );

}

}

return TRUE;

}


阶段

//
建立连接

//(
发送信号的对象,信号的索引,接收信号的对象,处理信号的类型,处理信号的索引
)

void QObject::connectInternal( const QObject *sender, int signal_index,

const QObject *receiver,

int membcode, int member_index )

{

//
发送信号的对象

QObject *s = (QObject*)sender;

//
接收信号的对象

QObject *r = (QObject*)receiver;

//
如果发送对象的连接查询表为
null
,则建立

if ( !s->connections ) {
// create connections lookup table

s->connections = new QSignalVec( signal_index+1 );

Q_CHECK_PTR( s->connections );

s->connections->setAutoDelete( TRUE );

}

//
获取发送对象的相应信号的连接列表

QConnectionList *clist = s->connections->at( signal_index );

if ( !clist ) {
// create receiver list

clist = new QConnectionList;

Q_CHECK_PTR( clist );

clist->setAutoDelete( TRUE );

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

}

QMetaObject *rmeta = r->metaObject();

const QMetaData *rm = 0;

switch ( membcode ) {
// get receiver member

case QSLOT_CODE:

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

break;

case QSIGNAL_CODE:

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

break;

}

//
建立连接

QConnection *c = new QConnection( r, member_index, rm ? rm->name :

"qt_invoke", membcode );

Q_CHECK_PTR( c );

//
把连接添加到发送对象的连接列表中

clist->append( c );

//
判断接收对象的发送对象列表是否为
null

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

{

//
建立接收对象的发送对象列表

r->senderObjects = new QSenderObjectList;

}

//
把发送对象添加到发送对象列表中

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

}


2
)信号发生时激活的操作函数

接口:

/***************************************************************

**
激活
slot
的方法

****************************************************************/

void QObject::activate_signal( int signal )

{

#ifndef QT_NO_PRELIMINARY_SIGNAL_SPY

if ( qt_preliminary_signal_spy ) {

//
信号没有被阻塞

//
信号
>=0

//
连接列表不为空,或者信号对应的连接存在

if ( !signalsBlocked() && signal >= 0 &&

( !connections || !connections->at( signal ) ) ) {

//

QUObject o[1];

qt_spy_signal( this, signal, o );

return;

}

}

#endif

if ( !connections || signalsBlocked() || signal < 0 )

return;

//
获取信号对应的连接列表

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

if ( !clist )

return;

QUObject o[1];

//

activate_signal( clist, o );

}

/***************************************************************

**
激活
slot
的方法

****************************************************************/

void QObject::activate_signal( QConnectionList *clist, QUObject *o )

{

if ( !clist )

return;

#ifndef QT_NO_PRELIMINARY_SIGNAL_SPY

if ( qt_preliminary_signal_spy )

qt_spy_signal( this, connections->findRef( clist), o );

#endif

QObject *object;

//
发送对象列表

QSenderObjectList* sol;

//
旧的发送对象

QObject* oldSender = 0;

//
连接

QConnection *c;

if ( clist->count() == 1 ) { // save iterator

//
获取连接

c = clist->first();

//

object = c->object();

//
获取发送对象列表

sol = object->senderObjects;

if ( sol ) {

//
获取旧的发送对象

oldSender = sol->currentSender;

//

sol->ref();

//
设置新的发送对象

sol->currentSender = this;

}

if ( c->memberType() == QSIGNAL_CODE )//
如果是信号,则发送出去

object->qt_emit( c->member(), o );

else

object->qt_invoke( c->member(), o );//
如果是槽,则执行

//

if ( sol ) {

//
设置恢复为旧的发送对象

sol->currentSender = oldSender;

if ( sol->deref() )

delete sol;

}

} else {

QConnection *cd = 0;

QConnectionListIt it(*clist);

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

++it;

if ( c == cd )

continue;

cd = c;

object = c->object();

//
操作前设置当前发送对象

sol = object->senderObjects;

if ( sol ) {

oldSender = sol->currentSender;

sol->ref();

sol->currentSender = this;

}

//
如果是信号,则发送出去

if ( c->memberType() == QSIGNAL_CODE ){

object->qt_emit( c->member(), o );

}

//
如果是槽,则执行

else{

object->qt_invoke( c->member(), o );

}

//
操作后恢复当前发送对象

if (sol ) {

sol->currentSender = oldSender;

if ( sol->deref() )

delete sol;

}

}

}

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