0
点赞
收藏
分享

微信扫一扫

QT信号槽实现原理源码

生活记录馆 2022-04-24 阅读 53
qt

我们使用QT的UI控件时,常用到触发控件的操作。
比如点击一个按钮,就进行一个什么操作。ui文件可以右击控件–转到槽来生成一个槽函数,很方便。
在这里插入图片描述
选择槽函数后,头文件里会多一个槽函数
private slots:
void on_pushButton_clicked();
这是QT的 QObject函数的信号与槽功能,使得控件的点击为一个信号,点击后触发槽函数进行操作。

我们自己也可以在QObject类里写上Q_OBJECT,signals和slots来实现信号槽。
QObject::connect(u,SIGNAL(started()),uObj,SLOT(dosthing()));
元对象编译器
MOC, the Meta Object Compiler。
Qt程序在交由标准编译器(例如MSVC)编译之前,先使用moc分析cpp头文件;如果它发现在一个头文件中包含了Q_OBJECT宏,则会生成另外一个cpp源文件(moc_文件名.cpp),该cpp源文件中包含了Q_OBJECT宏的实现、运行时信息(反射)等。因此Qt程序的完整编译过程为moc->预处理->编译->链接

它怎么实现的呢?
QT的QObject类型有signals
slots
Q_OBJECT
emit
SIGNAL
SLOT等宏定义。
定义相关信号槽的实现。
在这里插入图片描述
CTRL键+鼠标点击这些宏,进入qobjecdefs.h文件,这里定义了这些宏。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
信号
当按钮(对象)改变状态时,信号就emit,对象只负责发送,不负责接收方的处理。

接收到信号,不关心信号。
这个是观察者模式。
信号与槽如何连接:
(connect函数)
ctrl按鼠标进入qobject.h文件
看到 在QObject里定义connect()函数


    static QMetaObject::Connection connect(const QObject *sender, const char *signal,
                        const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);

    static QMetaObject::Connection connect(const QObject *sender, const QMetaMethod &signal,
                        const QObject *receiver, const QMetaMethod &method,
                        Qt::ConnectionType type = Qt::AutoConnection);

    inline QMetaObject::Connection connect(const QObject *sender, const char *signal,
                        const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;

原来QT是通过QObject::connect静态函数建立连接;其中sender与receiver是指向对象的指针,分别代表了被观察者和 观察者。
signal与method分别通过SIGNAL()与SLOT()宏来进行转换 (类型是const char*)
思路:

  • 定义两个类 sender(被观察者)和recver(观察者)
  • recver(观察者),定义对某个处理函数比如按钮触发后处理函数。槽函数
  • sender(被观察者),定义一个数据结构,保持观察者对哪个事件id感兴趣,使用map建立对应关系。
  • sender(被观察者) 有两个方法
    1 添加观察者和感兴趣的事件id到容器map 中
    2 通知事件函数执行逻辑:首先遍历map容器,有没有感兴趣的id
    若有,则代表一系列观察者,对这个事件感兴趣,再次遍历观察者列表,让其执行相应的槽函数。

程序运行时,connect借助两个字符串,即可将信号与槽的关联建立起来,那么,它是如果做到的呢?C++的经验可以告诉我们:

1.类中应该保存有信号和槽的字符串信息
2.字符串和信号槽函数要关联
moc_xxx.cpp文件中看到

// SIGNAL 0
void Worker::resultReady(const QString & _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

qobjectdefs.h文件里

    // internal index-based signal activation
    static void activate(QObject *sender, int signal_index, void **argv);
    static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
    static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);

所以

class Object;
struct MetaObject
{
    const char * sig_names;
    const char * slts_names;

    static void active(Object * sender, int idx);
};

这个函数该怎么写呢:思路很简单
●从前面的保存连接的map中,找出与该信号关联的对象和槽
●调用该对象这个槽

typedef std::multimap<int, Connection> ConnectionMap;
typedef std::multimap<int, Connection>::iterator ConnectionMapIt;

void MetaObject::active(Object* sender, int idx)
{
    ConnectionMapIt it;
    std::pair<ConnectionMapIt, ConnectionMapIt> ret;
    ret = sender->connections.equal_range(idx);
    for (it=ret.first; it!=ret.second; ++it) {
        Connection c = (*it).second;
        **//c.receiver->metacall(c.method);**
    }
}

槽如何调用呢

void Worker::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<Worker *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->resultReady((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        case 1: _t->doWork((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (Worker::*)(const QString & );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Worker::resultReady)) {
                *result = 0;
                return;
            }
        }
    }
}
void myclass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        auto *_t = static_cast<myclass *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->operate((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        case 1: _t->handleResults((*reinterpret_cast< const QString(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        {
            using _t = void (myclass::*)(const QString & );
            if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&myclass::operate)) {
                *result = 0;
                return;
            }
        }
    }
}

●直接调用槽函数我们都知道了,就一个普通函数
●可现在通过索引调用了,那么我们必须定义一个接口函数

class Object
{
    void metacall(int idx);
...
举报

相关推荐

0 条评论