Location>code7788 >text

QT signal-slot mechanism

Popularity:234 ℃/2025-04-16 10:40:34

For communication problems between objects, many frameworks use callback function classes to solve them. QT uses signal-slot to solve communication problems between objects. As long as you inherit the QObject class, you can use the signal-slot mechanism. Signal-slots are very simple and flexible to use, and transmit and receive objects to achieve decoupling. The object transmitting the signal does not need to pay attention to which objects need to receive signals, but only needs to transmit signals when the state changes; the object receiving the signal does not need to pay attention to when the signal is transmitted, but only needs to pay attention to the implementation of the slot function. Compared with callback functions, signal-slot efficiency will be lower. Generally speaking, using the signal-slot mechanism is 10 times slower than directly calling the slot function.

Signal-Slot link type

  • Qt::AutoConnection(Default type)
    If the transmitted signal is in the same thread as the receiving object, this type is handled the same as Qt::DirectConnection, otherwise it is the same as Qt::QueuedConnectio.

  • Qt::DirectConnection
    The slot function is called immediately when the signal is transmitted (as is the same as the callback function). The slot function is executed in the thread that transmits the signal.

  • Qt::QueuedConnection
    The slot function is executed within the receiver thread. The slot function is executed when the control of the event loop is handed over to the receiving thread. useQt::QueuedConnectionWhen typed, the parameter types of the signal and slot must be a known type of the QT meta object system. Because QT needs to copy and store parameters in the background to events. If the parameter type is not a known type of the QT meta object system, the following error will be triggered:

    QObject::connect: Cannot queue arguments of type 'MyType'

    At this time, before establishing the link, you need to call the qRegisterMetaType() method class to register the data type.

  • Qt::BlockingQueuedConnection
    After the signal is transmitted, the current thread blocks until the thread of the slot function is awakened and executed.Notice:Using this type will result in deadlock when the object of the transmit signal and the receiving slot function is within the same thread.

  • Qt::UniqueConnection
    This type can be used in conjunction with the above type using OR operation. After setting to this type, the same signal can only be linked to the slot function of the same object once. If the link already exists, the link will not be established again, connect() returns false. Notice:The slot functions corresponding to this type can only be member functions of the class, and cannot use lambda expressions and non-member functions.

  • Qt::SingleShotConnection
    This type can be used in conjunction with the above type using OR operation. After setting to this type, the slot function is called only once. After the signal is transmitted, the connection between the signal and the slot will be automatically disconnected. This type was introduced after QT 6.0.

    QObject::connect() itself is thread-safe, butQt::DirectConnectionWhen type, if the sender and receiver of the signal are not in the same thread, it is not thread-safe.

Signal-slot link method

A signal can link multiple slot functions, a slot function can link multiple signals, and a signal can also link directly to another signal. If a signal links multiple slot functions, when a signal is transmitted, the slot functions are called and executed in accordance with the sequence of linking.

QT provides 2 linking methods: string-based syntax and function-based syntax.

String-based syntax:

QMetaObject::Connection QObject::connect(const QObject **sender*, const char **signal*, const QObject **receiver*, const char **method*, Qt::ConnectionType *type* = Qt::AutoConnection)
     // Macros SIGNAL() and SLOT() are required to declare signals and slot functions

Function-based syntax:

QMetaObject::Connection  QObject::connect(const QObject **sender*, const QMetaMethod &*signal*, const QObject **receiver*, const QMetaMethod &*method*, Qt::ConnectionType *type* = Qt::AutoConnection)

Their differences are as follows:

Based on strings Based on functions
Type Check Runtime Compilation period
Support implicit type conversion no yes
Supports linking signals using lambda expressions no yes
The parameters of the support slot are fewer than the number of parameters of the signal yes no
Support linking C++ functions to QML functions yes no
  1. Type checking and implicit type conversion

    The syntax based on strings depends on the reflection function of the meta-object system, and uses string matching to check signals and slot functions, which have the following limitations:

  • Link errors can only be checked when running;

  • Implicit type conversion cannot be used;

  • Cannot resolve type definitions and namespaces;

    Function-based syntax is checked by the compiler. The compiler can check for link errors during the compilation period, and supports implicit type conversion, and can also identify different names of the same type (i.e. type definition).

    auto slider = new QSlider(this);
     auto doubleSpinBox = new QDoubleSpinBox(this);
    
     // OK: The compiler converts int to double
     connect(slider, &QSlider::valueChanged,
             doubleSpinBox, &QDoubleSpinBox::setValue);
    
     // ERROR: The string cannot contain conversion information
     connect(slider, SIGNAL(valueChanged(int)),
             doubleSpinBox, SLOT(setValue(double));
    
             auto audioInput = new QAudioInput(QAudioFormat(), this);
     auto widget = new QWidget(this);
    
     // OK
     connect(audioInput, SIGNAL(stateChanged(QAudio::State)),
             widget, SLOT(show()));
    
     // ERROR: The namespace cannot be used, the string "State" does not match "QAudio::State"
     using namespace QAudio;
     connect(audioInput, SIGNAL(stateChanged(State)),
             widget, SLOT(show()));
    
     // ...
  1. Linking signals using lambda expressions

    Function-based syntax supports C++11 lambda expressions, and also supports standard functions, non-member functions, and pointers to functions. But for greater readability, signals should be linked to slot functions, lambda expressions, and other signals.

    class TextSender : public QWidget {
         Q_OBJECT
    
         QLineEdit *lineEdit;
         QPushButton *button;
    
     signals:
         void textCompleted(const QString& text) const;
    
     public:
         TextSender(QWidget *parent = nullptr);
     };
    
     TextSender::TextSender(QWidget *parent) : QWidget(parent) {
         lineEdit = new QLineEdit(this);
         button = new QPushButton("Send", this);
    	 // Use lambda expression as slot function
         connect(button, &QPushButton::clicked, [=] {
             emit textCompleted(lineEdit->text());
         });
    
         // ...
     }
  2. Linking C++ objects with QML objects

    String-based syntax can link C++ objects with QML objects, because the QML type is only parsed at runtime and cannot be recognized by the C++ compiler.

    //  document
     Rectangle {
         width: 100; height: 100
    
         signal qmlSignal(string sentMsg)
         function qmlSlot(receivedMsg) {
             ("QML received: " + receivedMsg)
         }
    
         MouseArea {
             : parent
             onClicked: qmlSignal("Hello from QML!")
         }
     }
    
     // C++ class file
     class CppGui: public QWidget {
         Q_OBJECT
    
         QPushButton *button;
    
     signals:
         void cppSignal(const QVariant& sentMsg) const;
    
     public slots:
         void cppSlot(const QString& receivedMsg) const {
             qDebug() << "C++ received:" << receivedMsg;
         }
    
     public:
         CppGui(QWidget *parent = nullptr) : QWidget(parent) {
             button = new QPushButton("Click Me!", this);
             connect(button, &QPushButton::clicked, [=] {
                 emit cppSignal("Hello from C++!");
             });
         }
     };
    
    
     auto cppObj = new CppGui(this);
     auto quickWidget = new QQuickWidget(QUrl(""), this);
     auto qmlObj = quickWidget->rootObject();
    
     // QML signal is linked to C++ slot function
     connect(qmlObj, SIGNAL(qmlSignal(QString)), cppObj, SLOT(cppSlot(QString)));
    
     // C++ signal is linked to QML slot function
     connect(cppObj, SIGNAL(cppSignal(QVariant)), qmlObj, SLOT(qmlSlot(QVariant)));
  3. Number of parameters of slot function

    Generally speaking, the parameter type of the slot function is consistent with the signal declaration, and the number is equal to or less than the parameters of the signal. String-based syntax provides a workaround: if the parameter function has default parameters, the transmitted signal can omit these parameters. When the parameters of the transmitted signal are less than those of the slot function, QT will use the default parameters of the slot function.

    Function-based syntax cannot directly link such signals-slots, but signals can be linked to lambda expressions, and slot functions can be called within the expression.

    public slots:
         void printNumber(int number = 42) {
             qDebug() << "Lucky number" << number;
         }
        
        
     DemoWidget::DemoWidget(QWidget *parent) : QWidget(parent) {
    
         // OK: Use the default value when calling printNumber() 42
         connect(qApp, SIGNAL(aboutToQuit()),
                 this, SLOT(printNumber()));
    
         // ERROR: The compiler requires parameter matching
         connect(qApp, &QCoreApplication::aboutToQuit,
                 this, &DemoWidget::printNumber);
     }
  4. Signal-Slot Reload

    For overloaded signals or slots, the string-based link syntax can display the declared parameter type, but the function-based link syntax cannot tell the compiler which strength to use to link. At this time, the parameter type needs to be specified through the qOverload function.

    // Definition of slot function overload
     QLCDNumber::display(int)
     QLCDNumber::display(double)
     QLCDNumber::display(QString)
    
    
     auto slider = new QSlider(this);
     auto lcd = new QLCDNumber(this);
    
     // S string-based link syntax
     connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)));
    
     // Function-based link syntax
     connect(slider, &QSlider::valueChanged, lcd, qOverload<int>(&QLCDNumber::display));

Signal-Slot automatic link

Signal slots can be linked manually or automatically during compilation or runtime. QT's meta-object system (QMetaObject) can automatically match slots matching names based on signals. When declaring and implementing the slot function as follows, uic (User Interface Compiler) will automatically establish a link between the signal and the slot in the setupUi() function. (This is the case with slot functions automatically created through the form interface of QT Creator)

void on_<object name>_<signal name>(<signal parameters>);

Note: When the widgets in form is renamed, the name of the slot function also needs to be modified accordingly.

You can also use the following function to enable automatic matching of signals and slots:

QMetaObject::connectSlotsByName(this);

Get the signal transmitter

Calling the function QObject::sender() in the slot function can get the QObject object pointer of the signal transmitter. If you know the type of the signal transmitter, you can convert the QObject pointer to a pointer of the object of the deterministic type and then use this deterministic class interface function.

// btnProperty is the QPushButton type, as the transmitter of the signal,
 // This method is a slot function of the click() signal and uses automatic linking
 void Widget::on_btnProperty_clicked()
 {
     //The transmitter who obtains the signal
     QPushButton *btn= qobject_cast<QPushButton*>(sender());
     bool isFlat= btn->property("flat").toBool();
     btn->setProperty("flat", !isFlat);
 }

If the slot function is a lambda expression, it is easier to obtain the signal transmitter, just pass parameters.

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

Unconnecting the signal to the slot

The function disconnect() is used to disconnect the signal and the slot. It has 2 member function forms and 4 static function forms. There are the following ways to use it. In the schematic code, myObject is the object that transmits the signal, and myReceiver is the object that receives the signal.

  1. Unconnect all signals from one transmitter
    // Static function form
     disconnect(myObject, nullptr, nullptr, nullptr);
     // Member function form
     myObject->disconnect();
  2. Unlock all connections to a specific signal
    // Static function form
     disconnect(myObject, SIGNAL(mySignal()), nullptr, nullptr);
     // Member function form
     myObject->disconnect(SIGNAL(mySignal()));
  3. Unlock all connections to a specific recipient
    // Static function form
     disconnect(myObject, nullptr, myReceiver, nullptr);
     // Member function form
     myObject->disconnect(myReceiver);
  4. Unconnecting a specific signal to the slot
    // Static function form
     disconnect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText);

Some rules for signal-slots

  1. Inheriting the QObject class

    Signal-slots can only be used if the QObject class is inherited. When there is multiple inheritance, QObject must be the first inheritance class, because moc always checks whether the first inherited class is QObject and if not, the moc file will not be generated. In addition, the template class cannot use the Q_OBJECT macro.

    // WRONG
    class SomeTemplate<int> : public QFrame
    {
        Q_OBJECT
        ...
    
    signals:
        void mySignal(int);
    };
    
    // correct
    class SomeClass : public QObject, public OtherClass
    {
        ...
    };
    
  2. Function pointers cannot be used as parameters for signals or slots

    In many cases, inheritance or virtual functions can be used instead of function pointers.

    class SomeClass : public QObject
    {
       Q_OBJECT
    
    public slots:
       void apply(void (*apply)(List *, void *), char *); // WRONG
    };
    
    
    
    // correct
    typedef void (*ApplyFunction)(List *, void *);
    
    class SomeClass : public QObject
    {
       Q_OBJECT
    
    public slots:
       void apply(ApplyFunction, char *);
    };
    
  3. When the parameters of a signal or slot are enumerated quantities, it must be fully qualified.

    This is mainly aimed at string-based link syntax, because it relies on string matching to identify data types.

    class MyClass : public QObject
    {
       Q_OBJECT
    
       enum Error {
           ConnectionRefused,
           RemoteHostClosed,
           UnknownError
       };
    
    signals:
       void stateChanged(MyClass::Error error);
    };
    
  4. Nested classes will not be able to use signals or slots

    class A
    {
    public:
        class B
        {
            Q_OBJECT
    
            public slots:   // WRONG
                void b();
        };
    };
    
  5. Inverse return type of signal or slot cannot be referenced

    Although signals or slots can have return types, their return references will be treated as void type.

  6. The part of a class declares a signal or slot can only declare a signal or slot.

    The moc compiler will check the statements in this section

Integrated third-party signal-slot

Integrating the third-party signal-slot mechanism requires avoiding conflicts between signal, slots, emit keywords and third parties (such as Boost). It is mainly through configuration that moc does not use signal, slots, emit keywords. The following configuration is required: For projects using CMake, you need to add them in the project file

target_compile_definitions(my_app PRIVATE QT_NO_KEYWORDS)

For projects using qmake, you need to add them in the .pro file

CONFIG += no_keywords

The corresponding keywords in the source file should be replaced by Q_SIGNALS (Q_SIGNAL), Q_SLOTS (Q_SLOT), Q_EMIT.

The public API of Qt-based libraries should use the keywords Q_SIGNALS and Q_SLOTS, otherwise it will be difficult to use such libraries in projects that define QT_NO_KEYWORDS. You can set the preprocessor definition QT_NO_SIGNALS_SLOTS_KEYWORDS when building the library to enforce this restriction.

Signal-slot performance

The signal slot mechanism of QT is not as good as template-based solutions (such as boost::signal2 or custom implementation). Because the signal-slot of QT depends on the meta-object system, the compiler (MOC) generates additional code that will dynamically look up the association between the signal and the slot at runtime; the parameters are encapsulated by QVariant, adding runtime checks. The cost of transmitting a signal by a template-based solution is about the cost of calling 4 functions, but QT costs about the cost of calling 10 functions. Although signal transmission increases time overhead, these overheads are negligible relative to the execution of code in the slot. The signal-slot of QT is not suitable for scenarios where high performance needs are required (for example, scenarios where core algorithms, high-frequency event processing, game loops, audio processing, etc. require millisecond response).

【refer to】:

Why Does Qt Use Moc for Signals and Slots?

Signals and Slots Across Threads

Differences between String-Based and Functor-Based Connections | Qt 6.8

Signals & Slots | Qt Core 6.8.3