Qt connectSlotsByName and signal slots

A scene

  • As mentioned in the article Qt connect and signal slot , signal slots are mainly connected through connect, and there are several different ways of writing.
  • Create a new Demo Widgets program. In the Qt designer interface, drag in a QPushButton control, right-click on it-go to the slot-select the signal clicked(), and a slot function on_pushButton_clicked() will be automatically generated. It is found that clicking the button will trigger the slot, but the existence of connect is not found in the code. In other words, there are other ways to connect the signal slot.

二 setupUi

  • Exploring the code, there is no special place in the main interface class constructor except for the setupUi function. setupUi is as follows:

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
          
          
        ui->setupUi(this);
    }
    
  • 展开 setupUi

    ......
    class Ui_MainWindow
    {
          
          
    public:
        QWidget *centralWidget;
        QPushButton *pushButton;
        QMenuBar *menuBar;
        QToolBar *mainToolBar;
        QStatusBar *statusBar;
    
        void setupUi(QMainWindow *MainWindow)
        {
          
          
            if (MainWindow->objectName().isEmpty())
                MainWindow->setObjectName(QStringLiteral("MainWindow"));
            MainWindow->resize(400, 300);
            centralWidget = new QWidget(MainWindow);
            centralWidget->setObjectName(QStringLiteral("centralWidget"));
            pushButton = new QPushButton(centralWidget);
            pushButton->setObjectName(QStringLiteral("pushButton"));
            pushButton->setGeometry(QRect(140, 80, 75, 23));
            MainWindow->setCentralWidget(centralWidget);
            menuBar = new QMenuBar(MainWindow);
            menuBar->setObjectName(QStringLiteral("menuBar"));
            menuBar->setGeometry(QRect(0, 0, 400, 23));
            MainWindow->setMenuBar(menuBar);
            mainToolBar = new QToolBar(MainWindow);
            mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
            MainWindow->addToolBar(Qt::TopToolBarArea, mainToolBar);
            statusBar = new QStatusBar(MainWindow);
            statusBar->setObjectName(QStringLiteral("statusBar"));
            MainWindow->setStatusBar(statusBar);
    
            retranslateUi(MainWindow);
    
            QMetaObject::connectSlotsByName(MainWindow); // 注意此处
        } // setupUi
    ......
    
    • The only special thing in setupUi is calling QMetaObject::connectSlotsByName.

三 connectSlotsByName

  • Search for this function in Qt Assistant, and the description is as follows:

    Searches recursively for all child objects of the given object, and connects matching signals from them to slots of object that follow the following form:

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

    Let’s assume our object has a child object of type QPushButton with the object name button1. The slot to catch the button’s clicked() signal would be:

      void on_button1_clicked();
    

    If object itself has a properly set object name, its own signals are also connected to its respective slots.See also QObject::setObjectName().

  • Simply put, write the name of the slot function according to the rules and call QMetaObject::connectSlotsByName to automatically connect the signal of the child object to the slot.

  • For example

    // mainwindow.h
    ......
    private slots:
        void on_btn_clicked();
    ......
    
    // mainwindow.cpp
    ......
    QPushButton* button = new QPushButton(this);
    button->setObjectName(QStringLiteral("btn"));
    qDebug() << button->objectName();
    QMetaObject::connectSlotsByName(this);
    ......
    
    • According on_ <objectName> _ <signal> Create function name in the form of grooves.
    • Call QMetaObject::connectSlotsByName directly in the code to realize signal slot connection.

Four source code

  • The implementation of QMetaObject::connectSlotsByName is in the source file qobject.cpp, as follows:

    void QMetaObject::connectSlotsByName(QObject *o)
    {
          
          
        if (!o)
            return;
        const QMetaObject *mo = o->metaObject();
        Q_ASSERT(mo);
        const QObjectList list = // list of all objects to look for matching signals including...
                o->findChildren<QObject *>(QString()) // all children of 'o'...
                << o; // and the object 'o' itself
    
        // for each method/slot of o ...
        for (int i = 0; i < mo->methodCount(); ++i) {
          
          
            const QByteArray slotSignature = mo->method(i).methodSignature();
            const char *slot = slotSignature.constData();
            Q_ASSERT(slot);
    
            // ...that starts with "on_", ...
            if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_')
                continue;
    
            // ...we check each object in our list, ...
            bool foundIt = false;
            for(int j = 0; j < list.count(); ++j) {
          
          
                const QObject *co = list.at(j);
                const QByteArray coName = co->objectName().toLatin1();
    
                // ...discarding those whose objectName is not fitting the pattern "on_<objectName>_...", ...
                if (coName.isEmpty() || qstrncmp(slot + 3, coName.constData(), coName.size())    
                    || slot[coName.size()+3] != '_')
                    continue;
    
                const char *signal = slot + coName.size() + 4; // the 'signal' part of the slot name
    
                // ...for the presence of a matching signal "on_<objectName>_<signal>".
                const QMetaObject *smeta;
                int sigIndex = co->d_func()->signalIndex(signal, &smeta);
                if (sigIndex < 0) {
          
          
                    // if no exactly fitting signal (name + complete parameter type list) could be found
                    // look for just any signal with the correct name and at least the slot's parameter list.
                    // Note: if more than one of thoses signals exist, the one that gets connected is
                    // chosen 'at random' (order of declaration in source file)
                    QList<QByteArray> compatibleSignals;
                    const QMetaObject *smo = co->metaObject();
                    int sigLen = qstrlen(signal) - 1; // ignore the trailing ')'
                    for (int k = QMetaObjectPrivate::absoluteSignalCount(smo)-1; k >= 0; --k) {
          
          
                        const QMetaMethod method = QMetaObjectPrivate::signal(smo, k);
                        if (!qstrncmp(method.methodSignature().constData(), signal, sigLen)) {
          
          
                            smeta = method.enclosingMetaObject();
                            sigIndex = k;
                            compatibleSignals.prepend(method.methodSignature());
                        }
                    }
                    if (compatibleSignals.size() > 1)
                        qWarning() << "QMetaObject::connectSlotsByName: Connecting slot" << slot
                                   << "with the first of the following compatible signals:" << compatibleSignals;
                }
    
                if (sigIndex < 0)
                    continue;
    
                // we connect it...
                if (Connection(QMetaObjectPrivate::connect(co, sigIndex, smeta, o, i))) {
          
          
                    foundIt = true;
                    // ...and stop looking for further objects with the same name.
                    // Note: the Designer will make sure each object name is unique in the above
                    // 'list' but other code may create two child objects with the same name. In
                    // this case one is chosen 'at random'.
                    break;
                }
            }
            if (foundIt) {
          
          
                // we found our slot, now skip all overloads
                while (mo->method(i + 1).attributes() & QMetaMethod::Cloned)
                      ++i;
            } else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) {
          
          
                // check if the slot has the following signature: "on_..._...(..."
                int iParen = slotSignature.indexOf('(');
                int iLastUnderscore = slotSignature.lastIndexOf('_', iParen-1);
                if (iLastUnderscore > 3)
                    qWarning("QMetaObject::connectSlotsByName: No matching signal for %s", slot);
            }
        }
    }
    
  • process:

    • findChildren Get all child objects of object o and store them in list;
    • Traverse all methods of object o, and process only methods in the form of on_< objectname >_< signal >;
    • Traverse the sub-objects, first find the sub-object matching objectname, and then find the signal that exactly matches the sub-object through signalIndex
    • If no perfect matching signal (fitting signal: name + complete parameter type list) is found, then traverse the signal of the sub-object to find compatible signal: any signal with the correct name and at least the slot's parameter list.
    • If the signal is found, the connection is made through QMetaObjectPrivate::connect.
  • note

    • If multiple matching signals are found, only the first signal will be connected;

    • If the signal is not found, there will be a No matching signal prompt message, pay attention to it.

      // 将 on_pushButton_clicked 更改为 on_pushButton1_clicked 或 on_pushButton_clicked1
      // 会出现以下输出信息:
      QMetaObject::connectSlotsByName: No matching signal for on_pushButton1_clicked()
      QMetaObject::connectSlotsByName: No matching signal for on_pushButton_clicked1()
      

Five attention

  1. According to need, connect and QMetaObject::connectSlotsByName can be used together to improve efficiency.

Guess you like

Origin blog.csdn.net/luoshabugui/article/details/109518874