QT的信号槽机制

QT的信号槽机制,第1张

        使用QT开发时,我们一般要使用到信号槽机制,这个机制由三部分组成:信号、槽、连接函数connect,我们主要 说下connect函数,了解清楚此函数,信号槽机制也基本上懂了。我们一般情况下我们使用connect函数只传递四个参数:

connect(Sender,SIGNAL(signal),Receiver,SLOT(slot))

        这四个参数分别是发送对象、发送者对象发送的信号、接收者对象、接收者对象响应该信号的槽函数,所以我们有可能认为该函数就只有四个参数,但实际上是有第五个参数的,只是通常该函数已经给第五个参数赋值了而已,我们所使用的是默认值。实际上connect函数应该是如下形式:

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

Qt::DirectConnection参数 参数含义

Qt::AutoConnection 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。

Qt::DirectConnection 槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。

Qt::QueuedConnection 槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。

Qt::BlockingQueuedConnection 槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

Qt::UniqueConnection 这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

sender和receiver是QObject对象指针,函数里面我们用到了Qt提供的两个宏SIGNAL()和SLOT()这是Qt要求的,要关联信号和槽必须借助于这两个宏,两个宏的定义如下:

#define SLOT(name) "1"#name

#define SIGNAL(name) "2"#name

        通过这两个宏,就可以把我们传递进去的槽和信号的名字转化成字符串,并在这两个字符串前面加上附加的字符。Qt5又在此基础上扩展了一种写法不必用到两个宏SIGNAL()和SLOT(),而是直接写&类名::信号或者&类名::槽函数。一个信号可以和多个槽相连;也可以多个信号可以连接一个槽;也有一个信号可以连接到另一个信号;一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽,有时候我们需要手动去断开连接,如下情况:

        有时我们程序中某些情况下某个 *** 作需要断开这个信号槽连接, *** 作结束后有需要重新连接,断开连接时,那我们需要调用函数

bool QObject::disconnect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method )

用法和connect大致相同。

下载好源码生成DLL或者直接下载DLL文件,引入项目即可。工具栏点击右键,选择项,添加浏览DLL文件确定即可出现工具ZedGraphControl拖进要加的页面或者FORM即可。注意有winform和web区分。不一样的。

一: [ 每个对象 ] 都有一个相应的纪录该对象的 [ 元对象 ]

关于元对象的类:

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

}

}

}

}


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/bake/11826269.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-19
下一篇2023-05-19

发表评论

登录后才能评论

评论列表(0条)

    保存