QT信号和槽

信号和信号槽

 在QT中,我们有一个callback技术的替代方案: 我们使用信号(signal)和信号槽(slot)。当一个特定的事件触发时一个对应的信号就会被发射。QT的控件(widget)拥有许多的预定义信号,当然我们也可以通过在widget的继承子类中添加自己的信号给它们。在一个特定的信号响应里一个被称之为信号槽(slot)的方法被调用。同样的,QT的控件也拥有许多的预定义信号槽,你知道的,我们也可以增加自己的信号槽来处理(handle)那些我们关注的信号。

abstract connections


信号和信号槽机制是类型安全的(type-safe):一个信号签名(signature)必需匹配一个接收信号槽的签名。(事实上,一个信号槽可能有一个比它接收到的信号短的签名,因为它可以过忽略掉额外的参数)因为签名是兼容的,那么编译器就可以帮我们找到类型不匹配。信号和信号槽是松耦合的:一个类发射一个信号不用知道也不想知道是哪个信号槽会接收它。Qt的信号和信号槽机制可保证的是当你连接(connect)一个信号到信号槽,在正确时间里信号槽肯定会被调用,带上信号的参数。信号和信号槽可以搞定任何类型的任意数量参数。它们全部都是类型安全的。

所有继承自QObject或它的其中一个子类(如QWidget)都自动的包含了信号和信号槽。当对象改变它们的状态时会向关注它的对象发射信号。这是所有的类都会做的通信。它不知道也不关心是谁接收了它发射的信号。真实的情况就是如此,并且被用来保障对象以软件组件的方式来使用。

信号槽被用来接收信号,但也可以是一般的成员方法。就像一个对象不知道谁接收信号,一个信号槽也不知道它会被连接到哪个信号上。它是保证一个真正的独立组件可以被用在Qt中。你可以尽可能的把多个信号连接到单个的信号槽上,也可以把一个信号连接到多个信号槽上,如果你需要的话。甚至你可以将一个信号直接连接到另一个信号上。(这将会导致第二个信号在第一个信号发射出后立即发射)。
总而言之,信号和信号槽成全了强大的组件式编程机制。

一个简短的例子

一个短小的C++类声明可能是这样的:
 class Counter
 {
 public:
     Counter() { m_value = 0; }

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

 private:
     int m_value;
 };
一个小的基于QObject的类看起来是这样的:
 #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版本的类有着同样的内部状态(state),也提供了public方法来访问状态,只是增加了信号和信号槽以便为组件式编程提供支持。这个类可以通过发射一个信号 valueChanged()来告诉外面的世界它的状态已经改变,并且它还有一个其它对象可以向它发送信号的信号槽。
所有的类要想包含信号和信号槽必需在它们的声明头部加上Q_OBJECT。也必需得继承自(直接或间接)QObject。
信号槽需要程序开发者来实现。这有一个Counter::setValue信号槽的大概实现:
 void Counter::setValue(int value)
 {
     if (value != m_value) {
         m_value = value;
         emit valueChanged(value);
     }
 }
“发射行(emit line)”从对象中发射一个valueChanged信号,以新的值作为参数。在下面的代码片断里,我们创建两个Counter对象然后用QObject::connect方法将第一个对象的valueChanged信号连接(connect)到第二对象的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
Calling a.setValue(12) makes a emit a valueChanged(12) signal, which b will receive in its setValue() slot, i.e. b.setValue(12) is called. Then b emits the same valueChanged() signal, but since no slot has been connected to b's valueChanged() signal, the signal is ignored.
Note that the setValue() function sets the value and emits the signal only if value != m_value. This prevents infinite looping in the case of cyclic connections (e.g., if b.valueChanged() were connected to a.setValue()).
By default, for every connection you make, a signal is emitted; two signals are emitted for duplicate connections. You can break all of these connections with a single disconnect() call. If you pass the Qt::UniqueConnection type, the connection will only be made if it is not a duplicate. If there is already a duplicate (exact same signal to the exact same slot on the same objects), the connection will fail and connect will return false
This example illustrates that objects can work together without needing to know any information about each other. To enable this, the objects only need to be connected together, and this can be achieved with some simple QObject::connect() function calls, or with uic's automatic connections feature.

你可能感兴趣的:(QT信号和槽)