Answers:
在Qt文档可能是最好的解释吧:
在Qt中,事件是从抽象
QEvent
类派生的对象,它们表示发生在应用程序内部或由于应用程序需要了解的外部活动而发生的事情。QObject
子类的任何实例都可以接收和处理事件,但是事件与小部件特别相关。本文档介绍了在典型应用程序中如何传递和处理事件。
因此,事件和信号/时隙是完成相同任务的两种并行机制。通常,事件将由外部实体(例如,键盘或鼠标滚轮)生成,并将通过中的事件循环传递QApplication
。通常,除非设置代码,否则将不会生成事件。您可以QObject::installEventFilter()
通过覆盖适当的函数来过滤它们或处理子类对象中的事件。
信号和插槽更容易生成和接收,您可以连接任何两个QObject
子类。它们是通过元类处理的(更多信息请参见moc_classname.cpp文件),但是您将产生的大多数类间通信可能都将使用信号和插槽。信号可以立即传递或通过队列延迟(如果使用线程)。
可以产生信号。
在Qt中,信号和事件都是Observer模式的实现。因为它们具有不同的优点和缺点,所以它们在不同的情况下使用。
首先,让我们精确地定义“ Qt事件”的含义:Qt类中的虚函数,如果您想处理事件,则希望在您的基类中重新实现它。它与“ 模板方法”模式有关。
注意我如何使用“ handle ” 一词。确实,这是信号和事件的意图之间的基本区别:
区别在于,当您“处理”事件时,您将承担“响应”课堂外有用行为的责任。例如,考虑一个应用程序上有一个带有数字的按钮。该应用需要让用户聚焦按钮并通过按下“上”和“下”键盘键来更改数字。否则,按钮应该像普通按钮一样起作用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)
看到?这段代码提出了一个新的抽象:一个小部件,其功能类似于按钮,但具有一些额外的功能。我们非常方便地添加了此功能:
keyPressEvent
了信号,我们将需要决定是继承QPushButton
还是外部连接到信号。但这将是愚蠢的,因为在Qt中编写具有自定义行为的窗口小部件时总是被期望继承(有充分的理由-可重用性/模块化)。因此,通过创建keyPressEvent
事件,他们传达了自己的意图,keyPressEvent
而意图只是功能的基本组成部分。如果这是一个信号,那么它看起来就像是一个面向用户的东西,而并非故意如此。keyPressEvent
有信号,这几乎是不可能的。Qt的设计经过深思熟虑- 通过使容易做正确的事情而难于做错事情(通过使keyPressEvent成为事件),它们使我们落入成功的陷阱。
另一方面,请考虑最简单的用法QPushButton
- 只需实例化它,然后在单击它时得到通知:
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
显然,这是由该类的用户完成的:
QPushButton
每次我们都需要子类化时,我们需要一些按钮来通知我们单击,那么这将需要很多子类,而没有充分的理由!始终显示“ Hello world”的小部件messagebox
当单击时仅在单个情况下有用-因此完全不可重用。同样,我们别无选择,只能做正确的事情-通过外部连接它。clicked()
-或将多个信号连接到sayHello()
。有了信号就没有大惊小怪。对于子类化,您必须坐下来考虑一些类图,直到您决定合适的设计为止。请注意,QPushButton
发出的地方之一clicked()
是在mousePressEvent()
实现。这并不意味着clicked()
并且mousePressEvent()
可以互换-只是它们是相关的。
因此,信号和事件具有不同的用途(但两者之间的相关性在于,它们都可以让您“订阅”某种事件的通知)。
到目前为止,我不喜欢这些答案。–让我专注于问题的这一部分:
事件是信号/时隙的抽象吗?
简短的回答:不。长的答案提出了一个“更好的”问题:信号和事件如何关联?
空闲的主循环(例如Qt)通常“卡”在操作系统的select()调用中。该调用使应用程序“休眠”,同时将一堆套接字或文件或任何内容传递给内核,要求它们:如果这些改变了,请返回select()调用。–作为世界的主人,内核知道何时发生。
该select()调用的结果可能是:套接字上的新数据连接到X11,我们监听的UDP端口的数据包进来,等等– 。–这些东西既不是Qt信号,也不是Qt事件,并且Qt主循环自行决定是否将新数据转换为一个,另一个或忽略它。
Qt可以调用一个或多个类似keyPressEvent()的方法,从而将其有效地转换为Qt事件。或Qt发出信号,实际上查找该信号注册的所有功能,然后一个接一个地调用它们。
这两个概念的区别在这里可见:一个插槽对注册到该信号的其他插槽是否会被调用没有投票权。–事件更像一个链,事件处理程序决定是否中断该链。在这方面,信号看起来像星星或树。
一个事件可以触发或完全变成一个信号(只发出一个信号,不要调用“ super()”)。信号可以转换为事件(称为事件处理程序)。
什么抽象取决于情况:clicked()信号抽象鼠标事件(一个按钮又向下又向上滑动,没有太多移动)。键盘事件是较低级别的抽象(例如,果或é是我系统上的几个按键)。
也许focusInEvent()是相反的例子:它可以使用(从而抽象化)clicked()信号,但我不知道它是否确实如此。
事件由事件循环调度。每个GUI程序都需要一个事件循环,无论您使用Qt,Win32或任何其他GUI库将其编写为Windows还是Linux,都需要一个事件循环。同样,每个线程都有自己的事件循环。在Qt中,“ GUI事件循环”(这是所有Qt应用程序的主循环)是隐藏的,但是您可以通过以下方式启动它:
QApplication a(argc, argv);
return a.exec();
操作系统和其他应用程序发送给您程序的消息将作为事件调度。
信号和时隙是Qt机制。在使用moc(元对象编译器)进行编译的过程中,它们被更改为回调函数。
事件应具有一个接收者,该接收者应该调度它。没有其他人可以参加该活动。
连接到发射信号的所有插槽将被执行。
您不应将Signals视为事件,因为您可以在Qt文档中阅读:
发出信号后,与其连接的插槽通常会立即执行,就像正常的函数调用一样。发生这种情况时,信号和时隙机制完全独立于任何GUI事件循环。
发送事件时,它必须等待一段时间,直到事件循环调度所有较早出现的事件为止。因此,发送事件或信号后代码执行不同。发送事件后的代码将立即运行。使用信号和插槽机制,取决于连接类型。通常,它将在所有插槽之后执行。使用Qt :: QueuedConnection,它将像事件一样立即执行。检查Qt文档中的所有连接类型。
When you send an event, it must wait for time when event loop dispatch all events that came earlier. Because of this, execution of the cod after sending event or signal is different
有一篇文章详细讨论了事件处理:http : //www.packtpub.com/article/events-and-signals
它在这里讨论事件和信号之间的区别:
事件和信号是用于完成同一件事的两种并行机制。通常,信号在使用小部件时有用,而事件在实现小部件时有用。例如,当我们使用QPushButton之类的小部件时,我们对它的clicked()信号比对引起信号发射的低级鼠标按下或按键事件更感兴趣。但是,如果我们正在实现QPushButton类,则对鼠标和键事件的代码实现更感兴趣。此外,我们通常会处理事件,但会通过信号发射得到通知。
这似乎是谈论它的一种常见方式,因为公认的答案使用了某些相同的短语。
请注意,请参阅下面有关库巴·奥伯(Kuba Ober)的答案的有用评论,这使我想知道这是否可能有点简单。
event
。信号和插槽是具体的方法,而连接机制是一种数据结构,可让信号调用一个或多个列为已连接到其上的插槽。我希望您看到将信号/插槽说成是事件的“子集”或“变体”是胡说八道,反之亦然。它们确实是不同的东西,在某些小部件的上下文中碰巧会用于相似的目的。而已。您推广的越多,对IMHO的帮助就越小。
事件(从一般意义上来说,用户/网络交互)通常在Qt中使用信号/插槽来处理,但是信号/插槽可以做很多其他事情。
QEvent及其子类基本上只是用于框架与代码进行通信的少量标准化数据包。如果您想以某种方式关注鼠标,则只需查看QMouseEvent API,并且库设计人员不必每次都需要弄清楚鼠标在鼠标某个角处的操作时就重新发明了轮子。 Qt API。
的确,如果您正在等待某种事件(通常也是这种情况),则您的广告位几乎可以肯定会接受QEvent子类作为参数。
话虽如此,信号和插槽当然可以在没有QEvents的情况下使用,尽管您会发现激活信号的原始动力通常是某种用户交互或其他异步活动。但是,有时候,您的代码会达到触发特定信号的正确做法。例如,在很长的过程中触发连接到进度条的信号直到该点都不会涉及QEvent。
我在阅读Leow Wee Kheng的“事件处理”时发现了这个问题。它还说:
茉莉花·布兰切特(Jasmine Blanchette)说:
使用事件而不是标准函数调用或信号和插槽的主要原因是,事件可以同步和异步使用(取决于您是否调用sendEvent()或postEvents()),而调用函数或调用插槽始终是同步的。事件的另一个优点是可以对其进行过滤。
我认为事件是完全多余的,可能会被丢弃。除了已经按原样设置Qt之外,没有理由不能用事件或事件替换信号。排队的信号由事件包装,事件可以想象为由信号包装,例如:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
将替换(但现在不再可用)中的便捷mouseMoveEvent()
功能,并将处理场景管理器将为该项目发出的信号。尽管某些信号是不允许的,但某些外部实体代表项目发出信号的事实并不重要,并且经常在Qt组件的世界中发生(即使Qt组件经常绕过此规则)。但是Qt是许多不同设计决策的集合体,并且由于担心破坏旧代码(无论如何经常发生)而几乎陷入僵局。QWidget
QQuickItem
mouseMove
QObject
在必要时,事件具有传播父子层次结构的优势。满足特定条件时,信号/插槽连接只是直接或间接调用函数的承诺。没有与信号和插槽关联的处理层次结构。
accepted
在信号和处理信号的插槽中添加参考参数,也可以将结构作为带有accepted
字段的参考参数。但是Qt做出了它所做出的设计选择,现在它们已经定格了。