信号与槽的主要实现的功能是对界面中按钮进行定义,比如主界面中有个按钮pushbutton,并设置其显现名称为“计算”,那需要定义一个当这个按钮被点击时的功能,这个功能称之为槽,全称是槽函数,可见槽就是一种函数;点击的操作称之为信号。信号与槽可理解为,当信号为触发时,将自动执行与该信号相关联的槽函数。这其实很好理解,也很常见。有过VB程序设计基础(或者matlab中的GUI设计)的同学应该知道,每个按钮都有一个callback,应该和这里的信号与槽是类似的。《Qt Creator快速入门》第2版一书中提到这二者之间的差别。
信号与槽的实现有两种方式:
Qt中有很多函数是很特殊的,主要体现在可以通过函数名来判断其功能,比如信号与槽的自动关联函数。在某个按钮上右击,选择“转到槽”,选择触发信号(一般为clicked(),即点击),然后就可以自动转到槽函数进行定义了。非常的方便。这里槽函数的之所以能够和信号关联起来,完全是靠槽函数的命名,虽然这是程序自动完成的。
void Widget::on_pushButton_clicked()
{
//在这里定义槽要实现的功能
}
这是一个自动关联的槽函数,其命名方式为:on_部件对象名_信号名()。当然,作为一个函数,也需要进行声明,Qt已经自动在头文件中进行了声明:
private slots:
void on_pushButton_clicked();
如果部件对象名称发生改变,相应的声明和定义都要进行更改,当然,可以采用如下的方式:在函数名on_pushButton_clicked()上右击,选择Refactor->Rename Symbol Under Cursor进行替换,或者用快捷键Ctrl+Shift+R亦可。
这种自动关联的方式非常的方便,适合用过VB和MATLAB GUI的用户,跟上述的思路一模一样,但是这种方式有以下几种局限性:
首先确认一点,手动关联比自动关联要复杂一些,但是实现的功能要多,也就是说自动关联一定可以通过手动关联来实现。手动关联的基本格式是:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
其中 sender 与 receiver 是指向对象的指针,SIGNAL() 与 SLOT() 是转换信号与槽的宏。
现在我们将上述的自动关联的方式改为手动关联的方式:
1.在头文件中添加声明:
private slots:
void function_of_pushButton();//此时,函数的命名是任意的,尽量做到顾名思义就好
2.在源文件中添加定义:
void Widget::function_of_pushButton()
{
//在这里定义槽要实现的功能
}
3.在构造函数中添加关联:
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(function_of_pushButton()));
可以看出,虽然手动关联的代码只多了一行(第3步),但是添加声明确需要程序员自己敲(声明后,光标停在在函数名上,按下Alt+Enter,选择定义函数的位置(一般为在XXX.cpp中添加定义),就可以自动跳转到槽,进行定义了,省去了敲击第2步中的代码。
另外,需要注意的是,由于clicked()是Qt提供的信号,这里并没有声明信号。注意,信号只需要声明,不需要定义。
总之,在这种同级的界面下,进行信号与槽的操作还是自动关联比较方便。为了完整的体现信号与槽的在不同界面下的信号的传递,通过下面的程序说明一下。
程序设计如下:
主界面的基类为QWidget,上面有一个label;子界面的基类是QDialog,上面有个spin box获取一个整数,和一个pushbutton,当该按钮被点击后,主界面的label显示了子界面spin box的值。
界面如下图所示:
类的命名如下图:
基本思路为:
1. 当dialog中的按钮被点击后将获取的值发射到主界面widget的槽函数,注意这部操作,“按钮点击就发射信号”这本身就是就是一个信号与槽的操作,信号是:按钮被点击,槽:发射信号。但这里的信号与槽是都是在dialog同一个类中,数据可以访问,所以直接用自动关联的方式最方便,在设计界面,右击按钮->转到槽,然后定义发射信号的操作,槽的定义如下:
void ww_Dialog::on_pushButton_clicked()
{
int val=ui->spinBox->value();
emit getdata(val);//信号要在头文件中声明
close();
}
信号的声明如下(在ww_dialog.h中):
signals:
void getdata(int);
注意信号不需要定义。可以将信号理解为当信号出现时,自动执行槽函数的一个标识,同时起到参数传递的作用。比如点击clicked(),再比如这里的emit getdata(val),说明getdata()这个信号被执行了,同时将参数val传递给槽函数。经测试,没有emit 也具有相同的功能(版本Qt5.7),所以emit的作用还需要进一步分析。
2. 定义主界面的槽函数为:
void ww_Widget::showdata(int val)
{
ui->label->setText(tr("the data is %1").arg(val));
}
3.在主界面的构造函数中,将界面联系起来。即新建dialog对象,并将其显示出来;同时,将上述的dialog的信号与widget的槽函数联系起来,具体如下:
ww_Dialog *dialog=new ww_Dialog(this);//新建对话框
connect(dialog,SIGNAL(getdata(int)),this,SLOT(showdata(int)));//关联信号与槽
dialog->show();//对话框默认显示
这里包含参数传递的信号与槽的操作是这么实现的: