Итак первое, что надо понять это макросы 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