Qt信号和槽-->day04
Qt信号和槽
- 标准的信号和槽函数
- Qt中的槽函数
- Qt中的信号
- connect
- 案例
- 自定义信号和槽
- 案例分析
- 信号槽的拓展
- 信号连接信号案例
- 信号槽的两种连接方式
- Qt5中的处理方式
- Qt4中的处理方式
- Qt5处理信号槽重载问题
- 案例
- lambda表达式
- 简单案例
- Qt中的应用
- 补充知识点
标准的信号和槽函数
QWidget标准的信号和槽函数
此外信号和槽函数是可以被继承的
可以看到QPushButton是有标准的槽函数,但是没有看到信号。
实际上QPushButton是继承了它的父类的信号
Qt中的槽函数
Qt中的槽函数可以是类的成员函数、全局函数、静态函数、Lambda表达式(匿名函数)
Qt中的信号
信号需要使用 signals 关键字进行声明, 使用方法类似于public等关键字
connect
connect(信号发生者,信号,信号接受者,接受者的处理动作);
connect只是做了一个信号注册,具体操作步骤是通过qt框架进行实现的
案例
功能实现: 点击窗口上的按钮, 关闭窗口
功能分析:
- 按钮: 信号发出者 -> QPushButton 类型
- 窗口: 信号的接收者和处理者 -> QWidget 类型
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ui->closeBtn;//>>>>>>>>>>>>>>>>>>通过ui对象可以操纵所创建的按钮
connect(ui->closeBtn,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
不可以将第四个参数写为 &this->close,因为 &this->close 表示的是 MainWindow 类的成员函数指针,而 QPushButton::clicked 信号需要一个成员函数指针,而这个指针的类型是 void (MainWindow:😗)()(即没有参数且没有返回值的成员函数指针)。
自定义信号和槽
案例分析
假设要实现一个类GirlFriend发出hungry信号,一个类Me来接受信号进行处理。
- 1、由于这两个是不同的类,所以应该提供一个窗口类对上面的这两个类进行包装
- 2、在GirlFriend类中只需要声明信号,不需要实现,因为这是信号的发出者
- 3、在Me类的槽函数不仅需要声明也需要实现
主窗口头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "girlfriend.h"
#include "me.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void hungrySlot();
private:
Ui::MainWindow *ui;
GirlFriend *g_girl;
Me *m_me;
};
#endif // MAINWINDOW_H
主窗口函数实现
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ui->closeBtn;//>>>>>>>>>>>>>>>>>>通过ui对象可以操纵所创建的按钮
g_girl=new GirlFriend;
m_me=new Me;
//这个hungry是自定义的不由Qt框架提供,需要使用者发射信号 >>>>>>> 窗口中实现
connect(g_girl,&GirlFriend::hungry,m_me,&Me::eat);
//>>>>>>>>>>>>>>提醒Qt框架有信号
connect(ui->hungry,&QPushButton::clicked,this,&MainWindow::hungrySlot);
connect(ui->closeBtn,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::hungrySlot()
{
//发射信号
emit g_girl->hungry();
}
信号槽的拓展
- 一个信号可以连接多个槽函数, 发送一个信号有多个处理动作
需要写多个connect()连接
槽函数的执行顺序是随机的, 和connect函数的调用顺序没有关系- 信号的接收者可以是一个对象, 也可以是多个对象
一个槽函数可以连接多个信号, 多个不同的信号, 处理动作是相同的
需要写多个connect()连接- 信号可以连接信号
connect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::siganl-new);
- 信号槽可以断开信号 --> 很少使用
disconnect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);
信号连接信号案例
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ui->closeBtn;//>>>>>>>>>>>>>>>>>>通过ui对象可以操纵所创建的按钮
g_girl=new GirlFriend;
m_me=new Me;
//这个hungry是自定义的不由Qt框架提供
connect(g_girl,&GirlFriend::hungry,m_me,&Me::eat);
connect(g_girl,&GirlFriend::hungry,this,&MainWindow::eatSlot);
//信号连接信号
connect(ui->hungry,&QPushButton::clicked,g_girl,&GirlFriend::hungry);
//信号连接槽函数
//connect(ui->hungry,&QPushButton::clicked,this,&MainWindow::hungrySlot);
connect(ui->closeBtn,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::hungrySlot()
{
//发射信号
emit g_girl->hungry();
}
void MainWindow::eatSlot()
{
qDebug()<<"喝尿";
}
信号槽的两种连接方式
Qt5中的处理方式
第二个和第四个参数是函数地址
Qt4中的处理方式
信号槽函数通过宏SIGNAL和SLOT转换为字符串类型
Qt5处理信号槽重载问题
signals:
void hungry();
void hungry(QString msg);
public slots:
//槽函数
void eat();
void eat(QString msg);
解决方案1:使用Qt4的信号与槽的机制(慎用,编译器不会报错如果你写错了)
//Qt4的信号与槽
connect(g_girl,SIGNAL(hungry()),m_me,SLOT(eat()));
connect(g_girl,SIGNAL(hungry(QString)),m_me,SLOT(eat(QString)));
解决方案2:使用函数指针进行指定
void(GirlFriend::*girl1)()=&GirlFriend::hungry;
void(GirlFriend::*girl2)(QString)=&GirlFriend::hungry;
void(Me::*point)(QString)=&Me::eat;
void(Me::*pointt)()=&Me::eat;
connect(g_girl,girl1,m_me,pointt);
connect(g_girl,girl2,m_me,point);// >>>>> Qt5
connect(g_girl,girl2,this,&MainWindow::eatSlot);// >>>>>> 信号是带参的而槽函数是不带参的 >>>> 直接就是执行eatSlot
案例
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// ui->closeBtn;//>>>>>>>>>>>>>>>>>>通过ui对象可以操纵所创建的按钮
g_girl=new GirlFriend;
m_me=new Me;
//这个hungry是自定义的不由Qt框架提供
// >>>>>>>>>>>>>> Qt5处理重载
void(GirlFriend::*girl1)()=&GirlFriend::hungry;
void(GirlFriend::*girl2)(QString)=&GirlFriend::hungry;
void(Me::*point)(QString)=&Me::eat;
void(Me::*pointt)()=&Me::eat;
connect(g_girl,girl1,m_me,pointt);
connect(g_girl,girl2,m_me,point);// >>>>> Qt5
connect(g_girl,girl2,this,&MainWindow::eatSlot);
//err
//connect(g_girl,&GirlFriend::hungry,m_me,&Me::eat);// >>>>> Qt5
//connect(g_girl,&GirlFriend::hungry,this,&MainWindow::eatSlot);
//Qt4的信号与槽
connect(g_girl,SIGNAL(hungry()),m_me,SLOT(eat()));
connect(g_girl,SIGNAL(hungry(QString)),m_me,SLOT(eat(QString)));
//信号连接信号
//connect(ui->hungry,&QPushButton::clicked,g_girl,&GirlFriend::hungry);
//信号连接槽函数
connect(ui->hungry,&QPushButton::clicked,this,&MainWindow::hungrySlot);
connect(ui->closeBtn,&QPushButton::clicked,this,&MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::hungrySlot()
{
//发射信号
g_girl->hungry();
g_girl->hungry("粑粑");
}
void MainWindow::eatSlot()
{
qDebug()<<"喝尿";
}
lambda表达式
lambda表达式–>匿名函数
[capture](params) opt -> ret {body;};
- capture: 捕获列表
- params: 参数列表
- opt: 函数选项
- ret: 返回值类型
- body: 函数体
捕获列表: 捕获一定范围内的变量
[] - 不捕捉任何变量
[&] - 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)
[=] - 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获)
拷贝的副本在匿名函数体内部是只读的
[=, &foo] - 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo
[bar] - 按值捕获 bar 变量, 同时不捕获其他变量
[&bar] - 按引用捕获 bar 变量, 同时不捕获其他变量
[this] - 捕获当前类中的this指针
让lambda表达式拥有和当前类成员函数同样的访问权限
如果已经使用了 & 或者 =, 默认添加此选项
简单案例
// 匿名函数的定义, 程序执行这个匿名函数是不会被调用的
[](){
qDebug() << "hello, 我是一个lambda表达式...";
};
// 匿名函数的定义+调用:
int ret = [](int a) -> int
{
return a+1;
}(100); // 100是传递给匿名函数的参数
Qt中的应用
补充知识点
#include<iostream>
using namespace std;
class a
{
public:
void print()
{
cout << "1" << endl;
}
};
int main()
{
a a; // 创建对象
a.print();
// 声明成员函数指针并调用
void (a:: * print_ptr)() = &a::print; // 成员函数指针
(a.*print_ptr)(); // 通过指针调用
}