现在的位置: 首页 > 综合 > 正文

QT信号槽

2018年02月16日 ⁄ 综合 ⁄ 共 3261字 ⁄ 字号 评论关闭

</qt4GUI教程>

2.2深入介绍信号和槽

信号和槽机制是Qt编程的基础。它可以让应用程序编程人员把这些互不了解的对象绑定在一起。

Qt的元对象系统

Qt的主要成就之一就是使用了一种机制对C++进行了扩展,并且使用这种机制创建了独立的软件组件。这些组件可以绑定在一起,但任何一个组件对于它所要连接的组件的情况事先都一无所知。

这种机制称为元对象系统(meta-object system),它提供了关键的两项技术:信号---槽以及内省(introspection)内省功能对于实现信号和槽是必需的,并且允许应用程序的开发人员在运行时获得有关QObject子类的"元信息",包括一个含有对象的类名以及它所支持的信号和槽的列表。

标准C++没有对Qt的元对象系统所需要的动态元信息提供支持。Qt通过提供一个独立的moc工具解决了这个问题。Moc解析Q_OBJECT类的定义并且通过C++函数来提供可使用的信息。

这一机制是这样工作的:

Q_OBJECT宏声明了在每一个QObject子类中必须实现的一些内省函数:metaObject(),tr(),qt_metacall(),以及其他一些函数。

Qtmoc工具生成了角于由Q_OBJECT声明的所有函数和所有信号的实现。

connnect() disconnect()这样的QObject的成员函数使用这些内省函数来完成它们的工作。

</qt4GUI教程>

//<网上代码>
#include "qobject.h"  
class Informer:public QObject  
{  
Q_OBJECT;  
public:       
void notify()  
{  
for(int i=0;i<=10;i++)  
{  
emit send(i);  
}  
};  
signals:  
void send(int status);  
}; 
class Receiver:public QObject  
{  
Q_OBJECT;  
private:  
int total;  
public:  
Receiver(){this->total=0;};  
public slots:  
void count(int i)  
{  
printf("Counting: %d+%d=%d\n",this->total,i,this->total+i);  
this->total=this->total+i;  
};  
}; 
#include "main.moc"
#include <iostream>
int main(int argc, char* argv[])  
{  
Informer *s=new Informer();  
Receiver *r1=new Receiver();  
QObject::connect(s,SIGNAL(send(int)),r1,SLOT(count(int)));  
s->notify();  
delete s,r1;  
return 0;  
}  
//</网上代码>

核心问题1:Informer如何在不知道Receiver的情况下,调用Receiver的方法

这个例子中,要调用的函数为Receiver中的void count(int i)方法。调用过程如下

void Informer::send(int _t1)
{
    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
                           void **argv)
{
 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
}
int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
{
     return object->qt_metacall(cl, idx, argv);
}
int Receiver::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: count((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
        _id -= 1;
    }
    return _id;
}

    问题的核心变成了怎么样得到receiver这个对象。Receiver来源于QObject * const receiver = c->receiver; c来源于QObjectPrivate::Connection *c = connectionLists->at(signal_index).first;

connectionLists来源于

QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;

也就是说,receiver对象被保存在sender对象中。

现在要解决的问题是,receiver对象是如何被保存到sender对象中的。查看源码,发现在QObject::connect中向connectionLists添加元素。connect函数已包含sender对象和receiver对象,只要将receiver对象添到senderconnectionLists中即可。在日后的使用中,便能找到recevier对象了。

核心问题2:触发信号函数时系统如何找到与信号函数匹配的接收函数

这个问题可分为两个小问题:

(1)Reciver对象可能有多个方法,qt如何知道要调用哪个函数.

(2)如何将参数传给reciver对应的函数

突破口在这个段代码中

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,

                           void **argv)

{

//....................

 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);

//....................

}

metacall函数声明如下

static int metacall(QObject *, Call, int, void **);

这个函数又调用了

 return object->qt_metacall(cl, idx, argv);

Objectreceiver对象,mocreceiver生成了qt_metacall函数。在这个函数内部,通过idx找出要调用的函数,参数地址在argv数组中。通过argv,加上强转,就可以得到参数了。

示例代码如下

int Receiver::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: count((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
        _id -= 1;
    }
    return _id;
}

通过上面的代码,完成了调用过程。

【上篇】
【下篇】

抱歉!评论已关闭.