Qt信号与槽的使用
参考视频:黑马程序员https://www.bilibili.com/video/BV1XW411x7NU?p=4
1 新建工程
先创建一个控件基础工程,创建后的界面如下:
主函数我们不需要修改,就保持这样,对于C++的知识我不太理解。
上述代码中,执行到第7行的时候,会先去执行基类的构造函数,再执行MyWidget类的构造函数。我们只需要在构造函数中实现需要实现的功能。
构造函数对应在mywidget.cpp中:
2 测试代码
实例一:标准的信号处理
测试目的:在主窗口中,新建两个按钮,功能如下:
按钮一的功能:按下按钮,关闭主窗口;
按钮二的功能:释放按钮,更改按钮二的文本,并隐藏按钮一。
由于在实现过程中,会用到信号与槽的知识,就先简单介绍一下基本知识,可能不正确,这只是我个人的理解:
信号:某一事件发生时产生,用于表示一个事件发生了。
槽:就是信号处理函数,用于指示当信号发生时,需要做出什么动作。
其中,我们connect函数来连接信号与槽之间的关系,函数的原型如下:
connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection);
举例说明:
connect(&b1, &QPushButton::pressed, this, &MyWidget::close);
/* &b1:信号发出者,指针类型
* &QPushButton::pressed:处理的信号,&发送者的类名::信号名字
* this:信号接收者
* &MyWidget::close:槽函数,信号处理函数 &接收的类名::槽函数名字
*/
不同的控件有哪些信号,可通过帮助文档查看,以QPushButton为例进行说明(点QPushButton,再按F1):
这里没有看到signal函数,可能是从它的父类继承过来的,我们再看一下它的父类:
实现的相关代码
先在MyWidget类中实现两个按钮(mywidget.h):
1 #ifndef MYWIDGET_H 2 #define MYWIDGET_H 3 4 #include <QWidget> 5 #include <QPushButton> 6 7 class MyWidget : public QWidget 8 { 9 Q_OBJECT 10 11 public: 12 MyWidget(QWidget *parent = 0); 13 ~MyWidget(); 14 15 private: 16 QPushButton b1; 17 QPushButton *b2; 18 19 void myslot(); 20 21 }; 22 23 #endif // MYWIDGET_H
再在构造函数中实现两个按钮的功能(mywidget.cpp):
1 #include "mywidget.h" 2 #include <QPushButton> 3 4 MyWidget::MyWidget(QWidget *parent) 5 : QWidget(parent) 6 { 7 b1.setParent(this); 8 b1.setText("按钮一"); 9 b1.move(100, 100); 10 11 b2 = new QPushButton(this); 12 b2->setText("按钮二"); 13 14 connect(&b1, &QPushButton::pressed, this, &MyWidget::close); 15 /* &b1:信号发出者,指针类型 16 * &QPushButton::pressed:处理的信号,&发送者的类名::信号名字 17 * this:信号接收者 18 * &MyWidget::close:槽函数,信号处理函数 &接收的类名::槽函数名字 19 */ 20 21 /* 22 * 自定义槽,普通函数的用法 23 * Qt5:任意的成员函数,普通全局函数,静态函数 24 * 槽函数需要和信号一致(参数、返回值) 25 * 由于信号都是没有返回值,所以槽函数一定没有返回值 26 */ 27 connect(b2, &QPushButton::released, this, &MyWidget::myslot); 28 29 connect(b2, &QPushButton::released, &b1, &MyWidget::hide); 30 /* 31 * 信号:短信 32 * 槽函数:接收短信的手机 33 */ 34 } 35 36 void MyWidget::myslot() 37 { 38 b2->setText("123"); 39 } 40 41 MyWidget::~MyWidget() 42 { 43 44 }
运行,进行测试:
实例二:自定义不带参数信号处理
目的:实现两个窗口,主窗口和子窗口,并且每个窗口都有一个按键,按键功能如下:
主窗口按键的功能:显示子窗口,关闭父窗口;
子窗口按键的功能:关闭子窗口,显示父窗口。
主窗口和子窗口的切换显示全部由主窗口控制,可以理解为:主窗口的权限大于子窗口,子窗口的按键按下去只是产生一个信号,主窗口再对信号进行处理。
新建一个工程,工程创建之后,有以下文件,其中,subwindown相关文件是通过点击signal_slot_2目录文件,选择添加新文件,选择C++ --> C++ Class,基类我们选择QWidget:
实现的代码如下:
widget.h是widget类所在同文件,主要实现:声明主窗口按键处理函数、声明子窗口信号接收处理函数、实例化一个subwindown类对象、实例化一个QPushButton类对象。
1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 #include <QPushButton> 6 #include "subwindown.h" 7 8 class Widget : public QWidget 9 { 10 Q_OBJECT 11 12 public: 13 Widget(QWidget *parent = 0); 14 ~Widget(); 15 16 void my_slot(); 17 void dealsub(); 18 19 private: 20 QPushButton b_main; 21 22 subwindown sw; 23 }; 24 25 #endif // WIDGET_H
subwindown.h是subwindown类所在头文件,主要实现:声明子窗口按键处理函数、声明一个信号、实例化一个QPushButton类对象。
1 #ifndef SUBWINDOWN_H 2 #define SUBWINDOWN_H 3 4 #include <QWidget> 5 #include <QPushButton> 6 7 class subwindown : public QWidget 8 { 9 Q_OBJECT 10 public: 11 explicit subwindown(QWidget *parent = nullptr); 12 //定义槽函数 13 void sendslot(); 14 15 signals: 16 /* 17 * 信号必须有signals关键字声明 18 * 信号没有返回值,但可以有参数 19 * 信号就是函数的声明,只需声明,无需定义 20 * 使用:emit mysignal(); 21 */ 22 void mysignal(); 23 24 public slots: 25 26 private: 27 QPushButton b_sub; 28 29 }; 30 31 #endif // SUBWINDOWN_H
main.cpp中的代码不改动,就使用生成的代码:
1 #include "widget.h" 2 #include <QApplication> 3 4 int main(int argc, char *argv[]) 5 { 6 QApplication a(argc, argv); 7 Widget w; 8 w.show(); 9 10 return a.exec(); 11 }
widget.cpp中实现的是主窗口的构造函数,主要实现:接收主窗口按键的信号并处理、接收子窗口发送的信号并处理。
1 #include "widget.h" 2 3 Widget::Widget(QWidget *parent) 4 : QWidget(parent) 5 { 6 setWindowTitle("主窗口"); 7 8 //设置按钮相关属性 9 b_main.setParent(this); 10 b_main.setText("切换子窗口"); 11 b_main.move(50, 50); 12 //处理主窗口的按钮所产生的信号 13 connect(&b_main, &QPushButton::clicked, this, Widget::my_slot); 14 15 //接收子窗口信号,并进行处理 16 connect(&sw, &subwindown::mysignal, this, &Widget::dealsub); 17 resize(400, 300); 18 } 19 20 void Widget::my_slot() 21 { 22 //显示子窗口 23 sw.show(); 24 //父窗口隐藏 25 this->hide(); 26 } 27 28 void Widget::dealsub() 29 { 30 //主窗口显示 31 this->show(); 32 //子窗口隐藏 33 sw.hide(); 34 } 35 36 Widget::~Widget() 37 { 38 39 }
subwindown.cpp中实现的是子窗口的构造函数,主要实现:接收子进程按键的信号并处理,处理方式为发送一个自定义信号。
1 #include "subwindown.h" 2 3 subwindown::subwindown(QWidget *parent) : QWidget(parent) 4 { 5 setWindowTitle("子窗口"); 6 7 //按键属性设置 8 b_sub.setText("切换主窗口"); 9 b_sub.setParent(this); 10 b_sub.move(50, 50); 11 12 //接收按键信号并处理 13 connect(&b_sub, &QPushButton::clicked, this, &subwindown::sendslot); 14 resize(400, 300); 15 } 16 17 void subwindown::sendslot() 18 { 19 //发送信号 20 emit mysignal(); 21 }
运行测试:
点击“切换子窗口”按键:
实例三:自定义带参数信号处理
功能:在空白窗口中定义一个按钮,点击按钮发送一个自定义的带参数的信号,然后由本窗口接收信号并将参数打印出来。
首先,创建一个控件基类工程,创建后包含的文件如下:
在widget.h中,实现的功能有:声明一个发送信号函数、声明一个接收信号函数、自定义一个带参数的信号、实例化一个QPushButton对象。
1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 #include <QPushButton> 6 7 class Widget : public QWidget 8 { 9 Q_OBJECT 10 11 public: 12 Widget(QWidget *parent = 0); 13 ~Widget(); 14 15 //点击按钮,发送信号函数 16 void send_signal(); 17 //接收信号处理函数 18 void recv_signal(int, QString); 19 20 signals: 21 //自定义一个带参数的信号 22 void mysignal(int, QString); 23 24 private: 25 QPushButton b_main; 26 27 }; 28 29 #endif // WIDGET_H
在widget.cpp中,实现的功能有:接收点击按键的信号并进行处理,接收自定义的信号并进行处理。
1 #include "widget.h" 2 #include <QDebug> 3 4 Widget::Widget(QWidget *parent) 5 : QWidget(parent) 6 { 7 b_main.setParent(this); 8 b_main.setText("发送"); 9 b_main.move(100, 100); 10 11 //点击按钮,发送信号 12 connect(&b_main, &QPushButton::clicked, this, &Widget::send_signal); 13 14 //接收点击按钮,发送的信号 15 void (Widget::*testsignal)(int, QString) = &Widget::mysignal; 16 connect(this, testsignal, this, &Widget::recv_signal); 17 18 this->resize(300, 300); 19 } 20 21 void Widget::send_signal() 22 { 23 emit mysignal(100, "我已经发送信号"); 24 } 25 26 void Widget::recv_signal(int a, QString str) 27 { 28 qDebug() << a << str; 29 } 30 31 Widget::~Widget() 32 { 33 34 }
运行,并进行测试,点击一次发送按钮,就会发送一个自定义信号,并接收到了它:
实例四:lambda表达式的使用
功能:创建一个按钮,点击按钮发送一个信号,不需要指定信号接收函数和信号接收者,直接使用匿名函数(lambda表达式)实现。
先新建一个工程,新建后的工程文件如下:
由于lambda是C++11支持的特性,所以我们需要在lambda_test.pro文件末尾添加一句:CONFIG += C++11
先把代码放上,再说明lambda表达式的作用。
widget.h文件:
1 #ifndef WIDGET_H 2 #define WIDGET_H 3 4 #include <QWidget> 5 #include <QPushButton> 6 7 class Widget : public QWidget 8 { 9 Q_OBJECT 10 11 public: 12 Widget(QWidget *parent = 0); 13 ~Widget(); 14 15 private: 16 //定义一个类中成员变量 17 int a = 10; 18 19 }; 20 21 #endif // WIDGET_H
widget.cpp文件:
1 #include "widget.h" 2 #include <QDebug> 3 4 Widget::Widget(QWidget *parent) 5 : QWidget(parent) 6 { 7 QPushButton *b_main = new QPushButton(this); 8 b_main->setParent(this); 9 b_main->setText("开始"); 10 b_main->move(100, 100); 11 12 //定义一个外部局部变量 13 int b = 12; 14 //Lambda表达式,匿名函数对象 15 //C++11增加的新特性,项目文件: CONFIG += C++11 16 //Qt配合信号一起使用,非常方便 17 connect(b_main, &QPushButton::clicked, 18 // =:把外部所有局部变量、类中所有成员以值方式传进来 19 // this:类中所有成员以值传递方式 20 // &:引用符号,外部所有局部变量 21 [=](bool isCheck)mutable 22 { 23 b_main->setText("lambda表达式"); 24 qDebug() << "已经进入lambda表达式"; 25 qDebug() << a << b; 26 a += 10; 27 b += 20; 28 qDebug() << isCheck; 29 } 30 ); 31 32 this->resize(300, 300); 33 } 34 35 Widget::~Widget() 36 { 37 38 }
下面就简单的说明lambda表达式的语法,我也不是很懂,根据测试结果进行说明。
参考了这篇博客:https://www.cnblogs.com/rainbow70626/p/10328143.html
格式:[capture](parameters) mutable ->return-type{statement};
capture:捕捉列表,也单独代表函数的开始,主要有以下使用方式:
- 单独传递外部变量,如:[b_main]
- =:把外部所有局部变量、类中所有成员以值方式传进来(widget.c和widget.h文件中的成员)
- this:类中所有成员以值传递方式(widget.h文件中的成员)
- &:引用符号,外部所有局部变量(widget.c文件中的成员),当变量是指针时,最好别用这个。
parameters:参数列表,信号的参数。
mutable :值传递时,默认变量为只读的,如果需要在匿名函数中改变它,需要这个参数
运行代码进行测试: