当前位置: 首页 > news >正文

『 QT 』信号-槽 补充: Qt信号槽断开连接与Lambda槽技巧

文章目录

    • 信号槽断开连接
    • Lambda 表达式定义槽函数


信号槽断开连接

通常情况下, 信号和槽是多对多的关系;

但在某些情况下我们并不期望一个信号同时引发多个槽的执行, 因此我们希望能使用某些方法使得在一定条件下能够断开某个信号与某个槽的连接并希望该信号与另一个槽进行连接;

这就涉及到了disconnect()断开连接;

[static] bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method)

这个即为disconnect的函数定义, 为QObject的成员函数;

通常为断开某个信号与某个槽之间的联系;

其使用方式与connect类似, 基本上相同, 主要存在四个参数:

  • const QObject *sender

    即信号发送者;

  • const QMetaMethod &signal

    信号函数;

  • const QObject *receiver

    信号接受者;

  • const QMetaMethod &method

    槽;

connect相同, disconnect函数同样存在新旧版本的语法差异, 其中QT4及以前的disconnect语法使用的是字符串风格, 也为上述所提到的风格, 所返回的返回值为一个bool值来判断一个控件的某个信号是否与对应的槽成功断开连接, 其返回值结果如下:

  • true

    连接成功断开;

  • false

    连接断开失败(槽与信号不一定匹配, 或是该信号为连接任何槽);

除此之外在QT5/6开始, 在disconnect引入了函数模板的版本, 可以直接通过传入指针而非字符串对对应的信号和槽进行连接的断开;

template <typename Func1, typename Func2>static inline bool disconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot)

其参数传入与connect相同, 此处不进行赘述;

通常情况下, 两种版本的connectdisconnect逻辑并不相同, 如果使用字符串版本的connect进行连接, 一般需要采用字符串(宏)版本的disconnect进行断联, 否则可能会导致disconnect失败;

假设存在一个示例, 一个widget框架中存在两个按钮, 其中一个按钮clicked信号对应的槽为改变Widgettitle"Hello QT", 另一个按钮的clicked信号对应的槽为改变另一个按钮clicked信号的槽, 使其点击时让widgettitle变为"Hello world";

由于connectSlotsByName依靠命名进行连接的方式通常是采用字符串(宏)版本的connect进行连接, 因此如果使用了connectSlotsByName时需要使用字符串(宏)版本的disconnect来断开连接;

/////////// Widget.cpp ///////////#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::on_change_title_clicked()
{this->setWindowTitle("Hello QT");qDebug() << "Change title to Hello QT";
}void Widget::on_change_button_slot_clicked()
{if(disconnect(ui->change_title, SIGNAL(clicked()), this, SLOT(on_change_title_clicked()))){// 连接的是 Widget::on_change_title_clicked 且已经断开aconnect(ui->change_title, SIGNAL(clicked()), this, SLOT(changeTitleToHelloWorld()));qDebug()<<"Change to Hello world";}else{// 连接的是&Widget::changeTitleToHelloWorld, 需要手动断开该槽并连接disconnect(ui->change_title, SIGNAL(clicked()), this, SLOT(changeTitleToHelloWorld()));connect(ui->change_title, SIGNAL(clicked()), this, SLOT(on_change_title_clicked()));qDebug()<<"Change to Hello QT";}
}void Widget::changeTitleToHelloWorld()
{this->setWindowTitle("Hello World");qDebug() << "Change title to Hello World";
}

这段代码中使用了字符串版的disconnectconnectSlotsByName所连接的槽进行断联, 由于控件change_titleclicked信号需要来回对两个槽进行连接与断联, 因此为了避免字符串版的disconnect和模板函数版的connect混用, 因此该处的连接与断开连接都统一使用字符串版本的;

运行结果为如下:


Lambda 表达式定义槽函数

Lamdba表达式本质上是一个匿名函数, 其允许捕获外部变量, 可以通过捕获的变量来对表达式内部进行传参;

由于其是一个匿名函数, 其可以很好的作为回调函数, 即用即弃;

在QT使用Lambda表达式作为槽函数可以减少connect的绑定从而简化代码;

同样以上面的代码作为基础, 可以对该段代码进行Lambda版化;

即修改widgettitle;

  • 首先创建两个QPushButton按钮控件

    change Title按钮的objectNamechangeTitle;

    change Slot for 'change Title'按钮的objectNamechangeTitleslot;

  • changeTitle创建两个槽并连接其中一个槽

    Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
    {ui->setupUi(this);connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world);
    }void Widget::change_title_hello_world(){ // 槽1this->setWindowTitle("Hello World");qDebug() << "changed title to hello world";
    }void Widget::change_title_hello_qt(){ // 槽2this->setWindowTitle("Hello QT");qDebug() << "changed title to hello qt";
    }
    
  • changeTitleslot连接Lambda槽

    Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
    {ui->setupUi(this);connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world);// Lamdba 表达式作为槽connect(ui->changeTitleslot, &QPushButton::clicked, this, /* Lambda 表达式 */[=/* 值捕获外部变量 */](){if(disconnect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world)) {connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_qt);qDebug()<<"The slot is changed for changeTitle - Hello QT";}else{disconnect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_qt);connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world);qDebug()<<"The slot is changed for changeTitle - Hello World";}});
    }
    

    即尝试断联槽1, 如果断开成功则连接槽2, 如果断开失败说明当前所连接的为槽2, 使用disconnect断开槽2并连接槽1;

  • 运行结果

  • 完整代码

    • Widget.h

      #ifndef WIDGET_H
      #define WIDGET_H#include <QWidget>
      #include <QDebug>QT_BEGIN_NAMESPACE
      namespace Ui {
      class Widget;
      }
      QT_END_NAMESPACEclass Widget : public QWidget
      {Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void change_title_hello_world();void change_title_hello_qt();private:Ui::Widget *ui;
      };
      #endif // WIDGET_H
      
    • widget.cpp

      #include "widget.h"
      #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
      {ui->setupUi(this);connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world);// Lamdba 表达式作为槽connect(ui->changeTitleslot, &QPushButton::clicked, this, /* Lambda 表达式 */[=/* 值捕获外部变量 */](){if(disconnect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world)) {connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_qt);qDebug()<<"The slot is changed for changeTitle - Hello QT";}else{disconnect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_qt);connect(ui->changeTitle, &QPushButton::clicked, this, &Widget::change_title_hello_world);qDebug()<<"The slot is changed for changeTitle - Hello World";}});
      }Widget::~Widget()
      {delete ui;
      }void Widget::change_title_hello_world(){this->setWindowTitle("Hello World");qDebug() << "changed title to hello world";
      }void Widget::change_title_hello_qt(){this->setWindowTitle("Hello QT");qDebug() << "changed title to hello qt";
      }
      

为了生命周期的管理, 我们一般在QT中使用Lambda表达式进行变量捕获时通常使用值捕获而不使用引用捕获, 本质是防止在Lambda表达式中出现对非法内存的访问;

同时QT5开始编译时使用的是c++11, 若是QT4或之前的版本需要手动修改编译选项, 使其支持Lamdba表达式;

通常要在.pro文件中手动添加或者修改CONFIG += c++11;

http://www.dtcms.com/a/506892.html

相关文章:

  • 为何海尔洗衣机屡屡发明新品类?
  • 【案例实战】HarmonyOS应用性能优化实战案例
  • 企业网站建设尚未实现宣传功能交互效果好的移动端网站
  • 10m带宽做下载网站深圳一公司今年新成立16家核检机构
  • 优麒麟(Ubuntu Kylin) 安装向日葵远程工具/ToDesk
  • 速卖通新号优惠券采购:自养号效率提升的安全要点
  • Linux InfiniBand代理模块深度解析:管理数据包的高效处理引擎
  • 开源协作2.0:GitHub Discussions+AI重构开发者社区的知识共创生态
  • Linux01:基础指令与相关知识铺垫(一)
  • QueryWrapper - MyBatis-Plus的“查询条件构建器“
  • Linux外设驱动开发1 - 单总线驱动开发__dht11
  • 使用高性能流式的库SpreadCheetah来添加图片和合并表格单元
  • 建设银行网站建设情况免费招聘的网站
  • 手机上怎么做微电影网站徐州做网站谁家最专业
  • 【Mathematics】椭圆眼睛跟随鼠标交互中的仿射变换数学推导
  • 【u-boot】u-boot的分区支持
  • CG-FS-A3 风速传感器 485型 体积小巧 便捷安装 三杯式 聚碳材质
  • http和https区别如何转https
  • 国外的主要电机生产厂商
  • 英伟达公司发展历史
  • 网站首页文件名通常是无锡市建设安全监督网站
  • SQL之参数类型讲解——从基础类型到动态查询的核心逻辑
  • Linux中匿名设备和安全相关以及VFS的slab缓存对象创建
  • B.NET编写不阻塞UI线程的同步延时
  • 论文泛读:DYNAPROMPT: DYNAMIC TEST-TIME PROMPT TUNING(动态测试时调优)
  • 做 58 那样的网站北京公司网页设计
  • PyTorch实战(9)——从零开始实现Transformer
  • 18.SELInux安全性
  • Layui连线题编辑器组件(ConnectQuestion)
  • 电影网站加盟可以做么网奇seo培训官网