当按钮检测到自己被点击,它就发出一个信号
signal
,当对象对这个信号感兴趣,就会使用连接 connect 函数,将要处理的信号与自己的函数 (也称为槽
) 绑定到一起处理这个信号
# 信号
信号时由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了特定时间,Qt 会发出某个信号,以此对用户的挑选做出反映,信号的本质就是事件
- 鼠标点击,双击
- 窗口刷新 (大小变化)
- 鼠标移动,鼠标按下,鼠标释放
- 键盘输入
# 槽
槽函数是一类特殊的功能函数,在编码过程中也可以作为类的普通成员函数来使用。
# connect
在 Qt 中信号和槽都是独立的个体,通过 connect 函数进行二进制关联。
connnect(信号sender发出者,函数指针信号signal函数地址,信号接受者receiver,method方法) | |
// 当检测到 sender 发送 signal 信号时,receiver 对象调用 method 方法,信号发出之后处理动作 | |
//method 槽函数本质是一个回调函数,调用的时机是信号产生之后, | |
// 接受者和发出者的必须实例化 |
# 使用
功能实现:点击窗口上的按钮,关闭窗口
功能分析:
- 按钮:信号发出者 -> QPushBUtton 类型
- 窗口:信号的接收者和处理者 -> QWidget 类型
需要使用的标准信号槽函数
// 单击按钮发出的信号 | |
[signal] void QAbstractButton::clicked(bool checked = false) | |
// 关闭的窗口的槽函数 | |
[slot] bool QWidget::close(); | |
// 点击按钮关闭窗口 | |
connect(ui->closewindow,&QPushButon::clicked,this,&MainWindow::close) |
connect () 操作一般写在窗口的构造函数中,相当于在事件发生之前 qt 框架先进行注册,在程序运行过程中,被点击,即会调用对应的槽函数
# 自定义槽函数
自定义需要的信号和槽
# 满足条件
- 编写新的类让其继承 qt 的某些标准类
- 新的子类必须从
QObject类
或者QObject子类
进行派生 - 在定义类的头文件中加入
Q_OBJECT宏
# 自定义信号
- 信号时类的成员函数,需要使用
signals关键字
进行声明
- 返回值
必
须是void类型
- 参数可以随意指定,信号也
支持重载
- 信号函数只需要生命,
不需要定义
(没有函数体实现) - 发送信号的本质就是
调用信号函数
,习惯在信号函数前加关键字emit
声明信号被发射了
//Qt 类想要使用信号槽机制必须要从 QObject 类派生 (直接派生或间接派生) | |
Class Test : public QObject | |
{ | |
Q_OBJECT | |
signals: | |
void testsignal(); | |
// 参数的作用是数据传递,调用信号函数需要指定实参 | |
// 实参最终会被传递给槽函数 | |
void testsignal(int a); | |
} |
# 自定义槽
槽函数就是信号的处理动作,
# 注意事项
- 返回值必须是
void类型
- 槽也是函数,支持
重载
, - 连接的信号有
多少参数
,槽
函数也需要有多少个参数 (信号多的函数将会被忽略) Qt
中槽函数的类型可以是类的成员函数,全局函数,静态函数,lambda 表达式 (匿名函数)- 槽函数可以使用关键字 slots 声明:public slots,private slots (不能在类外调用),protected slots
女朋友饿了,于是带她去吃饭
// class GirlFriend | |
class GirlFriend : public QObject | |
{ | |
Q_OBJECT | |
public: | |
explicit GirlFriend(QObject *parent = nullptr); | |
signals: | |
void hungry(); // 不能表达出想要吃什么 | |
void hungry(QString msg); // 可以通过参数表达想要吃什么 | |
}; | |
// class Me | |
class Me : public QObject | |
{ | |
Q_OBJECT | |
public: | |
explicit Me(QObject *parent = nullptr); | |
//explicit 防止隐式类型转换,对应的函数不能使用隐式类型转换 | |
public slots: | |
// 槽函数 | |
void eatMeal(); // 不能知道信号发出者要吃什么 | |
void eatMeal(QString msg); // 可以知道信号发出者要吃什么 | |
}; |
隐式类型转换
Class Test | |
{ | |
public: | |
Test(char * str); | |
Test(int a); | |
private: | |
char* m_ste; | |
int size; | |
} | |
// 在调用时可以使用 | |
Test str = "abc"; // 会传递成功 | |
Test str1 = 10; // 也会传递成功,但是对用户端存在向 str1 不清楚的问题,当 int 前的构造函数加上 explict 就只能使用下面的方法传递参数,此方法将报错。 | |
Test str1(10); |
# 信号槽的扩展
一个信号可以连接多个槽函数,发送一个信号有多个处理动作,需要写
多个
connect 连接,槽函数的执行顺序是随机的,
// 建立连接 | |
connnect | |
// 断开连接 | |
disconnect |
# Lambda 表达式
匿名
表达式
[capture](params) opt -> ret{body;}; |
- capture :捕获列表
- params:参数列表
- opt : 函数选项
- ret : `返回值`类型
- body : 函数体
- 捕获列表:捕获一定范围内的变量
[]
不捕获任何变量[&]
捕获外部作用域的所有变量,并作为引用在函数体内使用 (按引用捕获)[=]
捕获外部作用域的所有变量,并作为副本在函数体内使用 (按值捕获),拷贝的副本在匿名函数体内是只读的
[=,&foo]
: 按值捕获外部作用域的所有变量,并按照引用捕获外部变量 foo[bar]
按引用捕获 bar 不变量,同时不捕获其他变量[this]
捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员 函数同样的访问权限,如果已经使用了 & 或者 = 默认添加此功能
- 参数列表:和普通参数相同
opt
可以省略mutable
: 可以修改按值
传递进来的拷贝 (注意是能修改拷贝,而不是值本身)exception
:指定
函数抛出的异常
,如抛出整数类型的异常,可以使用 throw ()
- 返回值类型:
- 标识函数返回值的类型,当返回值为
void
,或者函数体只有一处 return 的地方 (编译器可以自动判断出返回值类型),这部分可以省略
- 标识函数返回值的类型,当返回值为
- 函数体:函数的实现
# 定义与调用
// 匿名函数定义,执行这个匿名函数不会被调用 | |
[]() | |
{ | |
qDebug() <<"lambda表达式"; | |
} | |
// 匿名函数定义 + 调用 | |
int ret = [](int a) -> int | |
{ | |
return a+1; | |
}(100); //100 是传递给匿名函数的参数 |
# 捕获
列表案例
// 在匿名函数外部定义变量 | |
int a=100, b=200, c=300; | |
// 调用匿名函数 | |
[](){ | |
// 打印外部变量的值 | |
qDebug() << "a:" << a << ", b: " << b << ", c:" << c; //error, 不能使用任何外部变量 | |
} | |
[&](){ | |
qDebug() << "hello, 我是一个lambda表达式..."; | |
qDebug() << "使用引用的方式传递数据: "; | |
qDebug() << "a+1:" << a++ << ", b+c= " << b+c; | |
}(); | |
// 值拷贝的方式使用外部数据 | |
[=](int m, int n)mutable{ | |
qDebug() << "hello, 我是一个lambda表达式..."; | |
qDebug() << "使用拷贝的方式传递数据: "; | |
// 拷贝的外部数据在函数体内部是只读的,如果不添加 mutable 关键字是不能修改这些只读数据的值的 | |
// 添加 mutable 允许修改的数据是拷贝到函数内部的副本,对外部数据没有影响 | |
qDebug() << "a+1:" << a++ << ", b+c= " << b+c; | |
qDebug() << "m+1: " << ++m << ", n: " << n; | |
}(1, 2); |
# 实例
- 在
UI
界面建立一个QPushButton
,命名为close
- 定义按钮
数字++
命名为num
- 定义
取消按钮数字++
命名为close_num
- 新建
myclass1
类,属于QObject
建立信号
signals: // 信号, | |
void MySignal(void); // 不需要实现,调用信号使用 emit 信号函数即可 | |
void Num(int& num); // 信号函数实现数值 + |
- 新建
myclass2类
,属于QObject
建立槽
函数
public slots: | |
void MySlot(void); // 槽函数,需要实现 | |
void NumSlot(int& num); // 信号函数实现数值 + | |
// 函数实现 | |
void MyClass2::MySlot() | |
{ | |
// 调用 MySignal 槽函数实现 | |
qDebug()<<"调用了MysSignal槽函数"; | |
} | |
void MyClass2::NumSlot(int& num) | |
{ | |
num ++; | |
qDebug()<<"num: "<<num; | |
} |
主函数
实现
// 头文件 mainwindow.h | |
#ifndef MAINWINDOW_H | |
#define MAINWINDOW_H | |
#include <QMainWindow> | |
#include "myclass.h" | |
#include "myclass2.h" | |
namespace Ui { | |
class MainWindow; | |
} | |
class MainWindow : public QMainWindow | |
{ | |
Q_OBJECT | |
public: | |
explicit MainWindow(QWidget *parent = nullptr); | |
~MainWindow(); | |
private slots: | |
void on_num_clicked(); | |
void on_close_num_clicked(); | |
private: | |
Ui::MainWindow *ui; | |
MyClass * mc1; | |
MyClass2 * mc2; | |
int num =0; | |
}; | |
#endif // MAINWINDOW_H | |
// 函数实现 | |
// mainwindow.cpp | |
#include "mainwindow.h" | |
#include "ui_mainwindow.h" | |
#include <QLabel> | |
#include <QPushButton> | |
MainWindow::MainWindow(QWidget *parent) : | |
QMainWindow(parent), | |
ui(new Ui::MainWindow) | |
{ | |
ui->setupUi(this); | |
// 锁定全局大小不可放大 | |
//setFixedSize(400,300); | |
// 设置标题 | |
setWindowTitle("My first No UI"); | |
mc1 = new MyClass(this); | |
mc2 = new MyClass2(this); | |
//connect 连接信号与槽 | |
// 一个信号可以关联多个槽,多个槽可以被多个信号关联 | |
connect(ui->close,&QPushButton::clicked,mc2,&MyClass2::MySlot); // 调用 | |
connect(mc1,&MyClass::MySignal,mc2,&MyClass2::MySlot); | |
connect(mc1,&MyClass::Num,mc2,&MyClass2::NumSlot); | |
connect(ui->num,&QPushButton::clicked,mc1,&MyClass::MySignal); // 当点击数字信号发出,同时调用另一个信号 | |
//label 标签 | |
QLabel *label = new QLabel(this); // 创建 label 是带 this 参数,即显示到当前主页面 | |
label->setText("你好呀!"); | |
label->move(200,100); // 移动位置,以左上角为起点 | |
emit mc1->MySignal(); | |
// 或者使用 | |
// label->setParent(this); | |
// 锁定 this 窗口的大小,不能放大 | |
//button 标签 | |
QPushButton* button = new QPushButton(this); | |
button->setText("点击"); | |
button->move(200,150); | |
} | |
MainWindow::~MainWindow() | |
{ | |
delete ui; | |
} | |
void MainWindow::on_num_clicked() | |
{ | |
emit mc1->Num(num); // 当点击 num 之后,调用 mc1 函数的 Num 信号,并传递 num 数值 | |
} | |
void MainWindow::on_close_num_clicked() | |
{ | |
// 当点击 close_num 取消数字关联,参数和 connect 内容相同 | |
disconnect(mc1,&MyClass::Num,mc2,&MyClass2::NumSlot); | |
} |
# 参考资料
- 爱编程的大丙
- 嵌入式技术公开课
Qt5~8