C++ Qt 事件和信号/插槽
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3794649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Qt events and signal/slots
提问by Raphael
In the Qt world, what is the difference of events and signal/slots?
Qt世界中,事件和信号/槽的区别是什么?
Does one replace the other? Are events an abstraction of signal/slots?
一个会取代另一个吗?事件是信号/槽的抽象吗?
采纳答案by Harald Scheirich
The Qt documentationprobably explains it best:
在Qt文档可能是最好的解释吧:
In Qt, events are objects, derived from the abstract
QEvent
class, that represent things that have happened either within an application or as a result of outside activity that the application needs to know about. Events can be received and handled by any instance of aQObject
subclass, but they are especially relevant to widgets. This document describes how events are delivered and handled in a typical application.
在 Qt 中,事件是从抽象
QEvent
类派生的对象,它们表示在应用程序中发生的事情或作为应用程序需要了解的外部活动的结果。事件可以由QObject
子类的任何实例接收和处理,但它们与小部件特别相关。本文档描述了如何在典型应用程序中传递和处理事件。
So events and signal/slots are two parallel mechanisms accomplishing the same things. In general, an event will be generated by an outside entity (for example, keyboard or mouse wheel) and will be delivered through the event loop in QApplication
. In general, unless you set up the code, you will not be generating events. You might filter them through QObject::installEventFilter()
or handle events in subclassed object by overriding the appropriate functions.
所以事件和信号/槽是完成相同事情的两个并行机制。通常,事件将由外部实体(例如,键盘或鼠标滚轮)生成,并通过QApplication
. 通常,除非您设置代码,否则您不会生成事件。您可以QObject::installEventFilter()
通过覆盖适当的函数来过滤它们或处理子类对象中的事件。
Signals and Slots are much easier to generate and receive and you can connect any two QObject
subclasses. They are handled through the Metaclass (have a look at your moc_classname.cpp file for more), but most of the interclass communication that you will produce will probably use signals and slots. Signals can get delivered immediately or deferred via a queue (if you are using threads).
Signals 和 Slots 更容易生成和接收,您可以连接任何两个QObject
子类。它们是通过元类处理的(更多信息请查看您的 moc_classname.cpp 文件),但是您将生成的大多数类间通信可能会使用信号和槽。信号可以立即传递或通过队列延迟传递(如果您正在使用线程)。
A signal can be generated.
可以产生信号。
回答by Stefan Monov
In Qt, signals and events are both implementations of the Observer pattern. They are used in different situations because they have different strengths and weaknesses.
在 Qt 中,信号和事件都是观察者模式的实现。它们用于不同的情况,因为它们具有不同的优点和缺点。
First of all let's define what we mean by 'Qt event' exactly: a virtual function in a Qt class, which you're expected to reimplement in a base class of yours if you want to handle the event. It's related to the Template Method pattern.
首先,让我们准确定义“Qt 事件”的含义:Qt 类中的虚函数,如果您想处理该事件,则需要在您的基类中重新实现它。它与模板方法模式有关。
Note how I used the word "handle". Indeed, here's a basic difference between the intent of signals and events:
请注意我是如何使用“句柄”这个词的。实际上,这是信号和事件意图之间的基本区别:
- You "handle" events
- You "get notified of" signal emissions
- 你“处理”事件
- 你“得到通知”信号发射
The difference is that when you "handle" the event, you take on the responsibility to "respond" with a behavior that is useful outside the class. For example, consider an app that has a button with a number on it. The app needs to let the user focus the button and change the number by pressing the "up" and "down" keyboard keys. Otherwise the button should function like a normal QPushButton
(it can be clicked, etc). In Qt this is done by creating your own little reusable "component" (subclass of QPushButton
), which reimplements QWidget::keyPressEvent
. Pseudocode:
不同之处在于,当您“处理”事件时,您有责任以在类之外有用的行为“响应”。例如,考虑一个应用程序,上面有一个带有数字的按钮。该应用程序需要让用户聚焦按钮并通过按“向上”和“向下”键盘键来更改数字。否则按钮应该像正常一样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)
See? This code presents a new abstraction: a widget that acts like a button, but with some extra functionality. We added this functionality very conveniently:
看?这段代码提出了一个新的抽象:一个像按钮一样的小部件,但有一些额外的功能。我们非常方便地添加了此功能:
- Since we reimplemented a virtual, our implementation automatically became encapsulated in our class. If Qt's designers had made
keyPressEvent
a signal, we would need to decide whether to inheritQPushButton
or just externally connect to the signal. But that would be stupid, since in Qt you're alwaysexpected to inherit when writing a widget with a custom behavior (for good reason - reusability/modularity). So by makingkeyPressEvent
an event, they convey their intent thatkeyPressEvent
is just a basic building block of functionality. If it were a signal, it'd look like a user-facing thing, when it's not intended to be. - Since the base-class-implementation of the function is available, we easily implement the Chain-of-responsibility patternby handling our special cases (up&down keys) and leaving the rest to the base class. You can see this would be nearly impossible if
keyPressEvent
were a signal.
- 由于我们重新实现了一个 virtual,我们的实现会自动封装在我们的类中。如果 Qt 的设计者制作
keyPressEvent
了一个信号,我们将需要决定是继承QPushButton
还是只是从外部连接到该信号。但这将是愚蠢的,因为在 Qt 中,您总是希望在编写具有自定义行为的小部件时继承(有充分的理由 - 可重用性/模块化)。因此,通过keyPressEvent
举办活动,他们传达了他们的意图,这keyPressEvent
只是功能的基本构建块。如果它是一个信号,它看起来就像是一个面向用户的东西,但它并不打算这样做。 - 由于函数的基类实现是可用的,我们通过处理我们的特殊情况(向上和向下键)并将其余的留给基类来轻松实现责任链模式。你可以看到,如果这
keyPressEvent
是一个信号,这几乎是不可能的。
The design of Qt is well thought out - they made us fall into the pit of successby making it easy to do the right thing and hard to do the wrong thing (by making keyPressEvent an event).
Qt 的设计是经过深思熟虑的——它们使我们容易做正确的事情而难以做错误的事情(通过使 keyPressEvent 成为一个事件),从而使我们陷入成功的陷阱。
On the other hand, consider the simplest usage of QPushButton
- just instantiating it and getting notified when it's clicked:
另一方面,考虑最简单的用法QPushButton
-只需将其实例化并在单击时收到通知:
button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())
This is clearly meant to be done by the userof the class:
这显然是由类的用户完成的:
- if we had to subclass
QPushButton
every time we want some button to notify us of a click, that would require a lot of subclasses for no good reason!A widget that always shows a "Hello world"messagebox
when clicked is useful only in a single case - so it's totally not reusable. Again, we have no choice but to do the right thing - by connecting to it externally. - we may want to connect several slots to
clicked()
- or connect several signals tosayHello()
. With signals there is no fuss. With subclassing you would have to sit down and ponder some class diagrams until you decide on an appropriate design.
- 如果
QPushButton
每次我们想要某个按钮通知我们点击时都必须进行子类化,那将无缘无故地需要很多子类!messagebox
单击时始终显示“Hello world”的小部件仅在单个情况下有用 - 因此它完全不可重用。同样,我们别无选择,只能做正确的事情——通过外部连接。 - 我们可能希望将多个插槽连接到
clicked()
- 或将多个信号连接到sayHello()
. 有了信号,就没有大惊小怪了。对于子类化,您将不得不坐下来思考一些类图,直到您决定合适的设计。
Note that one of the places QPushButton
emits clicked()
is in its mousePressEvent()
implementation. That doesn't mean clicked()
and mousePressEvent()
are interchangable - just that they're related.
请注意,QPushButton
发出的地方之一clicked()
是在其mousePressEvent()
实现中。这并不意味着clicked()
并且mousePressEvent()
可以互换 - 只是它们是相关的。
So signals and events have different purposes (but are related in that both let you "subscribe" to a notification of something happening).
所以信号和事件有不同的目的(但两者都可以让你“订阅”正在发生的事情的通知)。
回答by Robert Siemer
I don't like the answers so far. – Let me concentrate on this part of the question:
我不喜欢到目前为止的答案。– 让我专注于问题的这一部分:
Are events an abstraction of signal/slots?
事件是信号/槽的抽象吗?
Short answer: no.The long answer raises a “better” question: How are signals and events related?
简短的回答:没有。长答案提出了一个“更好”的问题:信号和事件是如何相关的?
An idle main loop (Qt's for example) is usually “stuck” in a select() call of the operating system. That call makes the application “sleep”, while it passes a bunch of sockets or files or whatever to the kernel asking for: if something changes on these, let the select() call return. – And the kernel, as the master of the world, knows when that happens.
空闲的主循环(例如 Qt 的)通常“卡在”操作系统的 select() 调用中。该调用使应用程序“休眠”,同时它将一堆套接字或文件或其他任何内容传递给内核要求:如果这些内容发生变化,则让 select() 调用返回。– 而内核,作为世界的主人,知道什么时候会发生。
The result of that select() call could be: new data on the socket connect to X11, a packet to a UDP port we listen on came in, etc. – That stuff is neither a Qt signal, nor a Qt event, and the Qt main loop decides itself if it turns the fresh data into the one, the other or ignores it.
select() 调用的结果可能是:socket 上的新数据连接到 X11,我们监听的一个 UDP 端口的数据包进来了,等等。——这些东西既不是 Qt 信号,也不是 Qt 事件,而且Qt 主循环自行决定是否将新数据转换为一个、另一个或忽略它。
Qt could call a method (or several) like keyPressEvent(), effectively turning it into a Qt event. Or Qt emits a signal, which in effect looks up all functions registered for that signal, and calls them one after the other.
Qt 可以调用一个(或多个)方法,如 keyPressEvent(),有效地将其转换为 Qt 事件。或者 Qt 发出一个信号,它实际上查找为该信号注册的所有函数,并一个接一个地调用它们。
One difference of those two concepts is visible here: a slot has no vote on whether other slots registered to that signal will get called or not. – Events are more like a chain, and the event handler decides if it interrupts that chain or not. Signals look like a star or tree in this respect.
这两个概念的一个区别在这里是可见的:一个插槽没有投票决定是否注册到该信号的其他插槽将被调用。– 事件更像是一个链,事件处理程序决定是否中断该链。在这方面,信号看起来像一颗星星或一棵树。
An event can trigger or be entirely turned into a signal (just emit one, and don't call “super()”). A signal can be turned into an event (call an event handler).
一个事件可以触发或完全变成一个信号(只发出一个信号,不要调用“super()”)。信号可以变成事件(调用事件处理程序)。
What abstracts what depends on the case: the clicked()-signal abstracts mouse events (a button goes down and up again without too much moving around). Keyboard events are abstractions from lower levels (things like 果 or é are several key strokes on my system).
什么抽象取决于情况:clicked()-signal 抽象鼠标事件(一个按钮在没有太多移动的情况下再次上下移动)。键盘事件是来自较低级别的抽象(例如 果 或 é 是我系统上的几个击键)。
Maybe the focusInEvent() is an example of the opposite: it could use (and thus abstract) the clicked() signal, but I don't know if it actually does.
也许 focusInEvent() 是一个相反的例子:它可以使用(因此抽象)clicked() 信号,但我不知道它是否真的这样做了。
回答by firescreamer
Events are dispatched by the event loop. Each GUI program needs an event loop, whatever you write it Windows or Linux, using Qt, Win32 or any other GUI library. As well each thread has its own event loop. In Qt "GUI Event Loop" (which is the main loop of all Qt applications) is hidden, but you start it calling:
事件由事件循环调度。每个 GUI 程序都需要一个事件循环,无论您在 Windows 或 Linux 上编写它,使用 Qt、Win32 或任何其他 GUI 库。同样,每个线程都有自己的事件循环。在 Qt 中,“GUI 事件循环”(这是所有 Qt 应用程序的主循环)是隐藏的,但您启动它时调用:
QApplication a(argc, argv);
return a.exec();
Messages OS and other applications send to your program are dispatched as events.
操作系统和其他应用程序发送到您的程序的消息作为事件分派。
Signals and slots are Qt mechanisms. In the process of compilations using moc (meta-object compiler), they are changed to callback functions.
信号和槽是 Qt 机制。在使用 moc(元对象编译器)进行编译的过程中,它们被更改为回调函数。
Event should have one receiver, which should dispatch it. No one else should get that event.
事件应该有一个接收器,它应该调度它。没有其他人应该得到那个事件。
All slots connected to the emitted signal will be executed.
连接到发出信号的所有槽都将被执行。
You shouldn't think of Signals as events, because as you can read in the Qt documentation:
您不应该将信号视为事件,因为您可以在 Qt 文档中阅读:
When a signal is emitted, the slots connected to it are usually executed immediately, just like a normal function call. When this happens, the signals and slots mechanism is totally independent of any GUI event loop.
当一个信号被发出时,与其相连的槽通常会立即执行,就像一个普通的函数调用一样。发生这种情况时,信号和槽机制完全独立于任何 GUI 事件循环。
When you send an event, it must wait for some time until the event loop dispatches all events that came earlier. Because of this, execution of the code after sending event or signal is different. Code following sending an event will be run immediately. With the signals and slots mechanisms it depends on the connection type. Normally it will be executed after all slots. Using Qt::QueuedConnection, it will be executed immediately, just like events. Check all connection types in the Qt documentation.
当您发送事件时,它必须等待一段时间,直到事件循环调度所有较早出现的事件。因此,发送事件或信号后的代码执行是不同的。发送事件后的代码将立即运行。对于信号和插槽机制,它取决于连接类型。通常它会在所有插槽之后执行。使用 Qt::QueuedConnection,它将立即执行,就像事件一样。检查Qt 文档中的所有连接类型。
回答by eric
There is an article that discusses event processing in some detail: http://www.packtpub.com/article/events-and-signals
有一篇文章详细讨论了事件处理:http: //www.packtpub.com/article/events-and-signals
It discussions the difference between events and signals here:
它在这里讨论了事件和信号之间的区别:
Events and signals are two parallel mechanisms used to accomplish the same thing. As a general difference, signals are useful when using a widget, whereas events are useful when implementing the widget. For example, when we are using a widget like QPushButton, we are more interested in its clicked() signal than in the low-level mouse press or key press events that caused the signal to be emitted. But if we are implementing the QPushButton class, we are more interested in the implementation of code for mouse and key events. Also, we usually handle events but get notified by signal emissions.
事件和信号是用于完成同一件事的两种并行机制。一般来说,信号在使用小部件时很有用,而事件在实现小部件时很有用。例如,当我们使用像 QPushButton 这样的小部件时,我们对它的 clicked() 信号比导致信号发射的低级鼠标按下或按键事件更感兴趣。但是如果我们在实现 QPushButton 类,我们更感兴趣的是鼠标和按键事件的代码实现。此外,我们通常会处理事件,但会收到信号发射的通知。
This seems to be a common way of talking about it, as the accepted answer uses some of the same phrases.
这似乎是一种常见的谈论方式,因为接受的答案使用了一些相同的短语。
Note, please see helpful comments below on this answer from Kuba Ober, that make me wonder if it might be a bit simplistic.
请注意,请参阅下面关于 Kuba Ober 的这个答案的有用评论,这让我想知道它是否可能有点简单。
回答by Reinstate Monica
TL;DR: Signals and slots are indirect method calls. Events are data structures. So they are quite different animals.
TL;DR:信号和槽是间接方法调用。事件是数据结构。所以它们是完全不同的动物。
The only time when they come together is when slot calls are made across thread boundaries. The slot call arguments are packed up in a data structure and get sent as an event to the receiving thread's event queue. In the receiving thread, the QObject::event
method unpacks the arguments, executes the call, and possibly returns the result if it was a blocking connection.
它们在一起的唯一时间是跨线程边界进行槽调用时。槽调用参数打包在一个数据结构中,并作为事件发送到接收线程的事件队列。在接收线程中,该QObject::event
方法解包参数,执行调用,如果是阻塞连接,则可能返回结果。
If we're willing to generalize to oblivion, one could think of events as as a way of invoking the target object's event
method. This is an indirect method call, after a fashion - but I don't think it's a helpful way of thinking about it, even if it's a true statement.
如果我们愿意概括为遗忘,可以将事件视为调用目标对象event
方法的一种方式。这是一种间接的方法调用,在时尚之后 - 但我认为这不是一种有用的思考方式,即使它是一个真实的陈述。
回答by jkerian
Events (in a general sense of user/network interaction) are typically handled in Qt with signals/slots, but signals/slots can do plenty of other things.
事件(一般意义上的用户/网络交互)通常在 Qt 中使用信号/插槽处理,但信号/插槽可以做很多其他事情。
QEvent and its subclasses are basically just little standardized data packages for the framework to communicate with your code. If you want to pay attention to the mouse in some way, you only have to look at the QMouseEvent API, and the library designers don't have to reinvent the wheel every time you need to figure out what the mouse did in some corner of the Qt API.
QEvent 及其子类基本上只是框架与代码通信的小型标准化数据包。如果你想以某种方式关注鼠标,你只需要查看 QMouseEvent API,并且库设计者不必每次需要弄清楚鼠标在某个角落做了什么时重新发明轮子Qt API。
It is true that if you're waiting for events (again in the general case) of some sort, your slot will almost certainly accept a QEvent subclass as an argument.
确实,如果您正在等待某种类型的事件(再次在一般情况下),您的插槽几乎肯定会接受 QEvent 子类作为参数。
With that said, signals and slots can certainly be used without QEvents, although you'll find that the original impetus for activating a signal will often be some kind of user interaction or other asynchronous activity. Sometimes, however, your code will just reach a point where firing off a certain signal will be the right thing to do. For example, firing off a signal connected to a progress barduring a long process doesn't involve a QEvent up to that point.
话虽如此,信号和槽当然可以在没有 QEvents 的情况下使用,尽管您会发现激活信号的原始动力通常是某种用户交互或其他异步活动。但是,有时您的代码会达到触发某个信号才是正确做法的地步。例如,在一个漫长的过程中触发一个连接到进度条的信号在那个点之前并不涉及 QEvent。
回答by bootchk
Another minor pragmatic consideration: emitting or receiving signals requires inheriting QObject
whereas an object of any inheritance can post or send an event (since you invoke QCoreApplication.sendEvent()
or postEvent()
) This is usually not an issue but: to use signals PyQt strangely requires QObject
to be the first super class, and you might not want to rearrange your inheritance order just to be able to send signals.)
另一个实用的小考虑:发出或接收信号需要继承,QObject
而任何继承的对象都可以发布或发送事件(因为您调用了QCoreApplication.sendEvent()
或postEvent()
) 这通常不是问题,但是:奇怪的QObject
是,使用信号 PyQt 需要成为第一个超类,并且您可能不想为了能够发送信号而重新排列继承顺序。)
回答by francek
I found this question while reading 'Event processing'by Leow Wee Kheng. It also says:
我在阅读Leow Wee Kheng 的“事件处理”时发现了这个问题。它还说:
Jasmine Blanchettesays:
茉莉花布兰切特说:
The main reason why you would use events rather than standard function calls, or signals and slots, is that events can be used both synchronously and asynchronously (depending on whether you call sendEvent() or postEvents()), whereas calling a function or invoking a slot is always synchronous. Another advantage of events is that they can be filtered.
您将使用事件而不是标准函数调用或信号和槽的主要原因是事件可以同步和异步使用(取决于您是调用 sendEvent() 还是 postEvents()),而调用函数或调用插槽始终是同步的。事件的另一个优点是它们可以被过滤。
回答by user1095108
In my opinion events are completely redundant and could be thrown out. There is no reason why signals could not be replaced by events or events by signals, except that Qt is already set up as it is. Queued signals are wrapped by events and events could conceivably be wrapped by signals, for example:
在我看来,事件是完全多余的,可以扔掉。没有理由不能用事件代替信号或用信号代替事件,除非 Qt 已经按原样设置。排队的信号被事件包裹,而事件可以被信号包裹,例如:
connect(this, &MyItem::mouseMove, [this](QMouseEvent*){});
Would replace the convenience mouseMoveEvent()
function found in QWidget
(but not in QQuickItem
anymore) and would handle mouseMove
signals that a scene manager would emit for the item. The fact that the signal is emitted on behalf of the item by some outside entity is unimportant and happens quite often in the world of Qt components, even though it is supposedly not allowed (Qt components often circumvent this rule). But Qt is a conglomerate of many different design decisions and pretty much cast in stone for fear of breaking old code (which happens often enough anyway).
将替换mouseMoveEvent()
在QWidget
(但不再在QQuickItem
)中找到的便利函数,并处理mouseMove
场景管理器为项目发出的信号。某些外部实体代表项目发出信号这一事实并不重要,并且在 Qt 组件的世界中经常发生,即使它被认为是不允许的(Qt 组件经常绕过这个规则)。但是 Qt 是许多不同设计决策的综合体,并且几乎是一成不变的,因为害怕破坏旧代码(无论如何,这种情况经常发生)。