[Qt] A comprehensive summary of the signal and slot function mechanism

Signals and slots mechanism

1. Introduction to signal and slot mechanism

(Note 1: The slots and slot functions below represent the same meaning)
(Note 2: Reading this article may be a bit boring, but there are important knowledge about signals and slots in the article, which are often ignored even in development. Please continue look)

Signals and slots are used for communication between multiple objects. The signal and slot mechanism is the core feature of Qt, and it is also the biggest difference from other frameworks. Qt's meta-object system is the basis for the implementation of signals and slots.

In GUI programming, when one widget is changed, you usually want another widget to be notified. Objects of any type are expected to be able to communicate with each other. For example, you might want to call a window's function if the user clicks the close button Close().

Other software toolkits or frameworks may implement this communication mechanism using a callback mechanism. A callback function is a pointer to a function, so if you want a handler function to be notified of some event, you pass a pointer to another function (the callback function) to the handler function. The handler function then calls the callback function when appropriate. While successful frameworks do exist using this approach, callbacks can be less intuitive, and there can be problems ensuring the correct types of callback arguments.

In Qt, there is an alternative to the callback technique: that is 信号和槽the mechanism. A signal is emitted when a specific event occurs. There are many predefined signals in Qt's widgets, but we can subclass widgets to add custom signals to them. Slots are functions that respond to specific signals. Qt's widgets have many predefined slots, but it's common to subclass a widget and add your own so that you can handle the signals associated with it. As shown below:
insert image description here

The signal and slot mechanism is type-safe: the parameters of the signal must match the parameters of the receiving slot function. (Actually, a slot can have fewer parameters than the signal it receives, because the slot can ignore the extra parameters.) Because the parameters are compatible, when using the signal-slot association mechanism based on the function pointer syntax, the compiler can Helps detect type matches.

Signals and slots are loosely coupled: when an object emits a signal, the object does not know or care which object's slot will receive the signal. Qt's signal and slot mechanism ensures that if you connect a signal to a slot, the slot will be called at the correct time.

Signals and slots can accept any number of arguments of any type. They are completely type safe.

All QObjectclasses that inherit from or one of its subclasses (for example, QWidget) can contain signals and slots. Signals may be emitted when an object changes its state (determined by the developer and parent class).

Slot functions can be used to receive signals, but they are also ordinary member functions. Just like an object doesn't know if something receives its signal, a slot doesn't know if a signal is connected to it, so you can use Qt to create independent components. When it is necessary to use the independent component, determine the predefined signal and slot functions in its component class, and then connect the signal and slot functions.

​ Multiple signals can be connected to one slot function (i.e. [many-to-one]), and one signal can also be connected to multiple slot functions [i.e. one-to-many].

It is also possible to connect one signal directly to another. (When the first signal is emitted, it will immediately emit the second signal.)

In summary, signals and slot functions together form a powerful component programming mechanism.

2. [Signal]

(2-1) Issuance of signals

​ Since the arrival of a certain condition may cause the object to change, its internal state will change, and the object will send a signal at this time. The signal is a public access function and can be sent from anywhere, but it is recommended: [only send the signal from the class that defines the signal and its subclasses] .

​ Under the Qt framework, there are two types of signaling:

1. [Predefined signals for each class]: When these signals are sent out, you can check the official documents.

2. [Customized signals]: The sending of these signals is defined by the developer.

(2-2) Signal processing

When a signal is emitted, the slot function connected to it is usually executed immediately, just like a normal function call. In this case, the signal and slot mechanism is completely independent of the GUI event loop and does not interfere with the GUI event loop. emitThe code following the statement will not be executed until all slot functions have returned. The situation is slightly different if queued connections are used , in which case the code following the emit keyword will continue immediately, and the slot function will be executed later.

If several slot functions are connected to the same signal, when the signal is emitted, these slot functions will be executed sequentially in the order in which they were connected. 【this point is very important】

​ Signals are mocautomatically generated by tools and cannot be implemented in .cpp files, so signals can never have a return type (signals must voidbe defined using keywords).

Note on signal and slot parameters: experience shows that signals and slots are more reusable if they don't use special types.

​ The following table shows connect()5 different connection types that can be specified when using the create signal and slot function connection:

serial number type meaning
1 Qt::AutoConnection Qt::DirectConnectionUsed if the receiver lives in the thread that emitted the signal . Otherwise, use Qt::QueuedConnection. The connection type is determined when the signal is emitted. [This is the default connection method when Qt creates signal and slot functions]
2 Qt::DirectConnection When the signal is emitted, the slot function is called immediately. Slot functions execute in the thread that sends the signal.
3 Qt::QueuedConnection The slot function is called when control returns to the receiver thread's event loop. Slot functions are executed in the receiver's thread.
4 Qt::BlockingQueuedConnection Same Qt::QueuedConnectionas , except that the thread blocks until the slot returns. If the receiver exists in the thread that sent the signal, the connection cannot be used, otherwise the application will deadlock.
5 Qt::UniqueConnection This is a flag that can be combined using bitwise OR with the above join types. When Qt::UniqueConnectionset, QObject::connect()will fail if the connection already exists (for example, if the same signal is already connected to the same slot of the same pair of objects). Note: This flag was introduced in Qt 4.6.

3. [Slot function]

The slot function will be called when a signal connected to the slot function is emitted. Slot functions are ordinary C++ functions, and can be called normally in actual development; their only feature is: [signals can be connected to them].

Since slots are ordinary member functions, they follow normal C++ rules when called directly. However, as slots, any component can call them through a signal connection.

It is also possible to define slot functions as virtual, which is very useful in development.

The signals and slots mechanism is slightly slower compared to the callback mechanism, although for real applications the difference is not significant. In general, sending a signal connected to some slot is about 10 times slower than calling a non-virtual function directly. This is the overhead of locating the connection object, iterating over all connections safely (i.e. checking that subsequent receivers aren't destroyed in the middle of the emit), and passing calls. While 10 non-virtual function calls sounds like a lot, it is much less overhead than new or delete. Once a string, vector, or list operation requiring new or delete is performed in the background, the overhead of signals and slots is only a small fraction of the overall function call overhead. The same is true when performing system calls in slot functions (or calling more than ten functions indirectly). Therefore, the simplicity and flexibility of the signals and slots mechanism is worth it, and these overheads will not even be noticed in practical application scenarios.

Note that third-party libraries that define variables as signals or slots may cause compiler warnings and errors when compiled with Qt-based applications. To fix this, use #undefto define the preprocessor symbol that went wrong.

(3-1) Signal and slot functions with default parameters

Signals and slots can contain parameters, and parameters can have default values. For example: QObject::destroyed().

void destroyed(QObject* = nullptr);

​ When QObjectdeleted, it emits this QObject::destroyed()signal. Wherever we have a dangling reference to a deleted QObject, we want to catch this signal so it can be cleaned up. Suitable slot parameters could be:

void objectDestroyed(QObject* obj = nullptr);
(3-2) Use QObject::connect()three methods of connecting signals to slot functions:

(1) The first method: use function pointers

connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);

There QObject::connect()are several advantages to working with function pointers. It allows the compiler to check that the signal's arguments are compatible with the slot's arguments. The compiler can also convert parameters implicitly if necessary.

(2) The second method: you can connect toC++ 11 的 lambdas

connect(sender, &QObject::destroyed, this, [=](){
    
     this->m_objects.remove(sender); });

In both cases, we provide this context in the connect() call. The context object provides information about in which thread the receiver should be executed.

lambdaThe connection will be disconnected when the sender or the context is destroyed . Note: All objects used inside the function are still active when the signal is emitted.

(3) The third method: use QObject::connect()and signal and slot declaration macros. The rule for including parameters in SIGNAL()and SLOT()macros (if the parameter has a default value) is that no fewer parameters are passed to the SIGNAL() macro than to the SLOT() macro .

​ For example, the following codes are legal:

connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(Qbject*)));
connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed()));
connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));

​ But this is illegal:

connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed(QObject*)));

​ Because the slot function expects a QObject whose signal will not be sent. This connection will report a runtime error.

Note that QObject::connect()the compiler does not automatically check the parameters of signal and slot functions when overloading is used.

​ In summary: Using the first method to create signals and slots is more common and appropriate in development.

(3-3) Some advanced usage of signal and slot functions

​ When you need to obtain the information of the signal sender, you can use QObject::sender()the function provided by Qt, which returns a pointer to the object that sent the signal.

LambdaExpressions are a convenient way to pass custom parameters to slots:

connect(action, &QAction::triggered, engine,[=]() {
    
     engine->processAction(action->text()); });

Fourth, use disconnect to disconnect the signal/slot connection

disconnect()Used to disconnect a signal in an object sender from a method in an object receiver. Returns true if the connection was successfully disconnected; otherwise returns false.

The signal/slot connection is removed when either object on both sides of the signal/slot association is destroyed.

disconnect()There are three ways to use it, as shown in the following example:

1. Disconnect all signals/slots connected to the object:

disconnect(myObject, nullptr, nullptr, nullptr);

Equivalent to a non-static overloaded function:

myObject->disconnect();

2. Disconnect all objects connected to a specific signal:

disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr);

Equivalent to a non-static overloaded function:

myObject->disconnect(SIGNAL(mySignal()));

3. Disconnect the connection of a specific receiving object:

disconnect(myObject, nullptr, myReceiver, nullptr);

Equivalent to a non-static overloaded function:

myObject->disconnect(myReceiver);

nullptrCan be used as a wildcard to mean "any signal", "any receiving object", or "any slot in a receiving object", respectively.

Example usage of the following format:

disconnect(发送对象,信号,接收对象,方法)
  • The send object will not be nullptr.

  • If signal is nullptr, the receiving object and slot will be disconnected from all signals . Otherwise only the specified signal is disconnected .

  • If the receiving object is nullptr, it disconnects all connections associated with that signal. Otherwise, just disconnect the slot from the receiving object .

  • If method is nullptr, it disconnects any connections to the receiving object . If not, only the slot connection named method will be disconnected. Method must be nullptr if no receiving object . Right now:

disconnect(发送对象,信号,nullptrnullptr

5. Using Qt with third-party signal and slot functions

​ When the third-party library also has a signal/slot function mechanism, it is necessary to use Qt's signal and slot function mechanism at this time. For this development scenario, Qt can use both mechanisms in the same project. The following line needs to be added to the qmake project (.pro) project file:

CONFIG += no_keywords

It will tell Qt not to define the moc keyword signals, slots and emit, as these names will be used by third-party libraries (for example Boost). If you want to continue to use the Qt signal and slot mechanism with the no_keywords flag, you need to replace all Qt moc keywords in the source file with the corresponding Qt macros: Q_SIGNALS (or Q_SIGNAL), Q_SLOT (or Q_SLOT) and Q_EMIT.


This article describes Qt's signal and slot function mechanism from a development perspective. The follow-up plan starts from the source code point of view, and records some information about the implementation mechanism behind the signal and slot functions. Haha (ಡωಡ)hiahiahia


Search and follow [Embedded Xiaosheng] wx public account to get more exciting content >>>>
Please add a picture description

Guess you like

Origin blog.csdn.net/iriczhao/article/details/122973801