信号与槽
1. 说明
概念:信号和槽,是Qt对象间通信的接口。信号Signal、槽Slot,分别对应着事件的发送端和接受端。
语法:
connect( 信号的发送者, 发送的具体信号, 信号的接收者, 信号的处理(槽) );
信号槽的优点:松散耦合。信号发送端 和 接收端 本身是没有关联的,通过 connect连接 将两端耦合在一起。
事件的方法:查看 要操作对象的父类:QPushButton、QWidget,查询帮助手册的 Signals、Slots 事件
2. 用法示例
示例需求:点击按钮时,可以关闭掉窗口。
1)新建工程,窗口类的名称为 MainWidget。
2)代码
mainwidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QPushButton> class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = nullptr); ~MainWidget(); private: QPushButton btn1; QPushButton *btn2; }; #endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h" MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { btn1.setParent(this); btn1.setText("close"); btn1.resize(50, 50); btn1.move(100,100); btn2 = new QPushButton; btn2->setParent(this); btn2->setText("abc"); btn2->resize(50, 50); btn2->move(0, 100); this->resize(320, 240); connect( &btn1, &QPushButton::clicked, this, &QWidget::close ); /* * &b1:信号发出者,指针类型 * &QPushButton::clicked:处理的信号。 &发送者的信号::信号名称 * this:信号接收者 * &QWidget::close:槽函数,信号处理函数 */ } MainWidget::~MainWidget() { }
3)实验现象:点击close按钮会关闭窗口,点击abc无反应。
自定义信号和槽
类比软件中断和中断响应。
1. 自定义槽
自定义槽(基本等于普通函数的用法)
- Qt5的槽函数可以是:任意的成员函数,普通全局函数,静态函数
- 槽函数需要和信号一致(参数,返回值)
- 由于信号都是没有返回值,所以,槽函数也一定没有返回值
示例:创建一个工程,点击按键,按键显示变为 " Hello! "。用自定义槽的方法实现。
1)新建工程,窗口类的名称为 MainWidget。
2)代码
mainwidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QPushButton> class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = nullptr); ~MainWidget(); void MySlot(); private: QPushButton btn1; QPushButton *btn2; }; #endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h" MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { btn1.setParent(this); btn1.setText("close"); btn1.resize(50, 50); btn1.move(100,100); btn2 = new QPushButton; btn2->setParent(this); btn2->setText("abc"); btn2->resize(50, 50); btn2->move(0, 100); this->resize(320, 240); connect( &btn1, &QPushButton::clicked, this, &MainWidget::close ); /* * &b1:信号发出者,指针类型 * &QPushButton::clicked:处理的信号。 &发送者的信号::信号名称 * this:信号接收者 * &QWidget::close:槽函数,信号处理函数 */ /* * 自定义槽(基本等于普通函数的用法) * Qt5的槽函数可以是:任意的成员函数,普通全局函数,静态函数 * 槽函数需要和信号一致(参数,返回值) * 由于信号都是没有返回值,所以,槽函数一定没有返回值 */ connect( btn2, &QPushButton::clicked, this, &MainWidget::MySlot ); connect( btn2, &QPushButton::clicked, &btn1, &QPushButton::hide );// 隐藏 } void MainWidget::MySlot() { btn2->setText("Hello!"); } MainWidget::~MainWidget() { }
3)现象:
点击 " close ",关闭窗口。
点击 " abc ",此按钮显示文字变为 " Hello! ",并且会隐藏 " close "。
2. 自定义信号
自定义信号:
- 信号必须有signals关键字来声明。
- 信号没有返回值,但可以有参数。
- 信号就是函数的声明,只需声明,无需定义。
- 使用方法:emit 函数名();
示例需求:总共有两个窗口,一父窗口一子窗口,每次只显示一个。每个界面上都有一个 "切换窗口" 按钮,两个窗口通过按按键来回切换。(通过隐藏自己,显示另一个窗口来实现)
要求:(子窗口的属性为pritive,主窗口不能直接调用,要通过将按键事件包装成信号,与父窗口的槽形成关联)
1)新建一个窗口,新建类:工程,右键 - Add New...,选择 C++ Class。
2)代码:
subwidget.h
#ifndef SUBWIDGET_H #define SUBWIDGET_H #include <QWidget> #include <QPushButton> class SubWidget : public QWidget { Q_OBJECT public: explicit SubWidget(QWidget *parent = nullptr); void sendSlot(); signals: /* * 信号必须有signals关键字来声明 * 信号没有返回值,但可以有参数 * 信号就是函数的声明,只需声明,无需定义 * 使用:emit mySignal(); */ void mySignal(); private: QPushButton btn1; }; #endif // SUBWIDGET_H
subwidget.cpp
#include "subwidget.h" SubWidget::SubWidget(QWidget *parent) : QWidget(parent) { btn1.setParent(this); btn1.setText("切换窗口"); btn1.resize(80, 50); btn1.move(20, 20); setWindowTitle("02"); resize(200, 200); // 没有show,由MainWidget中的按钮触发 connect( &btn1, &QPushButton::clicked, this, &SubWidget::sendSlot); //直接这样写也是可以的,程序同样可以正常运行,emit本来就是一个空的宏定义,# define emit //connect( &btn1, &QPushButton::clicked, this, &SubWidget::mySignal); } void SubWidget::sendSlot() { emit mySignal(); }
mainwidget.h
#ifndef SUBWIDGET_H #define SUBWIDGET_H #include <QWidget> #include <QPushButton> class SubWidget : public QWidget { Q_OBJECT public: explicit SubWidget(QWidget *parent = nullptr); void sendSlot(); signals: /* * 信号必须有signals关键字来声明 * 信号没有返回值,但可以有参数 * 信号就是函数的声明,只需声明,无需定义 * 使用:emit mySignal(); */ void mySignal(); private: QPushButton btn1; }; #endif // SUBWIDGET_H
mainwidget.cpp
#include "mainwidget.h" MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { btn1.setParent(this); btn1.setText("close"); btn1.resize(50, 50); btn1.move(100,100); btn2 = new QPushButton; btn2->setParent(this); btn2->setText("abc"); btn2->resize(50, 50); btn2->move(0, 100); btn3.setParent(this); btn3.setText("切换窗口"); btn3.resize(80, 50); btn3.move(100, 0); resize(320, 240); setWindowTitle("01"); connect( &btn1, &QPushButton::clicked, this, &MainWidget::close ); /* * &b1:信号发出者,指针类型 * &QPushButton::clicked:处理的信号。 &发送者的信号::信号名称 * this:信号接收者 * &QWidget::close:槽函数,信号处理函数 */ /* * 自定义槽(基本等于普通函数的用法) * Qt5的槽函数可以是:任意的成员函数,普通全局函数,静态函数 * 槽函数需要和信号一致(参数,返回值) * 由于信号都是没有返回值,所以,槽函数一定没有返回值 */ connect( btn2, &QPushButton::clicked, this, &MainWidget::mySlot ); connect( btn2, &QPushButton::clicked, &btn1, &QPushButton::hide );// 隐藏 // 点击01窗口的"切换窗口",会显示02窗口,并将01隐藏 connect( &btn3, &QPushButton::clicked, this, &MainWidget::changeWin ); // 点击02窗口的"切换窗口",会显示01窗口,并将02隐藏 connect( &w2, &SubWidget::mySignal, this, &MainWidget::dealSub ); // 因为main里已经有show了,这里可以不写show。 } void MainWidget::mySlot() { btn2->setText("Hello!"); } void MainWidget::changeWin() { // 本窗口隐藏 this->hide(); // 子窗口显示 w2.show(); } void MainWidget::dealSub() { // 子窗口隐藏 w2.hide(); // 本窗口显示 this->show(); } MainWidget::~MainWidget() { }
3)现象:每次只显示一个窗口,点击切换窗口,会在两个窗口间切换。
注:一个信号可以关联多个槽,不过不建议这么做,不利于后期阅读。尽量只关联一个槽函数,在槽函数中去实现。
注:emit其实是空宏定义,而且实际代码中,即使发送信号不加emit,程序依然可以正常运行。详细说明:https://blog.csdn.net/qq_27312943/article/details/50789914
不过估计这个宏还是有意义的,应该在qmake源码里,只不过我们看不到它根据这个宏做了哪些处理