QT信号槽 官方文档和使用方式总结

信号、槽

参考文献

Signals & Slots | Qt Core 6.7.2
备注:本笔记大量内容可能属于官方文档的翻译或概括,但也包含个人的理解。不太清楚应该归为原创/转载/翻译的哪一类,如QT官方有修改或删除的需求请联系。

综述

  • 信号槽机制的目的

    • 信号槽机制是用来代替回调技术的,当信号发出时,对应的槽会被调用。它比回调技术略慢但更灵活。

    • 信号槽使得类被更好的封装。包含信号的类不需要关心是否有槽会接收信号,从而可以封装起来,反过来也是一样。

    • 信号槽机制还进行类型检查,通过编译器确保类型安全。信号和槽的参数必须匹配(但槽可以忽略部分参数,使得槽的参数个数比信号要少)。

  • 信号槽的使用

    • 一个类继承QObject或其子类,就可以使用信号槽。

    • 一个信号可以连接多个槽,一个槽可以接受多个信号。

    • 槽函数还可以当做普通函数使用。

信号

  • 信号是public函数,可以在任意处使用/发射(emit),但推荐在声明它的类及其子类中emit。

  • 当信号被发出时,连接到它的槽函数会像普通函数一样,被立即按照连接先后顺序依次执行;然后再执行信号发出之后的代码。

    • 但当使用queued connections时,顺序相反。会先执行emit之后的代码,然后再执行槽。

  • 信号的实现由moc 自动生成,不要在cpp文件中实现。

  • 参数可以有默认值

  • 槽可以看做能与信号连接的普通C++函数。它可以以通常方式被调用和执行。

  • 槽函数可以重载(同名),QT会自动选择适合的槽函数。

  • 信号槽机制可以一定程度上越过访问权限控制,即信号和槽的关系不受访问权限影响。举个极端的例子,信号位于A类中,槽甚至可以是与A没有关系的B类中的private函数。(但是在使用connect建立连接时,还是需要能同时使用信号和槽)

  • 参数可以用默认值

连接

有多种形式,见格式说明

示例和格式说明

  • 类、信号、槽的声明

    • 使用Q_OBJECT、public/private... slots和signals

    • 继承QObject或其子类

    #include <QObject>
    
    class Counter : public QObject
    {
        Q_OBJECT
    
    // Note. The Q_OBJECT macro starts a private section.(Q_OBJECT宏指令使用了private:)
    // To declare public members, use the 'public:' access modifier.(用public:覆盖)
    public:
        Counter() { m_value = 0; }
    
        int value() const { return m_value; }
    
    public slots:
        void setValue(int value);
    
    signals:
        void valueChanged(int newValue);
    
    private:
        int m_value;
    };
  • 槽的定义:与普通函数一样

  • 发出信号:

    • 使用emit关键字 + 函数调用

    • emit valueChanged(value);
    void Counter::setValue(int value)
    {
    if (value != m_value) {
        //这里检查不等于是防止不断发出信号导致死循环,比如一个对象的valuechanged与自身的setvalue连接
        m_value = value;
        emit valueChanged(value);
    }
    }
  • 连接信号槽

    • 使用connect,有多种方式

    • 需要对象、信号、槽

    Counter a, b;
        QObject::connect(&a, &Counter::valueChanged,
                         &b, &Counter::setValue);
    
        a.setValue(12);     // a.value() == 12, b.value() == 12
        b.setValue(48);     // a.value() == 12, b.value() == 48
  • 连接的多种形式
    假设

    void destroyed(QObject* = nullptr);
    void objectDestroyed(QObject* obj = nullptr);
    • 函数指针

      connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);

      这种形式的好处是编译器可以检查和进行隐式类型转换

    • lambda表达式

      connect(sender, &QObject::destroyed, this, [=](){ this->m_objects.remove(sender); });

      当sender或者this提供的上下文被销毁时,将会断开对lambda的连接。所以需要确保信号发出时所有用到的对象都还有效。

    • SIGNAL、SLOT宏 可以是

      connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(Qbject*)));
      connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed()));
      connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));

      但不能是

      connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed(QObject*)));

      因为槽的参数不能比信号多。使用这种方式时,不会检查参数;如果出错,会产生运行时错误。

    • 在以上形式中,都使用this指针提供执行的线程等上下文信息。

进阶使用

  • 获取发送者的信息

    • QObject::sender() 在被信号激活的槽中使用可以获取发送者的指针,否则返回空指针;返回值可能会因为断开连接或者发送者被销毁而失效

    • 或通过lambda形式在可以直接访问发送者的上下文中使用connect

      connect(action, &QAction::triggered, engine,
              [=]() { engine->processAction(action->text()); });

  • 避免与其他信号槽机制的库关键字冲突

    • cmake

      connect(action, &QAction::triggered, engine,
              [=]() { engine->processAction(action->text()); });

      qmake

      CONFIG += no_keywords

    • 然后把关键字对应替换为Q_SIGNALS/Q_SIGNAL、Q_SLOTS/Q_SLOT和Q_EMIT

  • 基于QT的库如何避免被QT_NO_KEYWORDS影响

    • 使用上面Q_形式的关键字代替signals和slots,并可以在构建库时对预处理器使用QT_NO_SIGNALS_SLOTS_KEYWORDS来保证没有使用信号槽关键字。

    • 这样做可以排除信号槽关键字,而不影响其他QT关键字的使用。

markdown文件版本:

https://download.csdn.net/download/hutao__233/89554589

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值