信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现,并不是 GoF 经典的观察者模式的实现方式。)。换句话说,信号=短信,槽函数=接受短信的手机。
一、使用内部槽函数
直接新建一个窗体程序,不需要空白。
为了体验一下信号槽的使用,我们以一段简单的代码说明:
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked,&app, &QApplication::quit);
button.show();
return app.exec();
}
信号与槽函数连接使用connect()函数.
原型:
QObject::connect(const QObject * sender,const char * signal, const QObject * receiver, const char * method)
参数解析:
sender:发出信号的对象
signal:发送对象发出的信号
receiver:接收信号的对象
method (slot):接收对象在接收到信号之后所需要调用的函数
注意点:
- 槽函数:任意的成员函数,普通全局函数,静态函数
- 槽函数需要和信号一致(参数,返回值)
void signal(int ,double,char)
void slot(int ,double,char)
3. 信号和槽是没有返回值,但是可以有参数
4.信号和槽的参数个数可以不一样
a) 信号的参数个数可以大于槽函数参数的个数
b) 槽函数个数不可以大于信号参数个数
5. 信号和槽函数可以使用QT内部定义好的,也可以自定义
如果信号槽不符合,或者根本找不到这个信号或者槽函数,比如我们改成:
connect(&button, &QPushButton::clicked, &QApplication::quit2);
由于 QApplication 没有 quit2 这样的函数,因此在编译时会有编译错误:
'quit2' is not a member of QApplication
这样,使用成员函数指针我们就不会担心在编写信号槽的时候出现函数错误。
前章学习了按钮控件,本次小小地运用以下。
说明:新建三个按钮控件,B1、B2和B3。点击按钮B1,按钮自动B2隐藏。点击按钮B3,窗口自动关闭。
在widget.h中,新建按钮控件对象:
#include <QPushButton>
QPushButton B1;
QPushButton *pB2;
QPushButton B3;
注意:不要在widget.cpp中创建。
原因:如果在widget.cpp中创建,那么此时B1,pB2,B3都将是局部变量,而不是全局变量。在main.cpp中可以看到 widget w;w.show();。本人是利用w的构造函数来初始化并操作。如果在构造函数中B1,pB2,B3都被释放了。那么在main.cpp中的w.show()将没有东西显示。
初始化按钮控件:
B1.setParent(this);
B1.setText("B1");
B1.move(100,100);
pB2=new QPushButton(this);
pB2->setText("B2");
pB2->move(200,100);
B3.setParent(this);
B3.setText("B3");
B3.move(300,100);
信号与槽函数的连接
connect(&B1,&QPushButton::pressed,pB2,&QPushButton::hide);
connect(&B3,&QPushButton::released,this,&QWidget::close);
解析:
信号由B1发出,发出Pressed(压下)信号,被pB2(按钮B2)接受,同时相应hide(隐藏)事件。
同理,信号由B3发出,发出released(弹起)信号,被this(窗口)接受,同时相应close(关闭)事件。
其中,信号pressed,released可以通过QpushButton来查询,即点击QpushButton按下F1,找基类,找信号。步骤如下:
光标放在QpushButton处按下F1,只有右边分栏,再按一下F1。
两次F1后,发现没有右侧没有Signal的介绍。此时找其基类,左边Inherits: QAbstractButton.点击进去。
点击Inherits: QabstractButton,可以找到Signal,再点击进去。
进去后 就可以找到有哪些信号了
二、人为定义槽函数
使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。仔细看上面的注意点,信号与槽函数。
connect(&B1,&QPushButton::pressed,this,&widget::MySlot);
void MySlot()
{
B1.setText("自创B3");
B1.move(100,200);
}
说明:B1发出信号,窗口接受,如果B1被按下后,触发槽函数MySlot,将B1的内容由“B1”修改成“自创B3”。MySlot是widget类的成员函数。
自定义信号槽需要注意的事项:
1.发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
2.使用 signals 标记信号函数,信号是一个函数声明,返回 void,不需要实现函数代码;
3.槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
4.使用 emit 在恰当的位置发送信号;
5.使用QObject::connect()函数连接信号和槽。
6.任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
信号槽的更多用法:
1.一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
2.多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
3.一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
4.槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
5.使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
我们的代码可以写成下面这样:
QObject::connect(&newspaper, static_cast<void (Newspaper:: *)
(const QString &)>(&Newspaper::newPaper),
[=](const QString &name)
{ /* Your code here. */ }
);
在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。
源代码:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPushButton>
namespace Ui {
class widget;
}
class widget : public QWidget
{
Q_OBJECT
public:
explicit widget(QWidget *parent = 0);
~widget();
void MySlot()
{
B1.setText("自创B3");
B1.move(100,200);
}
private:
Ui::widget *ui;
QPushButton B1;
QPushButton *pB2;
QPushButton B3;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
widget::widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::widget)
{
ui->setupUi(this);
//按钮初始化
B1.setParent(this);
B1.setText("B1");
B1.move(100,100);
pB2=new QPushButton(this);
pB2->setText("B2");
pB2->move(200,100);
B3.setParent(this);
B3.setText("B3");
B3.move(300,100);
/*
原型:
QObject::connect(const QObject * sender,
const char * signal, const QObject * receiver, const char * method,
*/
//信号与槽函数的连接
connect(&B1,&QPushButton::pressed,pB2,&QPushButton::hide);
connect(&B3,&QPushButton::released,this,&QWidget::close);
//connect(&B1,&QPushButton::pressed,pB2,&QPushButton::close);
//上面两者等效,因为QpushButton继承Qwidget。
//connect(&B1,SIGNAL(released()),this,SIGNAL(buttonclick()));
//人为定义槽函数
/*
Qt5:任意的成员函数,普通全局函数,静态函数
槽函数需要和信号一致(参数,返回值)
由于信号都是没有返回值,所以,槽函数一定没有返回值
*/
//connect(&B1,&QPushButton::pressed,this,&widget::MySlot);
}
widget::~widget()
{
delete ui;
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
widget w;
w.show();
return a.exec();
}