Notes - signals and slots

Posted by fuii_koh on Sun, 13 Feb 2022 08:13:14 +0100

Notes - signals and slots

Event driven model

Event driven model
Qt event mechanism

Principle and implementation of signal and slot

Principle and implementation of signal and slot
QMetaObject:: activate

  • The SLOT and SIGNAL macros will use the precompiler to convert some parameters into strings and add encoding in front.

    In the debugging mode, if there is a problem with the connection of signal, the corresponding file location will be indicated when the warning message is prompted. qFlagLocation is used to locate the line information corresponding to the code and register the address information of the corresponding code into a table with two entries.

    Q_CORE_EXPORT const char *qFlagLocation(const char *method);
    ...
    # define SLOT(a)     qFlagLocation("1"#a QLOCATION)
    # define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)
    
    onnect(this, SIGNAL(ageChanged(int)), this, SLOT(onAgeChanged(int)));
    connect(this, SIGNAL(scoreChanged(int)), this, SLOT(onScoreChanged(int)));
    
    connect(this, qFlagLocation("2""ageChanged(int)" "\0" "Object.h" ":" "54"), this, qFlagLocation("1""onAgeChanged(int)" "\0" "Object.h" ":" "54"));
    connect(this, qFlagLocation("2""scoreChanged(int)" "\0" "Object.h" ":" "55"), this, qFlagLocation("1""onScoreChanged(int)" "\0" "Object.h" ":" "55"));
    
  • Meta object of class
    When compiling the program, make calls MOC to parse the project source code and generate the MOC of the corresponding class_ xxx. Cpp file

  • Metadata Table
    When compiling Qt program, make will call MOC tool to analyze the source file. If a class contains Q_OBJECT macro, MOC will generate the corresponding moc_xxx.cpp file.

  • Realization of signal
    MOC in generated moc_xxx.cpp file implements the signal, creates an array of pointers to parameters, and passes the pointer array to the QMetaObject::activate function. The first element of the array is the return value. The value in this example is 0 because the return value is void.

    void HANS_iWatch::sig_nalRunOver_Mes()
    {
        QMetaObject::activate(this, &staticMetaObject, 25, nullptr);
    }
    
    // SIGNAL 26
    void HANS_iWatch::sigDatasave(const int & _t1, const S_ProcessData & _t2)
    {
        void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) };
        QMetaObject::activate(this, &staticMetaObject, 26, _a);
    }
    
  • Call of slot function
    Using slot function in QT_ static_ The slot function is called at the index position of the metacall function

    void HANS_iWatch::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) {
        if (_c == QMetaObject::InvokeMetaMethod) {
            HANS_iWatch *_t = static_cast<HANS_iWatch *>(_o);
            Q_UNUSED(_t)
            switch (_id) {
            case 0: _t->startGetM_Input(); break;
            case 1: _t->startGetE1_Input(); break;
            case 7: _t->sig_DisplayPosition_1((*reinterpret_cast< const QString(*)>(_a[1]))); break;
            case 8: _t->sig_DisplayPosition_2((*reinterpret_cast< const QString(*)>(_a[1]))); break;
            default: ;
        }
    } 
    
  • QObject::connect function of signal and slot link
    Start the link, qt then find the index of the signal and slot through the string in the meta object of the sender and receiver, and then the sender (signal party) creates a QObjectPrivate::Connection object, and the index of the signal and slot is added to the two-way linked list of QObjectPrivate object When the receiving object is destroyed, the corresponding connection can also be destroyed automatically. Therefore, each receiving object needs to know who is connected to it so that it can clean up the connection.
    Note that each class has a qobjectconnectionlistvector * connectionlists (two-way linked list), a Connection linked list container, and a qobjectprivate:: Connection (linked list) for each signal. The Connection linked list container saves the Connection linked list of each signal, so it is a node type ordinary linked list maintained in the structured two-way linked list

  • Signal transmission
    What is actually called is the signal function generated in the meta object

    void HANS_iWatch::sig_nalRunOver_Mes()
    {
        QMetaObject::activate(this, &staticMetaObject, 25, nullptr);
    }
    

    Call QMetaObject::activate again in the signal function
    The main process of activate is

    1. Judge whether the transmitted signal has slot connection and does not return directly
    2. Judge whether it is blocked, and the blocking returns directly
    3. Get the connection list container of the sender
    4. Use the signal index to obtain the connection linked list of signals in the connection linked list container
    5. Judge the connection type and perform different operations on the slots in the connection through different connection types

Call process of signal slot?

Object::qt_ The metacall function internally calls the Object::setAge function, the setAge function internally calls the Object::ageChanged signal function, the ageChanged signal function internally calls the QMetaObject::activate function, and the activate function internally calls Object::qt_static_metacall function, final QT_ static_ The slot function onAgeChanged is called internally in the metacall function.

Q_ moc file generated by object object

qt generates the moc file of the object by precompiling. The moc file will generate:
Class's function string qt_meta_stringdata_HANS_iWatch_t
Metadata table qt_meta_data_Object, which saves the metadata information of the class, the index of the signal and the parameter expression of the signal related parameters, the slot index, parameters and other related information, attributes and enumeration
Class, which is used to store the metadata pointers of this class and related base classes
For the implementation of signal, if the signal consists of parameters, create a pointer array pointing to parameters and pass the pointer to QMetaObject::activate Function. The parameters of the function consist of the index of the signal and the parameter pointer. The first value in the parameter pointer is the return value
Call QT of slot_ static_ Metacall, in which the corresponding slot function is called according to the passed in index
External call qt_metacall, qt_ The implementation in metacall is provided to the outside to call QT_ static_ Calling qt_ in the source code of metacall, QMetaObject:: Activate Metacall to call the slot function

Signal and slot multithreading

  • The thread of the object is the thread that created the object
  • QObject::connect type
    • Qt::DirectConnection, sender and receiver are in the same thread
    • Qt::QueuedConnection, which returns after the signal is sent. The relevant slot function is executed by the thread where the receiver is located after returning to the event loop

Signal and slot mechanism

The signal and slot mechanism is used to complete the corresponding of interface operation, which is to complete the communication mechanism between any two Qt objects. The signal will be triggered under a specific situation or action. The slot is equivalent to receiving and processing the signal. To inform another window part of the transformation of one window part, send a signal to the other window part. Each Qt object contains each predefined signal and slot. When a specific event occurs, a signal is sent, and the slot associated with the signal will the corresponding signal and complete the corresponding operation.
Signals and slots can be customized.

The signal is connected with the signal, and the slot corresponds to the signal

connect(Object1,SIGNAL(signal1),Object2,SIGNAL(signal2));
connect(Object1,SIGNAL(signal1),Object2,SLOT(slot));
signal such as clicked(), slot such as showarea()

Advantages of signal and slot

(1) Type safe. The signature of the associated signal and slot must be the same.
(2) Loose coupling. Reduce the coupling between Qt objects.
However, it increases the flexibility of the same message between objects, but the running speed is slower than that of the callback function.
Calling a slot function by passing a signal will run 10 times slower than calling a non virtual function directly.
(1) It is necessary to locate the object receiving the signal.
(2) Safely navigate through all associations.
(3) Parameters passed by Marshal and unmarshal.
(4) When multithreading, signals may need to be queued.

The so-called signal slot is actually the observer mode

connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
    QObject::connect(&pushButton,SIGNAL(clicked(bool)),&w,SLOT(show()));
connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type)
    QObject::connect(&pushButton,&QPushButton::clicked,&w,&QWidget::show);
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)
    QObject::connect(&pushButton,&QPushButton::clicked,&w,&QWidget::show);
connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)MainWindow *w = new MainWindow;
    QObject::connect(&pushButton,&QPushButton::clicked,[=](){w->show();})
connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type)

Key points

class Newspaper : public QObject{
    Q_OBJECT
public:
    Newspaper(const QString & name):m_name(name) {}
    void send(){emit newPaper(m_name);}
signals:
    void newPaper(const QString &name);
private:
    QString m_name;
}
  • Q_OBJECT: both sender and receiver need to be subclasses of QObject (of course, except when the slot function is a global function, Lambda expression, etc. there is no receiver);
  • Signal: use signals to mark the signal function. The signal is a function declaration and returns void. There is no need to implement the function code;
  • Slot function is an ordinary member function. As a member function, it will be affected by public, private and protected;
  • Emit: use emit to send signals at appropriate positions;
  • Use the QObject::connect() function to connect signals and slots.
  • Since MOC only processes the mark Q in the header file_ Object class declaration will not handle similar declarations in cpp files. Therefore, if our Newspaper and Reader classes are located in main In cpp, MOC cannot be processed. The solution is to manually call the MOC tool to process main cpp, and set main In cpp, #include "newsletter. H" should be changed to #include "moc_newsletter. H".
  • The signals block lists the signals of this class. A signal is a function name one by one. The return value is void (because the return value of the signal cannot be obtained, there is no need to return any value). The parameter is the data that needs to be known to the outside world. Signal as function name, There is no need to add any implementation to the cpp function (as we said before, Qt program can be compiled with ordinary make. How can the function name without implementation be compiled? The reason is still in moc. moc will help us realize the function body required by the signal function. Therefore, moc does not simply expand Q_OBJECT, but does a lot of additional operations).

common problem

  • Signal with heavy load

    • Solution: use a function pointer to indicate which signal to point to
      • Define function pointer
        void (Newspaper:: *newPaperNameDate)(const QString &, const QDate &) = &Newspaper::newPaper;
        QObject::connect(&newspaper, newPaperNameDate, &reader, &Reader::receiveNewspaper);

      • C cast type

            QObject::connect(&newspaper,(void (Newspaper:: *)(const QString &, const QDate &))&Newspaper::newPaper,&reader, &Reader::receiveNewspaper);
        
      • C + + type conversion

        QObject::connect(&newspaper, static_cast<void (Newspaper:: *)(const QString &, const QDate &)>(&Newspaper::newPaper),&reader,&Reader::receiveNewspaper);
        
  • Slot function with default parameters

    • Solution: use lambda expressions.

      Object::connect(&newspaper,static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),[=](const QString &name) { /* Your code here. */ });
      

Parameter transmission of signal and slot

  • When the number of parameters of signal and slot function is the same, the parameter types of their points should be exactly the same.
  • When the number of parameters of the signal and slot function is different, the number of parameters of the signal can only be greater than the number of parameters of the slot. Moreover, the types of the same number of parameters in the front should be the same, and the redundant parameters in the signal will be ignored.
  • When parameter transmission is not carried out, the parameters of the signal should also be greater than those of the slot.
  • When the slot parameter has a default value, the slot parameter can be greater than the signal parameter.

Connection mode of signal and slot

  1. Qt::AutoConnection
    (Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
    In automatic mode, when the signal and slot are in the same thread, DirectConnection is adopted, and QueuedConnection is adopted asynchronously in different threads
  2. Qt::DirectConnection
    The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.
    Direct connection. It is used in the same thread. The signal slot is triggered directly without queuing
  3. Qt::QueuedConnection
    The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
    Queue connection is used when the signal and slot are different threads. Put the execution of the slot corresponding to the signal into the queue and wait for the execution when the thread where the slot is located is idle
  4. Qt::BlockingQueuedConnection
    Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.
  5. Qt::UniqueConnection
    This is a flag that can be combined with any one of the above connection types, using a bitwise OR. When Qt::UniqueConnection is set, QObject::connect() will fail if the connection already exists (i.e. if the same signal is already connected to the same slot for the same pair of objects). This flag was introduced in Qt 4.6.

When using lambda to connect the signal slot, there is a point of attention, that is

connect(this, &HousingAirLeakTest::Signal_TE_Logs_AppendText, [=](const QString &message) {
        QDateTime datatime = QDateTime::currentDateTime();
        QString time = datatime.toString("yyyy-MM-dd HH:mm:ss:zzz");
        if (ui.TE_Logs->document()->lineCount() > 3000)
        {
            ui.TE_Logs->clear();
        }
        ui.TE_Logs->append("[" + time + "] " + message);
    });

and

connect(this, &HousingAirLeakTest::Signal_TE_Logs_AppendText,this, [=](const QString &message) {
        QDateTime datatime = QDateTime::currentDateTime();
        QString time = datatime.toString("yyyy-MM-dd HH:mm:ss:zzz");
        if (ui.TE_Logs->document()->lineCount() > 3000)
        {
            ui.TE_Logs->clear();
        }
        ui.TE_Logs->append("[" + time + "] " + message);
    });

The connection mode is different
The connection mode of the former is Qt::DirectConnection, while the connection mode of the latter is Qt::AutoConnection. Therefore, when using multithreading, you should pay attention to using the object with slot

Topics: Qt