下面举叫外卖的例子来说明什么是信号和槽,比如:
①比如到午饭时间了,某宅男饿了——由不饿到饿,是一个状态的变化,肚子饿了就相当于是一个信号。谁都会饿的,每个人都可以发这类信号。注意信号只是一个空想,没 东西吃是填不饱肚子的。饿了怎么办,准备叫外卖。
②街上餐馆很多,都希望多做点生意,送外卖也是常事——做好饭送外卖就是槽函数。这个送外卖功能,餐馆一般都是有的,但谁来买送给谁,这个暂时定不了。如果餐馆饭 做得好,但没人吃那也是不行的。
③食客饿了(信号),餐馆有送饭服务(槽函数),二者怎么沟通呢?通常我们都是打电话,Qt 把这个过程叫信号和槽的关联(connect)。虽然我们每次叫外卖都要拨一长串号码,但 Qt 关联比我们打电话方便,它只需要将信号关联具体某家餐馆外卖服务一次,以后都是自动拨号的。
Qt 对象的信号和槽关联好之后,源头只需要发个信号,叫一声“我饿了”,connect 函数会自动拨号,餐馆立刻就送餐过来。信号和槽函数在进行关联的时候,二者的参数需要一致,不能我叫西红柿鸡蛋的盖浇饭,餐馆给送兰州拉面,那是不行的。多个对象的信号和槽函数在参数匹配的情况下,它 们之间的关联可以是一对一,一对多(某吃货可以同时叫多个餐馆的饭),多对一(多个人可以同时订某家餐馆的饭),所以关联是比较自由的。
槽函数的声明与类的成员函数声明一样,使用方式也一样,唯一不一样的就是在声明访问权限时需要加slots
标识符,如public slots: void FoodIsComing();
将 pushButton 的信号 clicked (即“我饿了”)与 主窗口的槽函数 FoodIsComing 关联起来,实现自动拨号叫外卖。Qt 通过 QObject::connect 函数完成信号和槽函数的关联,因为主窗口最顶层的基类是 QObject,所以我们下面代码不需要加 QObject:: 前缀。
connect 函数第一个参数是发信号的源头对象指针,按钮对象的指针就是 ui->pushButton,ui 是为窗体构建界面的辅助类对象指针,我们在窗体设计界面拖的控件对象都存在这个 ui 指向的对象里。ui->pushButton 就指向我们之前拖的按钮对象。因为通过设计模式拖的控件全部是以指针类型访问的,所以以后说到窗体里的控件,一般都是说它的指针名字。
第二个参数用 SIGNAL 宏包裹,里面是按钮对象的信号 clicked() ,信号的声明和成员函数类似,但必须放在 signals 声明段落。上面没看到 signals 声明段落是因为 QPushButton 类的对象自带这个信号,不需要我们来定义。
第三个参数是接收对象的指针,也就是服务提供方,是槽函数所在对象的指针,我们上面用的 this 指针就是主窗体自己。
第四个参数是接收对象里的槽函数,并用 SLOT 宏封装起来。
connect 函数意义是非常清晰的,将源头和源头的信号,关联到接收端和接收端的槽函数。注意源头和接收端必须是存在的实体对象指针,不能是野指针。connect 函数必须放在 ui->setupUi 之后,否则控件指针是未定义的野指针,那种关联必然失败,会导致程序崩溃。
Widget::Widget(QWidget *parent) :
QWidget(parent), ui(new Ui::Widget)
{ ui->setupUi(this);
//添加关联代码,必须放在 setupUi 函数之后
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(FoodIsComing()));
}