как работает сигнал-слот изнутри

Итак первое, что надо понять это макросы SLOT и SIGNAL.

SLOT

Макрос SLOT генерирует только имя слота. Макрос SLOT раскладывается в такой вариант qFlagLocation("1slot1()" QLOCATION):

connect(ui->btn_2 ,SIGNAL(clicked()), cl1, SLOT(slot1()) )

// тоже самое, тоже работает
connect(ui->btn_2 ,SIGNAL(clicked()), cl1, qFlagLocation("1slot1()" QLOCATION) )

// тоже самое, тоже работает
connect(ui->btn_2 ,SIGNAL(clicked()), cl1, "1slot1()" );

Как же можно вызвать слот по имени? Иногда это действительно нужно. 

Ответ - просто надо указать вместо SLOT(blablabla()) вашу строку (char *) в стиле "1slot1()". Вот пример, который работает:

void Dialog::connect_signal_and_slotbyName( QObject* obj, const QString & slot_name)
{

    bool bb = connect( this , SIGNAL( sig_foo() ) , cl1,  slot_name.toLocal8Bit().constData() );

    qDebug()<< "connect( this , SIGNAL( sig_foo() ) , cl1,  slot_name.toLocal8Bit().constData() ) = "<< bb;
}

 QMetaObject::invokeMethod

Также можно вызвать слот любого объекта по имени через метод QMetaObject::invokeMethod:

void Dialog::call_slot_byName( QObject* obj, const QString & slot_name)
{
    bool bb2 = QMetaObject::invokeMethod( cl1 , slot_name.toLocal8Bit().constData(), Qt::QueuedConnection);

    qDebug() << ""<<bb2;
}

Передача параметров

Теперь вопрос - а как передать параметры в слоте? Ответ просто. Надо также указать  в строке "1slot1()" еще и параметры  например "1slot1(int,QString)". 

Но нюанс только в том, что и в сигнале также должны быть соответственно такие же указаны параметры.

void Dialog::on_btn_slot_with_parameters_clicked()
{
    connect_signal_and_slotbyName_withPars( cl1, "1slot2(int)");
}

void Dialog::connect_signal_and_slotbyName_withPars( QObject* obj, const QString & slot_name)
{
    bool bb = connect( this , SIGNAL( sig_foo2(int)) , cl1,  slot_name.toLocal8Bit().constData() );

    emit sig_foo2(123);
}

Нюансы указания типов

Как мы видим при связывании сигнал/слот мы видим только "чистые" типы данных такие как int, QString, то есть никаких ссылок. Хотя в самой реализации слота может использоваться const &int или const QString &


QTimer::singleShot

И теперь становится понятно почему нельзя передать параметры слоту при вызове сигнала, у которого нет параметров. Например при использовании отложенного вызова слота с QTimer::singleShot класс QTimer генерирует сигнал timeout() без параметров:

QTimer::singleShot( 200 , cl1 , slot_name.toLocal8Bit().constData() ) ;

И получается, что слот отложенно мы сможем вызвать, а вот параметры передать напрямую слоту как-то не получится.  Что же делать?

QMetaObject::invokeMethod

А можно опять обратиться к методу QMetaObject::invokeMethod. Через него можно передать 10 параметров и вернуть результат. Например так:

    QGenericReturnArgument  ret;

    bool bb2 = QMetaObject::invokeMethod( cl1 ,
                                          "slot2", // имя слота
                                          Qt::QueuedConnection,
                                          ret,
                                          Q_ARG(int, 321));

Или более универсальный метод есть с передачей параметров через QList<QVariant> например так:

bool Dialog::invokeMethod_slot_byNam_withPars( QObject* obj,
                                               const QString & slot_name,
                                               const QList<QVariant> &map)
{
    QList<QGenericArgument> lst;

    foreach(QVariant vv ,map)
    {
        lst.append( QGenericArgument(vv.typeName() , vv.constData()) );
    }

    QGenericReturnArgument  ret;

    bool bb2 = QMetaObject::invokeMethod( obj ,
                                          slot_name.toLocal8Bit().constData(), // имя слота
                                          Qt::QueuedConnection,
                                          ret,
                                          lst.count()>0 ? lst[0] : QGenericArgument(),
                                          lst.count()>1 ? lst[1] : QGenericArgument(),
                                          lst.count()>2 ? lst[2] : QGenericArgument(),
                                          lst.count()>3 ? lst[3] : QGenericArgument(),
                                          lst.count()>4 ? lst[4] : QGenericArgument(),
                                          lst.count()>5 ? lst[5] : QGenericArgument(),
                                          lst.count()>6 ? lst[6] : QGenericArgument(),
                                          lst.count()>7 ? lst[7] : QGenericArgument(),
                                          lst.count()>8 ? lst[8] : QGenericArgument(),
                                          lst.count()>9 ? lst[9] : QGenericArgument()
                                          );

    qDebug()<<"QMetaObject::invokeMethod = "<<bb2 ;


    return true;
}

void Dialog::on_btn_invokeMethod_slot_byName_withPars_clicked()
{
    qDebug() << "Dialog::on_btn_invokeMethod_slot_byName_withPars_clicked()";

    invokeMethod_slot_byNam_withPars( cl1 ,
                                      "slot3",
                                      QList<QVariant>() << 123 << "222");

}

//--------------------------------------------------
void Class1::slot3(int ii, const QString &str)
{
    qDebug() <<"Class1::slot3("<<ii<<","<<str<<")" ;
}

Причем invokeMethod проверяет тип параметров QVariant и если передать так : QList<QVariant>() << 123 << 222); то возникнет ошибка:
QMetaObject::invokeMethod: No such method Class1::slot3(int,int)

И вот теперь осталось только сделать развитие класса QTimer с целью переопределения функции QTimer::singleShot , чтобы передавать еще список параметров через дополнительный параметр QList<QVariant>.

В итоге - все получилось как надо , тестовый проект можно скачать ниже. Оказалось по факту, что QTimer свой на и не нужен , так как достаточно только одного класса QpSingleShotTimer, вот он:

#ifndef QP_SINGL_SLOT_SHOT_H
#define QP_SINGL_SLOT_SHOT_H

#include <QString>
#include <QStringList>
#include <QTimerEvent>

#include <QVector>
#include <QVariant>

#ifndef QT_NO_QOBJECT

#include <QtCore/qbasictimer.h> // conceptual inheritance
#include <QtCore/qobject.h>

//#include "private/qobject_p.h"

QT_BEGIN_HEADER

QT_BEGIN_NAMESPACE


class QpSingleShotTimer : public QObject
{
    Q_OBJECT
    int timerId;
public:
    ~QpSingleShotTimer();

    QpSingleShotTimer(
                       int msec,
                       QObject *Receiver,
                       const QString &slot_name,
                       const QList<QVariant> &pars,
                       QObject * parent);


    QObject *receiver;
    const QString slot_name;

    const QList<QVariant> pars;

Q_SIGNALS:

    void sig_timeout();

public slots:
    bool invokeMethod_slot_byNam_withPars( QObject* obj,
                                           const QString & slot_name,
                                           const QList<QVariant> &map);


    protected:

        void timerEvent(QTimerEvent *);
};


QT_END_NAMESPACE

QT_END_HEADER

#endif // QT_NO_QOBJECT


#endif // QP_SINGL_SLOT_SHOT_H

//-----------------------------------------------------------------------
#include "qpsingleshottimer.h"

#include "private/qobject_p.h"
#include "qabstracteventdispatcher.h"
//#include "qcoreapplication.h"
#include <QDebug>

QT_BEGIN_NAMESPACE

QpSingleShotTimer::QpSingleShotTimer( int msec,
                                      QObject *Receiver,
                                      const QString &Slot_name,
                                      const QList<QVariant> &Pars ,
                                      QObject * parent)

    :
      QObject(QAbstractEventDispatcher::instance()),
      slot_name(Slot_name),
      receiver(Receiver),
      pars(Pars)
{

    timerId = startTimer(msec);
}

QpSingleShotTimer::~QpSingleShotTimer()
{
    if (timerId > 0)
        killTimer(timerId);
}

bool QpSingleShotTimer::invokeMethod_slot_byNam_withPars( QObject* obj,
                                                          const QString & slot_name,
                                                          const QList<QVariant> &map)
{
    QList<QGenericArgument> lst;

    qDebug() << "map" << map;

    //foreach( QVariant vv , map)
    //QVectorIterator<QVariant>  vv( map );
    //while( vv.hasNext())
    QList<QVariant>::ConstIterator it = map.constBegin();


    while (it != map.constEnd())
    {
        const QVariant& var = *it;

        qDebug()<< "var " << var << " var.constData()"<<var.constData();

        lst.append( QGenericArgument( var.typeName() , var.constData() ) );

        ++it;
    }



    QGenericReturnArgument  ret;

    bool bb2 = QMetaObject::invokeMethod( obj ,
                                          slot_name.toLocal8Bit().constData(), // имя слота
                                          Qt::QueuedConnection,
                                          ret,
//                                          Q_ARG(double, 1.23),
//                                          Q_ARG(QString, "asdasds"),
//                                          Q_ARG(QString, "")
                                          lst.count()>0 ? lst[0] : QGenericArgument(),
                                          lst.count()>1 ? lst[1] : QGenericArgument(),
                                          lst.count()>2 ? lst[2] : QGenericArgument(),
                                          lst.count()>3 ? lst[3] : QGenericArgument(),
                                          lst.count()>4 ? lst[4] : QGenericArgument(),
                                          lst.count()>5 ? lst[5] : QGenericArgument(),
                                          lst.count()>6 ? lst[6] : QGenericArgument(),
                                          lst.count()>7 ? lst[7] : QGenericArgument(),
                                          lst.count()>8 ? lst[8] : QGenericArgument(),
                                          lst.count()>9 ? lst[9] : QGenericArgument()
                                                          );

    qDebug()<<"QMetaObject::invokeMethod = "<<bb2 ;


    return true;
}

void QpSingleShotTimer::timerEvent(QTimerEvent *)
{

    if (timerId > 0)
        killTimer(timerId);

    timerId = -1;

    invokeMethod_slot_byNam_withPars( receiver , slot_name , pars );

    // we would like to use delete later here, but it feels like a
    // waste to post a new event to handle this event, so we just unset the flag
    // and explicitly delete...

    qDeleteInEventHandler(this);
}


QT_END_NAMESPACE



Файлы для скачивания