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

Qt——信号和槽

目录

信号和槽

自定义槽

自定义信号

带参数的信号和槽


信号和槽

        信号一词在Linux中也学过:当进程越界/野指针出现错误时,操作系统OS就会给目标进程发送信号来终止异常进程,但在Qt中的信号是两个不同的概念,但也有相似性;在Qt中信号由三要素组成:

  • 信号源:信号是由哪个控件发出来的;
  • 信号类型:用户操作(控件操作发出的类型)是什么,如控件按钮被用户点击后产生点击信号,在输入框中移动光标产生光标信号等
  • 信号处理:通过设置的函数来进行处理,也叫做槽;一般通过connect这样的函数进行设置后,产生信号Qt就会自动使用该函数进行处理(函数也叫回调函数)

        信号的产生和处理函数:一定是先设置处理函数,后续信号产生之后才能有对应的处理动作,否则信号到来没有进行处理就会错过了,也就可能导致意想不到的情况发生

        connect 函数一般是由 QObject 类来提供的;本身被 QWidget 类所继承,所以 QWidget 也能使用,QWidget被某个控件继承后也能使用...

        使用代码来感受connect函数的使用

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);button = new QPushButton(this);button->setText("按钮");button->move(200,200);connect(button,&QPushButton::clicked,this,&Widget::close);}
  • connect 的第一个,第二个参数类型要一致;
  • 第三个参数传入的是要处理的对象;
  • 最后一个参数传入处理函数,调用close()函数(内置槽函数)来对点击信号进行关闭操作

        注意第二个参数传入的是clicked信号函数,对应的图标是类似wifi的图案,而click则不是,它是一个执行点击操作的(slot)函数

        但如果你看到文档上的connect函数会发现,第二个参数与第四个参数可是要我们传入的是char*类型,这里我们传的却是函数指针?        这是因为文档上介绍的connect是旧版本的,新版本对connect进行重载支持传入函数指针并对它进行检测(使用Qt封装的类型萃取器)

自定义槽

  • 自定义成员函数来进行信号处理
//Widget.cpp
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);button = new QPushButton(this);button->setText("hello world");connect(button,&QPushButton::clicked,this,&Widget::ClickHandlerCode);}void Widget::ClickHandlerCode()
{if(button->text() == "hello world")button->setText("hello qt");elsebutton->setText("hello world");
}
  • 使用ui的方式生成槽函数

        使用ui的方式生成一个按钮后,点击右键

        选择按钮点击信号后,就会会我们生成一个处理按钮点击的空的槽函数

        填写相关处理逻辑之后,生成的按钮点击后就会自动调用该函数进行处理

//Widget.h
private slots:void on_pushButton_clicked();//Widget.cpp
void Widget::on_pushButton_clicked()
{if(ui->pushButton->text() == "hello world")ui->pushButton->setText("hello qt");elseui->pushButton->setText("hello world");
}

        如果使用ui的方式生成控件则可以使用ui的方式生成槽函数进行填写,不用使用connect进行信号到来与槽函数连接;如果使用代码的方式生成控件则就没办法得使用connect建立连接

自定义信号

        内置的大部分信号足够我们使用了,但可能实际开发有一些场景内置信号解决不了,此时就要自定义信号来解决

  • 自定义信号本质与自定义槽一样,也是函数;但它只需要写函数声明(返回值为void,参数不做限制)并且在前面加上signals关键字来告诉Qt这是一个信号函数,函数定义是Qt自动实现不需要我们去完成
//Widget.h
signals:void MySignal();

        发射自定义信号则要采用emit关键字加信号函数名来完成(emit也可以不加但推荐加上)

//widget.cppWidget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//成员变量button = new QPushButton(this);button->setText("hello world");//处理按钮点击信号connect(button,&QPushButton::clicked,this,&Widget::ClickHandler);//处理自定义信号connect(this,&Widget::MySignal,this,&Widget::MySignalHandler);
}void Widget::ClickHandler()
{emit MySignal();if(button->text() == "hello world")button->setText("hello qt");elsebutton->setText("hello world");
}void Widget::MySignalHandler()
{this->setWindowTitle("MySignal Emit");
}

        自定义信号需要我们手动设置来发射信号,从而才来执行处理信号的槽函数;而Qt内置的信号则不用,它会在用户执行某种操作后自动发射信号(发射信号的代码内置在Qt框架中),此外如果自己定义的类中要想使用信号槽机制,就要加上 Q_OBJECT 让Qt在该类中自动生成一些代码才能使用

带参数的信号和槽

        信号和槽函数可以带参数,但有规定:二者参数类型须一致;参数个数要满足要求:信号的参数个数大于槽的参数个数

        信号和槽参数类型一致后就可以在发射信号函数的同时给槽函数传递信息

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//成员变量button = new QPushButton(this);button->setText("hello world");//处理按钮点击信号connect(button,&QPushButton::clicked,this,&Widget::ClickHandler);//处理自定义信号connect(this,&Widget::MySignal,this,&Widget::MySignalHandler);
}void Widget::ClickHandler()
{emit MySignal("设置当前窗口标题");if(button->text() == "hello world")button->setText("hello qt");elsebutton->setText("hello world");
}void Widget::MySignalHandler(const QString& str)
{this->setWindowTitle(str);
}

为什么要要求信号参数个数大于槽函数的,严格规定参数个数一致不好吗?

        可能一个槽函数连接多个信号,规定参数个数一致就意味着对信号的要求变高了,信号与槽的连接受到限制,相反规定信号参数大于槽函数的就对信号要求没那么高了,就可以简单的把多个信号与槽建立连接;槽的参数与依次与信号参数进行匹配,信号多出来的参数对槽来说就进行忽略

Qt设计的信号和槽这种机制“这么有特色”,后面出来的GUI框架为什么不沿用呢?

        Qt设计信号槽,是为了:

  • 信号函数与处理信号槽函数进行解耦
  • 实现一对一,一对多,多对一,多对多的关系

        也就是一个信号既可以对应一个槽函数,也可以对应多个槽函数;槽函数对应信号也同理;这种对应关系在数据库的表也是如此:此时在数据库中有两张表:学生表和课程表,可以将两张表进行关联产生出一个关联表:当中a学生既可以只选择课程1,还可以再选择课程2,课程对应学生也是同理;数据库的多对多关系在实际开发中很有用,但Qt信号槽的多对多关系是一个“伪需求”,基本上不会遇到,一对一就已经够用了,所有在后续的GUI框架中,既然不会使用到干脆就把这部分简单设计成一对一的关系就可以了(这就与C++设计多继承类似)

如果先取消信号对应的槽函数连接,怎么办?

        使用disconnect来断开连接,使用上与connect是类似的(使用ui来实现)

//Widget.cppvoid Widget::ClickHandler()
{this->setWindowTitle("新连接的窗口标题");
}
//第一个按钮点击的槽函数
void Widget::on_pushButton_clicked()
{this->setWindowTitle("旧连接的窗口标题");
}
//第二个按钮点击的槽函数更改第一个按钮的连接
void Widget::on_pushButton_2_clicked()
{disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::on_pushButton_clicked);connect(ui->pushButton,&QPushButton::clicked,this,&Widget::ClickHandler);
}

connect连接时不想使用(成员)函数实现,有别的简单方法实现吗?

        可以使用C++11的lambda表达式来实现

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(200,200);connect(button,&QPushButton::clicked,[=](){button->setText("修改按钮");this->move(200,200);});
}

以上便是全部内容,有问题欢迎在评论区指正,感谢观看!

http://www.xdnf.cn/news/17630.html

相关文章:

  • 移动端网页调试实战,键盘弹出与视口错位问题的定位与优化
  • iOS 签名证书实践日记,我的一次从申请到上架的亲历
  • vue项目封装axios请求,支持判断当前环境及判断token是否过期等等(详细教程,可复制粘贴代码)
  • UE官方文档学习 C++ TAarry 查询(三)Contain,Find函数的使用
  • Java面试题储备11: mysql优化全面讲一下,及你遇到的对应业务场景
  • 第六十三章:AI模型的“跨界之旅”:不同硬件架构下的兼容性方案
  • RK3568 Linux驱动学习——Linux LED驱动开发
  • 数据分析与可视化
  • Java的异常机制
  • Supabase快速入门与实战指南
  • Effective C++ 条款37:绝不重新定义继承而来的缺省参数值
  • 存储过程作为系统逻辑核心的架构思考 —— 以 SaaS 系统为例
  • 计算机视觉(8)-纯视觉方案实现端到端轨迹规划(模型训练+代码)
  • 数据库规范化:消除冗余与异常的核心法则
  • 经济基础知识第一节:物质资料生产和基本经济规律(一)
  • SQL 与 NoSQL 的核心区别
  • 为什么灰度图用G(绿色)通道?
  • Docker 101:面向初学者的综合教程
  • 【报错处理】mount: /boot/efi: unknown filesystem type ‘LVM2_member‘.
  • 记录一次react渲染优化
  • 实现文字在块元素中水平/垂直居中详解
  • 教程 | 用Parasoft SOAtest实现高效CI回归测试
  • AWS EKS 常用命令大全:从基础管理到高级运维
  • [激光原理与应用-257]:理论 - 几何光学 - 光束整形
  • Springboot注册过滤器的三种方式(Order 排序)
  • Spring Cloud系列—Config配置中心
  • 【Oracle APEX开发小技巧16】交互式网格操作内容根据是否启用进行隐藏/展示
  • VS4210芯片技术资料(IT6604+VS4210+MDIN380连接原理图)
  • 基于STC8单片机的RTC时钟实现:从原理到实践
  • 如何使股指期货套期保值效果更加精准?