当按钮检测到自己被点击,它就发出一个信号
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声明 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
//断开连接
disconnectLambda表达式
匿名表达式
[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
