您的位置:首页 > 编程语言 > Qt开发

Qt:信号与槽(Signals and Slots) 上

2010-03-30 15:19 225 查看
英文原文:http://qt.nokia.com/doc/4.6/signalsandslots.html

 

信号与槽用于对象间通信,它是Qt的核心特性,也是区别去其他框架特性的部分。

 

引言

在GUI编程中,当一个Widget改变时,我们通常希望通知另一个Widget。更一般的情形,我们想要一个对象和另一个对象可以通信。比如,一个用户点了关闭按钮,我们希望调用窗口的close()方法。

过去使用回调技术(callback)完成这样的通信。回调其实是一个函数指针,如果你想进程函数通知你某个事件(event),你要把函数指针作为参数传给进程函数。进程函数在适当的时候调用回调。回调有两个主要的缺点:一个是他们不是类型安全的,我们不能保证进程函数调用回调时使用正确的参数。另一个是回调和进程函数是强耦合,因为进程函数必须知道要调用哪个回调。

 

信号与槽

在Qt中,我们用另一种技术替代回调技术:信号与槽。一个特定的事件发生时会发出一个信号,Qt的Widget有许多预定义的信号,我们可以通过继承Widget,在子类中添加自定义的信号。一个槽是一个函数,用于响应一个特定的信号。Qt的Widget有许多预定义的槽,同样我们可以通过继承Widget,在子类中添加自定义的槽来处理我们感兴趣的信号。

 



 

信号与槽机制是类型安全的:一个信号的签名必须和接收槽的签名匹配(槽的签名要少于它接收信号的签名)。因为签名是可兼容的,编译器会帮助检查类型的不匹配。信号与槽是松散耦合:一个类可以在不知道接收信号的槽的情况下发出一个信号。如果你连接一个槽和一个信号,Qt的信号与槽机制可以确保信号发出后槽被正确调用。信号与槽可以有任意类型任意数量的参数。他们是完全类型安全的。

 

所有继承自QObject或任一QObject的子类(如Widget)的类可以包含信号与槽。对象的状态改变时会发出信号,在某种程度上,发出的信号可能对其他对象有吸引力。所有的对象都是这样通信。发出信号的对象不知道也不关心是否有对象正在接收信号。这是真正的信息封装,可以确保对象作为软件的组件使用。

 

槽可以用来接收信号,但同时它们也是普通的成员函数。正如一个对象不知道是否有对象在接受信号,槽也不知道是否有信号和它关联。这确保了Qt可以创建完全对立的组件。

 

你可以把任意个信号关联一个槽,也可以把一个信号关联任意个槽。甚至可以把一个信号关联另一个信号。

 

信号与槽构成了一个强大的基于组件的编程机制。

 

小例子

 

class Counter
{
public:
Counter() { m_value = 0; }

int value() const { return m_value; }
void setValue(int value);

private:
int m_value;
};

#include <QObject>

class Counter : public QObject
{
Q_OBJECT

public:
Counter() { m_value = 0; }

int value() const { return m_value; }

public slots:
void setValue(int value);

signals:
void valueChanged(int newValue);

private:
int m_value;
};


 

继承QObject的版本有着相同字段和公共方法,另外它使用了信号与槽支持基于组件的编程。当内部状态改变时它会发出一个信号,valueChanged(),同时它还有一个槽接受其他对象发出的信号。

 

所有使用信号与槽的类都必须在声明的顶部加上Q_OBJECT,而且都继承自QObject.

 

槽是由应用程序员实现,下面是一个可能的实现

void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}


emit行发出一个valueChanged() 信号,新的value为参数。

 

下面的代码片段,我们创建了两个Counter对象的实例,把一个实例的valueChanged() 信号关联到第二个实例的setValue() 槽。

Counter a, b;
QObject::connect(&a, SIGNAL(valueChanged(int)),
&b, SLOT(setValue(int)));

a.setValue(12);     // a.value() == 12, b.value() == 12
b.setValue(48);     // a.value() == 12, b.value() == 48


 

调用a.setValue(12)使a发出一个valueChanged(12)信号,b的setValue()槽会接收到这个信号。如果调用b.setValue(12),b同样会发出一个valueChanged(12)信号,但是没有槽和这个信号关联,最后这个信号被忽视了。

 

注意函数setValue(),只用在value != m_value是才赋值和发出信号。这能防止无限循环。

 

默认对于每一个连接只发出一个信号;重复的连接会发出多个信号。可以用单个disconnect()调用中断所有重复的连接。如果你传了Qt::UniqueConnection类型,只有在没有重复连接的情形下,新的连接才会建立。如果已有一个同样的连接,新的连接就会失败同时connect返回false.

 

生成实例

c++预处理器会改变或者移除signals,slots和emit关键字,以便于使用标准c++编译器。

通过在包含信号与槽的类定义上运行moc,将生成一个c++源文件。源文件最后被编译并和其他object文件链接。如果你使用qmake,makefile自动调用moc并被加到项目的makefile中。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息