信号与槽作为QT的核心机制在QT编程中有着广泛的应用,本章介绍了信号与槽的一些基本概念以及在实际使用过程如何使用。通过两个同步的拨盘的例子介绍信号和槽的使用。下面分别说一下两个基本概念。
信号
当某个信号他的所有者发生的内部状态发生改变,信号被一个对象发射。信号只有在定义过这个信号的类及其派生类能够发射这个信号。当一个信号被发射时,与其相关联的槽将被立刻执行,就像直接调用这个函数。信号 - 槽机制完全独立于任何 GUI 事件循环。只有当所有的槽返回以后发射函数(emit)才返回。 如果存在多个槽与某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地执行,但是它们执行的顺序将会是随机的、不确定的。
信号的声明是在头文件中进行的,Qt 的 signals 关键字指出进入了信号声明区,随后即可声明自己的信号。例如,下面定义了三个信号:
signals: void mySignal(); void mySignal(int x); void mySignalParam(QString str,int y); |
在上面的定义中,signals 是 QT 的关键字,而非 C/C++ 的。接下来的一行 void mySignal() 定义了信号 mySignal,这个信号没有携带参数;接下来的一行 void mySignal(int x) 定义了重名信号 mySignal,但是它携带一个整形参数,这有点类似于 C++ 中的虚函数。从形式上讲信号的声明与普通的 C++ 函数是一样的,但是信号却没有函数体定义,另外,信号的返回类型都是 void,不要指望能从信号返回什么有用信息。
注意:信号一定没有实现。
槽
槽是普通的 C++ 成员函数,可以被正常调用,它们唯一的特殊性就是很多信号可以与其相关联。当与其关联的信号被发射时,这个槽就会被调用。槽可以有参数,但槽的参数不能有缺省值。
既然槽是普通的成员函数,因此与其它的函数一样,它们也有存取权限。槽的存取权限决定了谁能够与其相关联。同普通的 C++ 成员函数一样,槽函数也分为三种类型,即 public slots、private slots 和 protected slots。
- public slots:在这个区内声明的槽意味着任何对象都可将信号与之相连接。这对于组件编程非常有用,你可以创建彼此互不了解的对象,将它们的信号与槽进行连接以便信息能够正确的传递。
- protected slots:在这个区内声明的槽意味着当前类及其子类可以将信号与之相连接。这适用于那些槽,它们是类实现的一部分,但是其界面接口却面向外部。
- private slots:在这个区内声明的槽意味着只有类自己可以将信号与之相连接。这适用于联系非常紧密的类。
槽也能够声明为虚函数,这也是非常有用的。
信号与槽的关联
通过调用 QObject 对象的 connect 函数来将某个对象的信号与另外一个对象的槽函数相关联,这样当发射者发射信号时,接收者的槽函数将被调用。该函数的定义如下:
bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * member ) [static] |
示例程序
我们首先创建一个Gui程序,继承自QDialog,然后设计界面,拖动两个Dial和一个Horizontal Scroll Bar。我们创建这个程序要实现的就是三个控制能够同步动作。
我们在Dialog类的构造函数中添加信号和槽的连接,操作很简单,如下所示
connect(ui->dial,SIGNAL(valueChanged(int)),ui->dial_2,SLOT(setValue(int)));
connect(ui->dial,SIGNAL(valueChanged(int)),ui->horizontalSlider,SLOT(setValue(int)));
connect(ui->dial_2,SIGNAL(valueChanged(int)),ui->dial,SLOT(setValue(int)));
connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),ui->dial,SLOT(setValue(int)));
这样运行程序,操作任何一个控件,另两个就会跟着动作了。
下面我们自己定义一个信号和槽。
signals:
void mySignal(int);
private:
Ui::Dialog *ui;
private slots:
void my_slot(int);
下面我们把我们刚才的几个连接注释掉,然后使用自己的信号和槽建立中间的连接。通过水平条控制dial的动作。
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// connect(ui->dial,SIGNAL(valueChanged(int)),ui->dial_2,SLOT(setValue(int)));
// connect(ui->dial,SIGNAL(valueChanged(int)),ui->horizontalSlider,SLOT(setValue(int)));
// connect(ui->dial_2,SIGNAL(valueChanged(int)),ui->dial,SLOT(setValue(int)));
// connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),ui->dial,SLOT(setValue(int)));
connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SLOT(my_slot(int)));
connect(this,SIGNAL(mySignal(int)),ui->dial,SLOT(setValue(int)));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::my_slot(int a){
emit mySignal(a);
}
上面的实现是通过水平条的变化连接到Dialog的my_slot(int a),然后在槽函数中发射一个信号,然后再将这个信号和dial的setval()槽函数相连,这样同样能实现我们上面的联动。
其实,不光信号和槽能够连接,信号和信号也能够连接。
下面我们再稍加改动,让信号和信号相连。看一下效果。注释掉上面的代码,然后重新建立如下的连接。能够实现相同的功能。
connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),this,SIGNAL(mySignal(int)));
connect(this,SIGNAL(mySignal(int)),ui->dial,SLOT(setValue(int)));
这一节实现起来很简单,这个例子只是给大家演示如何使用信号和槽,主要是理解信号和槽的概念。下一节讲解如何使用SignalMapper,敬请关注。