在 Qt 中,信号和事件都是观察者模式。它们用于不同的情况,因为它们有不同的优点和缺点。
首先,让我们准确定义“Qt 事件”的含义:Qt 类中的虚函数,如果您想处理事件,则需要在您的基类中重新实现该函数。这与模板方法模式.
请注意我如何使用“handle事实上,信号和事件的意图之间有一个基本区别:
- You "handle" events
- You "得到通知“ 信号发射
不同之处在于,当您“处理”事件时,您有责任以在课堂之外有用的行为来“响应”。例如,考虑一个具有带有数字的按钮的应用程序。该应用程序需要让用户聚焦按钮并通过按“向上”和“向下”键盘键来更改数字。否则按钮应该像普通按钮一样工作QPushButton
(可以单击等)。在 Qt 中,这是通过创建您自己的小可重用“组件”(QPushButton
),它重新实现QWidget::keyPressEvent
。伪代码:
class NumericButton extends QPushButton
private void addToNumber(int value):
// ...
reimplement base.keyPressEvent(QKeyEvent event):
if(event.key == up)
this.addToNumber(1)
else if(event.key == down)
this.addToNumber(-1)
else
base.keyPressEvent(event)
看?这段代码提出了一个新的抽象:一个像按钮一样的小部件,但具有一些额外的功能。我们非常方便地添加了这个功能:
- 由于我们重新实现了虚拟,我们的实现自动封装在我们的类中。如果 Qt 的设计师做了
keyPressEvent
一个信号,我们需要决定是否继承QPushButton
或者只是外部连接到信号。但这是愚蠢的,因为在 Qt 中你是always期望在编写具有自定义行为的小部件时继承(有充分的理由 - 可重用性/模块化)。所以通过制作keyPressEvent
一个事件,他们表达了他们的意图keyPressEvent
只是功能的基本构建块。如果它是一个信号,它看起来就像是一个面向用户的东西,但实际上它并不是这样的。
- 由于该函数的基类实现可用,因此我们可以轻松实现责任链模式通过处理我们的特殊情况(向上和向下键)并将其余的留给基类。你可以看到这几乎是不可能的,如果
keyPressEvent
是一个信号。
Qt 的设计经过深思熟虑 - 他们创造了我们掉进成功的深渊通过使做正确的事情变得容易而做错误的事情变得困难(通过使 keyPressEvent 成为一个事件)。
另一方面,考虑最简单的用法QPushButton
- 只需实例化它并在单击时收到通知:
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
这显然是由user班级:
- 如果我们必须子类化
QPushButton
每次我们想要某个按钮来通知我们单击时,这都需要很多子类无缘无故!始终显示“Hello world”的小部件messagebox
单击时仅在单一情况下有用 - 因此它完全不可重复使用。同样,我们别无选择,只能做正确的事情 - 通过外部连接。
- 我们可能想连接几个插槽
clicked()
- 或将多个信号连接到sayHello()
。有了信号就没有大惊小怪。对于子类化,您必须坐下来思考一些类图,直到决定合适的设计。
注意其中一个地方QPushButton
emits clicked()
是在其mousePressEvent()
执行。这并不意味着clicked()
and mousePressEvent()
是可以互换的——只是它们是相关的。
因此,信号和事件具有不同的目的(但相关之处在于两者都允许您“订阅”正在发生的事情的通知)。