java metaobject_QMetaObject之invokeMethod说明和使用

起源

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:

QMetaObject::invokeMethod(pushButton, "animateClick");

这可怎么办?一个是官方的图书,一个是官方的Manual。是否意味着两种方式都可以呢,还是说Qt的早期版本用的是前者?

查 Qt4.7/Qt4.6/Qt4.5/Qt4.4/Qt4.3/Qt4.2/Qt4.1/Qt4.0 ,结果发现都没有提到前面的用法。是不是书的出错呢?网上搜一下:确实有人抱怨它不工作

测试

本着事实就是的精神,还是先写个程序测试一下:

#include

#include #include 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()<

}

signals: void sig1(const QString& t);

public slots:

void slot1(const QString& t)

{ qDebug()<

}

}; #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 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 type

QThread *currentThread = QThread::currentThread();

QThread *objectThread = object->thread();

if (connectionType == Qt::AutoConnection) {

connectionType = currentThread == objectThread

? Qt::DirectConnection

: Qt::QueuedConnection;

}

对于 直连的,直接调 metacall,它进而去调用对象的 qt_metacall

if (connectionType == Qt::DirectConnection) {

return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0;

对于 Queued 的连接,post 相应的事件,进而转到对象的event()函数中

if (connectionType == Qt::QueuedConnection) {

QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,

0,

-1,

nargs,

types,

args));

对于 bolckedqueued 的连接,使用了信号量

QSemaphore semaphore;

QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex,

0,

-1,

nargs,

types,

args,

&semaphore));

semaphore.acquire();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值