Signals & Slots(Qt5) (一)

2014-11-24 02:54:24 · 作者: · 浏览: 6

>Signal-Slot的作用是对象间的通信; Signals-Slots机制是Qt的核心特性, 也可能是Qt和其他大多数框架提供的特性不同的部分;


介绍


>GUI编程中, 当我们改变了一个widget,经常希望另一个widget能被通知到; 通常我们希望各种对象间能互相通信. Example: 用户点击了CLOSE按钮, 我们会想要让window的close()函数被调用;

>老一点的toolkit包使用callback机制实现通信. callback是指向函数的指针, 如果你希望一个processing function能在一些事件上通知你, 需要传递一个函数指针到那个processing function; processing function会在何时的时候调用callback; Callbacks有两个基本瑕疵: 第一, 不是类型安全的type-safe. 我们永远不能确定processing function会用正确的参数来调用callback; 第二, callback和processing function有很强的耦合, processing function必须知道要调用哪一个callback;

Signals and Slots
>Qt使用Signals-Slots代替callback技术; signal在一个特定事件发生时被发出; Qt的widgets有很多预定义的signals, 我们可以自定义subclass来添加自己的signals; slot是一个函数, 接收到对应的signal时会被调用; 同样, Qt有预定义的slots, 我们也可以自定义slots来处理相关的signals;

>signals-slots机制是type-safe的: signal的原型signature必须和接收信号的slot的signature一致;(slot的signature会短一些, 因为它可以忽略多余的参数) signature和编译器兼容, 所以编译器可以进行类型匹配; signal和slot是松耦合的: 一个类发出一个signal, 它不会去关心哪个slot接受到; 对于相关联的signal和slot, Qt的signal-slot机制保证了slot会在合适的时间接收到signal的参数并被调用; Signal-slot可以传递任意个数和类型的参数; 完全type safe;


>所有继承于QObject或Object的subclass的类都可以包含signal和slot; Signal在对象改变状态时被发送, 对这个事件感兴趣的对象可以处理这个Signal; 我们不知道也不关心是否有对象接收到这个发送的信号; 这是真正的信息封装, 保证对象被当作软件的组件来使用;


>Slots可以接收Signals, 他们也可以被当作普通的成员函数; 和Signal一样, Slot也不用知道它是否和Signal连接起来了. 这样保证了Qt能创建真正独立的组件;


>你可以将任意多的signals连接到一个slot, 也可以将一个signal连接到任意多的slots; 甚至可以将一个signal连接到另一个signal(当第一个signal被发出时第二个signal会立即被发出)

Small Example

>基于QObject的类能发出signal告诉外界它的状态改变了, valueChanged(), 同时有一个slot可以接收其他对象的signal; 所有包含signal-slot的类必须在类声明的开始声明Q_OBJECT宏, 并且(直接或间接)继承自QObject类;


[cpp]
class Counter : public QObject
{
Q_OBJECT
public:
Counter(QObject *parent = 0) { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
};

class Counter : public QObject
{
Q_OBJECT
public:
Counter(QObject *parent = 0) { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
};

[cpp]
//Slot由程序员实现
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
//发出signal,传递新的参数值
emit valueChanged(value);
}
}

//Slot由程序员实现
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
//发出signal,传递新的参数值
emit valueChanged(value);
}
}>下面的代码段创建2个Counter对象, 把第一个对象的valueChanged() signal关联到第二个对象的setValue() slot;


[cpp]
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48

Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
>对象a调用setValue(12), 会发送一个signal valueChanged(12), 对象b会在slot setVaule()接收到, 并调用这个slot; 对象b同样会发出signal valueChanged(), 但是没有slot和对象b的valueChanged()连接, signal会被忽略;

Note setValue()只有在value != m_value的时候才会发出signal. 这样可以防止signal-slot环形关联导致无限循环的情况; (e.g. b.vauleChanged() 和 a.setVaule()互相关联)

>默认情况下, 你创建一个信号关联就应该有一个signal发出; 重复的关联会有两个或