ChatGPT解决这个技术问题 Extra ChatGPT

Qt events and signal/slots

In the Qt world, what is the difference of events and signal/slots?

Does one replace the other? Are events an abstraction of signal/slots?


S
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.

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.

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:

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 inherit QPushButton or just externally connect to the signal. But that would be stupid, since in Qt you're always expected to inherit when writing a widget with a custom behavior (for good reason - reusability/modularity). So by making keyPressEvent an event, they convey their intent that keyPressEvent 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 pattern by 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.

The design of Qt is well thought out - they made us fall into the pit of success by making it easy to do the right thing and hard to do the wrong thing (by making keyPressEvent an event).

On the other hand, consider the simplest usage of QPushButton - just instantiating it and getting notified when it's clicked:

button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())

This is clearly meant to be done by the user of 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 to sayHello(). 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.

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.

So signals and events have different purposes (but are related in that both let you "subscribe" to a notification of something happening).


I get the idea. Event is per-class, all the instance of a class will react the same, all of them will call the same QClassName::event. But signal is per-object, each object can have its unique signal-slot connection.
R
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.

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.

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.

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).

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).

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.


I find this part: "a slot has no vote on whether other slots registered to that signal will get called or not" very enlightening for the difference between signals and events. However, I have a related question: what are Messages and how are messages related to signals and events? (if they are related). Are messages an abstraction for signals and events? Can they be used to describe such interactions between QObjects (or other objects?)
@axeoth: In Qt, there's no such thing as "messages". Well, there's a message box, but that's about it.
@KubaOber: Thank you. I was meaning "events".
@axeoth: Then your question is nonsense. It reads: "what are events and how are events related to signals and events".
@KubaOber: I do not remember the context one year later. Anyway, it seems like that I was asking almost the same thing as the OP: what's the difference betwee, eventes and how events are related to signals and slots. What was really the case back then, IIRC, I was looking for a way to wrap Qt signal/slot-driven classes in some kind of non-Qt classes, so I was looking at some way to command the first from inside the latter. I imagine that I was considering sending events to them, since that meant that I only had to include Qt headers, and not force my classes to implement the signal/slots.
H
Harald Scheirich

The Qt documentation probably explains it best:

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 a QObject subclass, but they are especially relevant to widgets. This document describes how events are delivered and handled in a typical application.

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.

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).

A signal can be generated.


the deferred queue is the same as the queue event loop, or it exists two queues? one for deffered signals and on for event ?
@Raphael shame on you for accepting this answer. – The cited paragraph does not even contain the words “signal” or “slot”!
@neuronet: the paragraph is quoted with “[it] explains it best”, which it doesn’t. Not at all. Not even a little.
@Robert so if that little intro line was changed to "First let's consider how the docs explain events, before moving on to consider how they are related to signals" would you be ok with the answer? If so, then we can just edit the answer to improve it as it's a minor point and I agree could be worded better. [Edit that's a genuine question: as I am not sure the rest of it is much better....but just because the quoted part doesn't mention signals seems a superficial worry if the rest of it is actually good, which I am not assuming.]
@neuronet, if you remove the quote entirely it will improve the answer in my eyes. – If you remove the entire answer, it will improve this whole QA.
P
Peter Mortensen

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:

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.

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:

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.

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.


This sentence give me a concise understanding of the different between Qt signal-slot and events : 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
Thanks for saving me the time to write my own answer. Again the major difference is that events are delivered through a queue and an event-loop while signals are delivered directly from the observable to the observer. An event is delivered on a next event-loop cycle while a signal is delivered immediately. That also means event execution is asynchronous and signal synchronous. Quite a difference.
e
eric

There is an article that discusses event processing in some detail: 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.

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.


I can't see how they'd be two mechanisms used to accomplish the same thing - I think it's a useless generalization that helps no-one.
@KubaOber no it helps avoid the lower-level details. Would you say Python is not helpful because you can do the same thing writing in machine code? :)
I'm saying that the first sentence of the quote is generalizing so far as to be useless. Technically it is not incorrect, but only because you could, if you wished so, use event passing to accomplish what the signal-slot mechanism does, and vice-versa. It'd be very cumbersome. So, no, signal-slots are not the same as events, they don't accomplish the same thing, and the fact that both exist is critical to the success of Qt. The button example cited completely ignores the general functionality of signal-slot system.
"Events and signals are two parallel mechanisms used to accomplish the same thing within the narrow scope of some UI widgets/controls." The bold part is critically missing and makes the quote unhelpful. Even then, one can question how much of the "same thing" is really accomplished: signals let you react to click events without installing an event filter on the widget (or deriving from the widget). Doing it without the signals is much more cumbersome and couples the client code tightly to the control. That's bad design.
Events are an ubiquitous concept. The particular implementation in Qt concretely sets them as data structures passed to event. The signals and slots, are, concretely, methods, while the connection mechanism is a data structure that lets the signal invoke one or more slots listed as connected to it. I hope you see that talking of signal/slots as some "subset" or "variant" of events is nonsense, and vice versa. They really are different things that happen to be used to similar ends in the context of some widgets. That's it. The more you generalize, the less helpful you become IMHO.
K
Kuba hasn't forgotten Monica

TL;DR: Signals and slots are indirect method calls. Events are data structures. So they are quite different animals.

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.

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.


f
francek

'Event processing' by Leow Wee Kheng says:

https://i.stack.imgur.com/JV4K5.png

Jasmine Blanchette says:

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.


j
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.

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.

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.

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 bar during a long process doesn't involve a QEvent up to that point.


"Events are typically handled in Qt with signals/slots" - actually no... QEvent objects are always passed around via overloaded virtuals! For example, there's no signal "keyDown" in QWidget - instead, keyDown is a virtual function.
Agree with stefan, actually a pretty confusing piece of Qt
@Stefan: I would argue that few enough Qt apps override keyDown (and instead mostly use signals such as QAbstractButton::clicked and QLineEdit::editingFinished) to justify "typically". I've certainly snagged keyboard input before, but it's not the usual way of handling events.
after your edit (clarifying what you mean by "event"), your post is now correct. I refrain from reusing words like that, though. Things are muddy enough without calling signals "a type of events".
b
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.)


u
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:

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).


Events have the advantage of propagating up the QObject parent-child hierarchy, when necessary. Signal/slot connections are just a promise to call a function, directly or indirectly, when a certain condition is met. There isn't an associated handling hierarchy with signals and slots.
You could implement similar propagation rules with signals. There is no rule. that a certain signal can't be reissued to another object (child). You could add an accepted reference parameter to a signal and the slots handling it or you could have a structure as a reference parameter with an accepted field. But Qt made the design choices it made and they are now set in stone.
You are basically just reimplementing events if you do it the way you suggest.
@FabioA. That's the point of the OP's question. Events and signals can/could replace each other.
If your reimplement events, then you are not replacing events. More to that: signals are implemented on top of events. You need an "event system" of sorts in order to tell some other event loops, possibly in a thread that is not yours, that sometime in the future it has to execute a given function somewhere.