C++ GUI Qt4 编程 一书多线程部分提到invokeMethod的用法
QMetaObject::invokeMethod(label, SLOT(setText(const QString&)), Q_ARG(QString, "Hello"));而 Qt Manual 中介绍却是
- You only need to pass the name of the signal or slot to this function, not the entire signature. For example, to asynchronously invoke the animateClick() slot on a QPushButton, use the following code:
这可怎么办?一个是官方的图书,一个是官方的Manual。是否意味着两种方式都可以呢,还是说Qt的早期版本用的是前者?
查 Qt4.7/Qt4.6/Qt4.5/Qt4.4/Qt4.3/Qt4.2/Qt4.1/Qt4.0 ,结果发现都没有提到前面的用法。是不是书的出错呢?网上搜一下:确实有人抱怨它不工作
测试本着事实就是的精神,还是先写个程序测试一下:
#include <QtCore/QObject>#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
class Test : public QObject
{
Q_OBJECT
public:
Test(QObject * parent):QObject(parent)
{
connect(this, SIGNAL(sig1(QString)), SLOT(slot1(QString)));
QMetaObject::invokeMethod(this, "sig1", Q_ARG(QString, "constructor"));
}
Q_INVOKABLE void method1(const QString& t)
{
qDebug()<<"from method:"<<t;
}
signals:
void sig1(const QString& t);
public slots:
void slot1(const QString& t)
{
qDebug()<<"from slot:"<<t;
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Test obj(0);
QMetaObject::invokeMethod(&obj, "slot1", Q_ARG(QString, "Hello"));
QMetaObject::invokeMethod(&obj, "method1", Q_ARG(QString, "Hello"));
QMetaObject::invokeMethod(&obj, SLOT(slot1(QString)), Q_ARG(QString, "Hello with SLOT"));
QMetaObject::invokeMethod(&obj, METHOD(method1(QString)), Q_ARG(QString, "Hello with METHOD"));
return a.exec();
}
确实如他人所说,SLOT这种用法不工作
from slot: "constructor"from slot: "Hello"
from method: "Hello"
QMetaObject::invokeMethod: No such method Test::1slot1(QString)(QString)
QMetaObject::invokeMethod: No such method Test::0method1(QString)(QString)源码
顺便看看源码吧
bool QMetaObject::invokeMethod(QObject *obj,const char *member,
Qt::ConnectionType type,
QGenericReturnArgument ret,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9)
{
if (!obj)
return false;
QVarLengthArray<char, 512> sig;
int len = qstrlen(member);
if (len <= 0)
return false;
生成函数原型字符串(从这儿可以看到书中方法不工作的原因)
sig.append(member, len);sig.append('(');
const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(),
val4.name(), val5.name(), val6.name(), val7.name(), val8.name(),
val9.name()};
int paramCount;
for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
len = qstrlen(typeNames[paramCount]);
if (len <= 0)
break;
sig.append(typeNames[paramCount], len);
sig.append(',');
}
if (paramCount == 1)
sig.append(')'); // no parameters
else
sig[sig.size() - 1] = ')';
sig.append('\0');
在元对象系统中看该函数信息是否存在
int idx = obj->metaObject()->indexOfMethod(sig.constData());if (idx < 0) {
QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
idx = obj->metaObject()->indexOfMethod(norm.constData());
}
if (idx < 0 || idx >= obj->metaObject()->methodCount()) {
qWarning("QMetaObject::invokeMethod: No such method %s::%s", obj->metaObject()->className(), sig.constData());
return false;
}
获得相应的 QMetaMethod,调用其 invoke 方法
QMetaMethod method = obj->metaObject()->method(idx);return method.invoke(obj, type, ret,
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}QMetaMethod::invoke
接着看看它的源码,首先:
* 如果指定了返回值,检查返回值的类型是否和QMetaMethod 的中的一致
if (returnValue.data()) {const char *retType = typeName();
if (qstrcmp(returnValue.name(), retType) != 0) {
// normalize the return value as well
// the trick here is to make a function signature out of the return type
// so that we can call normalizedSignature() and avoid duplicating code
QByteArray unnormalized;
int len = qstrlen(returnValue.name());
unnormalized.reserve(len + 3);
unnormalized = "_(";
// the function is called "_"
unnormalized.append(returnValue.name());
unnormalized.append(')');
QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData());
normalized.truncate(normalized.length() - 1); // drop the ending ')'
if (qstrcmp(normalized.constData() + 2, retType) != 0)
return false;
}
}
-
为了利用现有的代码来规范化这儿返回值的类型,这儿构造了一个函数_(typeOfReturn)。
* 检查参数个数,传递的参数是否不少于需要的参数
* 检查Connection的类型,处理AutoConnection
// check connection typeQThread *currentThread = QThread::currentThread();
QThread *objectThread = object->thread();
if (connectionType == Qt::AutoConnection) {
connectionType = currentThread == objectThread
? Qt::DirectConnection
: Qt::QueuedConnection;
}
- 对于 直连的,直接调 metacall,它进而去调用对象的 qt_metacall
return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0;
- 对于 Queued 的连接,post 相应的事件,进而转到对象的event()函数中
QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,
0,
-1,
nargs,
types,
args));
- 对于 bolckedqueued 的连接,使用了信号量
QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,
0,
-1,
nargs,
types,
args,
&semaphore));
semaphore.acquire();